* I2PSOCKSIRCTunnel:

- New, for filtering IRC client traffic when using SOCKS
    * I2PTunnelIRCClient:
      - Make filter classes static and public for use by SOCKS
      - Eliminate redundant case conversion
      - Pass ISON message through (jIRCii uses it for pings)
      - Switch back to StringBuffer since it's used by 2 threads
      - Set daemon on filter threads
    * SOCKS5Server:
      - Fix handling of multiple authentication methods
This commit is contained in:
zzz
2010-03-05 14:04:32 +00:00
parent 6bb4403207
commit 9244bd6b0f
9 changed files with 150 additions and 25 deletions

View File

@ -61,6 +61,7 @@ import net.i2p.data.Base64;
import net.i2p.data.DataFormatException; import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.Destination; import net.i2p.data.Destination;
import net.i2p.i2ptunnel.socks.I2PSOCKSIRCTunnel;
import net.i2p.i2ptunnel.socks.I2PSOCKSTunnel; import net.i2p.i2ptunnel.socks.I2PSOCKSTunnel;
import net.i2p.i2ptunnel.streamr.StreamrConsumer; import net.i2p.i2ptunnel.streamr.StreamrConsumer;
import net.i2p.i2ptunnel.streamr.StreamrProducer; import net.i2p.i2ptunnel.streamr.StreamrProducer;
@ -895,6 +896,39 @@ public class I2PTunnel implements Logging, EventDispatcher {
} }
} }
/**
* Run an SOCKS IRC tunnel on the given port number
* @since 0.7.12
*/
public void runSOCKSIRCTunnel(String args[], Logging l) {
if (args.length >= 1 && args.length <= 2) {
int _port = -1;
try {
_port = Integer.parseInt(args[0]);
} catch (NumberFormatException nfe) {
l.log("invalid port");
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
notifyEvent("sockstunnelTaskId", Integer.valueOf(-1));
return;
}
boolean isShared = false;
if (args.length > 1)
isShared = "true".equalsIgnoreCase(args[1].trim());
ownDest = !isShared;
I2PTunnelTask task;
task = new I2PSOCKSIRCTunnel(_port, l, ownDest, (EventDispatcher) this, this);
addtask(task);
notifyEvent("sockstunnelTaskId", Integer.valueOf(task.getId()));
} else {
l.log("sockstunnel <port>");
l.log(" creates a tunnel that distributes SOCKS requests.");
notifyEvent("sockstunnelTaskId", Integer.valueOf(-1));
}
}
/** /**
* Streamr client * Streamr client
* *

View File

@ -82,10 +82,10 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
try { try {
i2ps = createI2PSocket(clientDest); i2ps = createI2PSocket(clientDest);
i2ps.setReadTimeout(readTimeout); i2ps.setReadTimeout(readTimeout);
StringBuilder expectedPong = new StringBuilder(); StringBuffer expectedPong = new StringBuffer();
Thread in = new I2PAppThread(new IrcInboundFilter(s,i2ps, expectedPong), "IRC Client " + __clientId + " in"); Thread in = new I2PAppThread(new IrcInboundFilter(s,i2ps, expectedPong), "IRC Client " + __clientId + " in", true);
in.start(); in.start();
Thread out = new I2PAppThread(new IrcOutboundFilter(s,i2ps, expectedPong), "IRC Client " + __clientId + " out"); Thread out = new I2PAppThread(new IrcOutboundFilter(s,i2ps, expectedPong), "IRC Client " + __clientId + " out", true);
out.start(); out.start();
} catch (Exception ex) { } catch (Exception ex) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR))
@ -117,13 +117,13 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
/************************************************************************* /*************************************************************************
* *
*/ */
private class IrcInboundFilter implements Runnable { public static class IrcInboundFilter implements Runnable {
private Socket local; private Socket local;
private I2PSocket remote; private I2PSocket remote;
private StringBuilder expectedPong; private StringBuffer expectedPong;
IrcInboundFilter(Socket _local, I2PSocket _remote, StringBuilder pong) { public IrcInboundFilter(Socket _local, I2PSocket _remote, StringBuffer pong) {
local=_local; local=_local;
remote=_remote; remote=_remote;
expectedPong=pong; expectedPong=pong;
@ -191,13 +191,13 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
/************************************************************************* /*************************************************************************
* *
*/ */
private class IrcOutboundFilter implements Runnable { public static class IrcOutboundFilter implements Runnable {
private Socket local; private Socket local;
private I2PSocket remote; private I2PSocket remote;
private StringBuilder expectedPong; private StringBuffer expectedPong;
IrcOutboundFilter(Socket _local, I2PSocket _remote, StringBuilder pong) { public IrcOutboundFilter(Socket _local, I2PSocket _remote, StringBuffer pong) {
local=_local; local=_local;
remote=_remote; remote=_remote;
expectedPong=pong; expectedPong=pong;
@ -266,7 +266,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
* *
*/ */
public String inboundFilter(String s, StringBuilder expectedPong) { public static String inboundFilter(String s, StringBuffer expectedPong) {
String field[]=s.split(" ",4); String field[]=s.split(" ",4);
String command; String command;
@ -353,7 +353,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
return null; return null;
} }
public String outboundFilter(String s, StringBuilder expectedPong) { public static String outboundFilter(String s, StringBuffer expectedPong) {
String field[]=s.split(" ",3); String field[]=s.split(" ",3);
String command; String command;
@ -378,7 +378,8 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
"KICK", "KICK",
"HELPME", "HELPME",
"RULES", "RULES",
"TOPIC" "TOPIC",
"ISON" // jIRCii uses this for a ping (response is 303)
}; };
if(field[0].length()==0) if(field[0].length()==0)
@ -390,7 +391,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
command = field[0].toUpperCase(); command = field[0].toUpperCase();
if ("PING".equalsIgnoreCase(command)) { if ("PING".equals(command)) {
// Most clients just send a PING and are happy with any old PONG. Others, // Most clients just send a PING and are happy with any old PONG. Others,
// like BitchX, actually expect certain behavior. It sends two different pings: // like BitchX, actually expect certain behavior. It sends two different pings:
// "PING :irc.freshcoffee.i2p" and "PING 1234567890 127.0.0.1" (where the IP is the proxy) // "PING :irc.freshcoffee.i2p" and "PING 1234567890 127.0.0.1" (where the IP is the proxy)
@ -426,19 +427,19 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
return rv; return rv;
} }
if ("PONG".equalsIgnoreCase(command)) if ("PONG".equals(command))
return "PONG 127.0.0.1"; // no way to know what the ircd to i2ptunnel server con is, so localhost works return "PONG 127.0.0.1"; // no way to know what the ircd to i2ptunnel server con is, so localhost works
// Allow all allowedCommands // Allow all allowedCommands
for(int i=0;i<allowedCommands.length;i++) for(int i=0;i<allowedCommands.length;i++)
{ {
if(allowedCommands[i].equalsIgnoreCase(command)) if(allowedCommands[i].equals(command))
return s; return s;
} }
// mIRC sends "NOTICE user :DCC Send file (IP)" // mIRC sends "NOTICE user :DCC Send file (IP)"
// in addition to the CTCP version // in addition to the CTCP version
if("NOTICE".equalsIgnoreCase(command)) if("NOTICE".equals(command))
{ {
String msg = field[2]; String msg = field[2];
if(msg.startsWith(":DCC ")) if(msg.startsWith(":DCC "))
@ -447,7 +448,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
} }
// Allow PRIVMSG, but block CTCP (except ACTION). // Allow PRIVMSG, but block CTCP (except ACTION).
if("PRIVMSG".equalsIgnoreCase(command) || "NOTICE".equalsIgnoreCase(command)) if("PRIVMSG".equals(command) || "NOTICE".equals(command))
{ {
String msg; String msg;
msg = field[2]; msg = field[2];
@ -465,14 +466,16 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
return s; return s;
} }
if("USER".equalsIgnoreCase(command)) { if("USER".equals(command)) {
int idx = field[2].lastIndexOf(":"); int idx = field[2].lastIndexOf(":");
if(idx<0) if(idx<0)
return "USER user hostname localhost :realname"; return "USER user hostname localhost :realname";
String realname = field[2].substring(idx+1); String realname = field[2].substring(idx+1);
String ret = "USER "+field[1]+" hostname localhost :"+realname; String ret = "USER "+field[1]+" hostname localhost :"+realname;
return ret; return ret;
} else if ("QUIT".equalsIgnoreCase(command)) { }
if ("QUIT".equals(command)) {
return "QUIT :leaving"; return "QUIT :leaving";
} }

View File

@ -144,6 +144,8 @@ public class TunnelController implements Logging {
startIrcClient(); startIrcClient();
} else if("sockstunnel".equals(type)) { } else if("sockstunnel".equals(type)) {
startSocksClient(); startSocksClient();
} else if("socksirctunnel".equals(type)) {
startSocksIRCClient();
} else if("connectclient".equals(type)) { } else if("connectclient".equals(type)) {
startConnectClient(); startConnectClient();
} else if ("client".equals(type)) { } else if ("client".equals(type)) {
@ -211,6 +213,14 @@ public class TunnelController implements Logging {
_tunnel.runSOCKSTunnel(new String[] { listenPort, sharedClient }, this); _tunnel.runSOCKSTunnel(new String[] { listenPort, sharedClient }, this);
} }
/** @since 0.7.12 */
private void startSocksIRCClient() {
setListenOn();
String listenPort = getListenPort();
String sharedClient = getSharedClient();
_tunnel.runSOCKSIRCTunnel(new String[] { listenPort, sharedClient }, this);
}
/* /*
* Streamr client is a UDP server, use the listenPort field for targetPort * Streamr client is a UDP server, use the listenPort field for targetPort
* and the listenOnInterface field for the targetHost * and the listenOnInterface field for the targetHost

View File

@ -0,0 +1,62 @@
/* I2PSOCKSTunnel is released under the terms of the GNU GPL,
* with an additional exception. For further details, see the
* licensing terms in I2PTunnel.java.
*
* Copyright (c) 2004 by human
*/
package net.i2p.i2ptunnel.socks;
import java.net.Socket;
import net.i2p.I2PAppContext;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.i2ptunnel.I2PTunnel;
import net.i2p.i2ptunnel.I2PTunnelIRCClient;
import net.i2p.i2ptunnel.Logging;
import net.i2p.util.EventDispatcher;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
/*
* Pipe SOCKS IRC connections through I2PTunnelIRCClient filtering,
* to get the best of both worlds:
*
* - SOCKS lets you specify the host so you don't have to set up
* a tunnel for each IRC server in advance
* - IRC filtering for security
*
* @since 0.7.12
* @author zzz
*/
public class I2PSOCKSIRCTunnel extends I2PSOCKSTunnel {
private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(I2PSOCKSIRCTunnel.class);
private static final int __clientId = 0;
public I2PSOCKSIRCTunnel(int localPort, Logging l, boolean ownDest, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(localPort, l, ownDest, notifyThis, tunnel);
setName(getLocalPort() + " -> SOCKSIRCTunnel");
}
/**
* Same as in I2PSOCKSTunnel, but run the filters from I2PTunnelIRCClient
* instead of I2PTunnelRunner
*/
@Override
protected void clientConnectionRun(Socket s) {
try {
_log.error("SOCKS IRC Tunnel Start");
SOCKSServer serv = SOCKSServerFactory.createSOCKSServer(s);
Socket clientSock = serv.getClientSocket();
I2PSocket destSock = serv.getDestinationI2PSocket(this);
StringBuffer expectedPong = new StringBuffer();
Thread in = new I2PAppThread(new I2PTunnelIRCClient.IrcInboundFilter(clientSock, destSock, expectedPong), "SOCKS IRC Client " + (++__clientId) + " in", true);
in.start();
Thread out = new I2PAppThread(new I2PTunnelIRCClient.IrcOutboundFilter(clientSock, destSock, expectedPong), "SOCKS IRC Client " + __clientId + " out", true);
out.start();
} catch (SOCKSException e) {
_log.error("Error from SOCKS connection", e);
closeSocket(s);
}
}
}

View File

@ -89,10 +89,10 @@ public class SOCKS5Server extends SOCKSServer {
int method = Method.NO_ACCEPTABLE_METHODS; int method = Method.NO_ACCEPTABLE_METHODS;
for (int i = 0; i < nMethods; ++i) { for (int i = 0; i < nMethods; ++i) {
method = in.readByte() & 0xff; int meth = in.readByte() & 0xff;
if (method == Method.NO_AUTH_REQUIRED) { if (meth == Method.NO_AUTH_REQUIRED) {
// That's fine, we do support this method // That's fine, we do support this method
break; method = meth;
} }
} }
@ -119,7 +119,7 @@ public class SOCKS5Server extends SOCKSServer {
int socksVer = in.readByte() & 0xff; int socksVer = in.readByte() & 0xff;
if (socksVer != SOCKS_VERSION_5) { if (socksVer != SOCKS_VERSION_5) {
_log.debug("error in SOCKS5 request (protocol != 5? wtf?)"); _log.debug("error in SOCKS5 request (protocol != 5? wtf?)");
throw new SOCKSException("Invalid protocol version in request"); throw new SOCKSException("Invalid protocol version in request: " + socksVer);
} }
int command = in.readByte() & 0xff; int command = in.readByte() & 0xff;

View File

@ -349,6 +349,7 @@ public class IndexBean {
return ( ("client".equals(type)) || return ( ("client".equals(type)) ||
("httpclient".equals(type)) || ("httpclient".equals(type)) ||
("sockstunnel".equals(type)) || ("sockstunnel".equals(type)) ||
("socksirctunnel".equals(type)) ||
("connectclient".equals(type)) || ("connectclient".equals(type)) ||
("streamrclient".equals(type)) || ("streamrclient".equals(type)) ||
("ircclient".equals(type))); ("ircclient".equals(type)));
@ -385,6 +386,7 @@ public class IndexBean {
else if ("server".equals(internalType)) return _("Standard server"); else if ("server".equals(internalType)) return _("Standard server");
else if ("httpserver".equals(internalType)) return _("HTTP server"); else if ("httpserver".equals(internalType)) return _("HTTP server");
else if ("sockstunnel".equals(internalType)) return _("SOCKS 4/4a/5 proxy"); else if ("sockstunnel".equals(internalType)) return _("SOCKS 4/4a/5 proxy");
else if ("socksirctunnel".equals(internalType)) return _("SOCKS IRC proxy");
else if ("connectclient".equals(internalType)) return _("CONNECT/SSL/HTTPS proxy"); else if ("connectclient".equals(internalType)) return _("CONNECT/SSL/HTTPS proxy");
else if ("ircserver".equals(internalType)) return _("IRC server"); else if ("ircserver".equals(internalType)) return _("IRC server");
else if ("streamrclient".equals(internalType)) return _("Streamr client"); else if ("streamrclient".equals(internalType)) return _("Streamr client");

View File

@ -250,7 +250,8 @@
} }
%></div> %></div>
<% if (!"sockstunnel".equals(indexBean.getInternalType(curClient))) { %> <% if (!("sockstunnel".equals(indexBean.getInternalType(curClient)) ||
"socksirctunnel".equals(indexBean.getInternalType(curClient)))) { %>
<div class="destinationField rowItem"> <div class="destinationField rowItem">
<label> <label>
<% if ("httpclient".equals(indexBean.getInternalType(curClient)) || "connectclient".equals(indexBean.getInternalType(curClient))) { %> <% if ("httpclient".equals(indexBean.getInternalType(curClient)) || "connectclient".equals(indexBean.getInternalType(curClient))) { %>
@ -288,6 +289,7 @@
<option value="httpclient">HTTP</option> <option value="httpclient">HTTP</option>
<option value="ircclient">IRC</option> <option value="ircclient">IRC</option>
<option value="sockstunnel">SOCKS 4/4a/5</option> <option value="sockstunnel">SOCKS 4/4a/5</option>
<option value="socksirctunnel">SOCKS IRC</option>
<option value="connectclient">CONNECT</option> <option value="connectclient">CONNECT</option>
<option value="streamrclient">Streamr</option> <option value="streamrclient">Streamr</option>
</select> </select>

View File

@ -1,3 +1,15 @@
2010-03-05 zzz
* I2PSOCKSIRCTunnel:
- New, for filtering IRC client traffic when using SOCKS
* I2PTunnelIRCClient:
- Make filter classes static and public for use by SOCKS
- Eliminate redundant case conversion
- Pass ISON message through (jIRCii uses it for pings)
- Switch back to StringBuffer since it's used by 2 threads
- Set daemon on filter threads
* SOCKS5Server:
- Fix handling of multiple authentication methods
2010-03-02 zzz 2010-03-02 zzz
* Console: * Console:
- Add link to jobs.jsp on configservice.jsp - Add link to jobs.jsp on configservice.jsp

View File

@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */ /** deprecated */
public final static String ID = "Monotone"; public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION; public final static String VERSION = CoreVersion.VERSION;
public final static long BUILD = 8; public final static long BUILD = 9;
/** for example "-test" */ /** for example "-test" */
public final static String EXTRA = ""; public final static String EXTRA = "";