diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java index 0b2d2d7c0..bfe311d3c 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java @@ -236,6 +236,8 @@ public class I2PTunnel implements Logging, EventDispatcher { runServer(args, l); } else if ("httpserver".equals(cmdname)) { runHttpServer(args, l); + } else if ("httpbidirserver".equals(cmdname)) { + runHttpBidirServer(args, l); } else if ("ircserver".equals(cmdname)) { runIrcServer(args, l); } else if ("textserver".equals(cmdname)) { @@ -300,6 +302,7 @@ public class I2PTunnel implements Logging, EventDispatcher { l.log("ping "); l.log("server "); l.log("httpserver "); + l.log("httpbidirserver "); l.log("textserver "); l.log("genkeys []"); l.log("gentextkeys"); @@ -503,6 +506,80 @@ public class I2PTunnel implements Logging, EventDispatcher { } } + /** + * Run the HTTP server pointing at the host and port specified using the private i2p + * destination loaded from the specified file, replacing the HTTP headers + * so that the Host: specified is the one spoofed. Also runs an HTTP proxy for + * bidirectional communications on the same tunnel destination.

+ * + * Sets the event "serverTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error) + * Also sets the event "openServerResult" = "ok" or "error" (displaying "Ready!" on the logger after + * 'ok'). So, success = serverTaskId != -1 and openServerResult = ok. + * + * @param args {hostname, portNumber, proxyPortNumber, spoofedHost, privKeyFilename} + * @param l logger to receive events and output + */ + public void runHttpBidirServer(String args[], Logging l) { + if (args.length == 5) { + InetAddress serverHost = null; + int portNum = -1; + int port2Num = -1; + File privKeyFile = null; + try { + serverHost = InetAddress.getByName(args[0]); + } catch (UnknownHostException uhe) { + l.log("unknown host"); + _log.error(getPrefix() + "Error resolving " + args[0], uhe); + notifyEvent("serverTaskId", Integer.valueOf(-1)); + return; + } + + try { + portNum = Integer.parseInt(args[1]); + } catch (NumberFormatException nfe) { + l.log("invalid port"); + _log.error(getPrefix() + "Port specified is not valid: " + args[1], nfe); + notifyEvent("serverTaskId", Integer.valueOf(-1)); + return; + } + + try { + port2Num = Integer.parseInt(args[2]); + } catch (NumberFormatException nfe) { + l.log("invalid port"); + _log.error(getPrefix() + "Port specified is not valid: " + args[2], nfe); + notifyEvent("serverTaskId", Integer.valueOf(-1)); + return; + } + + String spoofedHost = args[3]; + + privKeyFile = new File(args[4]); + if (!privKeyFile.isAbsolute()) + privKeyFile = new File(_context.getConfigDir(), args[4]); + if (!privKeyFile.canRead()) { + l.log("private key file does not exist"); + _log.error(getPrefix() + "Private key file does not exist or is not readable: " + args[4]); + notifyEvent("serverTaskId", Integer.valueOf(-1)); + return; + } + + I2PTunnelHTTPBidirServer serv = new I2PTunnelHTTPBidirServer(serverHost, portNum, port2Num, privKeyFile, args[3], spoofedHost, l, (EventDispatcher) this, this); + serv.setReadTimeout(readTimeout); + serv.startRunning(); + addtask(serv); + notifyEvent("serverTaskId", Integer.valueOf(serv.getId())); + return; + } else { + l.log("httpserver "); + l.log(" creates a bidirectional HTTP server that sends all incoming data\n" + + " of its destination to host:port., filtering the HTTP\n" + + " headers so it looks like the request is to the spoofed host," + + " and listens to host:proxyport to proxy HTTP requests."); + notifyEvent("serverTaskId", Integer.valueOf(-1)); + } + } + /** * Run the server pointing at the host and port specified using the private i2p * destination loaded from the given base64 stream.

diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java index 535e1d2b9..cd3e9c1ce 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java @@ -93,12 +93,12 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna // true if we are chained from a server. private boolean chained = false; - public I2PTunnelClientBase(int localPort, Logging l, I2PSocketManager MGR, + public I2PTunnelClientBase(int localPort, Logging l, I2PSocketManager SktMgr, I2PTunnel tunnel, EventDispatcher notifyThis, long clientId ) throws IllegalArgumentException { super(localPort + " (uninitialized)", notifyThis, tunnel); chained = true; - sockMgr = MGR; + sockMgr = SktMgr; _clientId = clientId; this.localPort = localPort; this.l = l; diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPBidirProxy.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPBidirProxy.java new file mode 100644 index 000000000..ecc52762e --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPBidirProxy.java @@ -0,0 +1,54 @@ +/** + * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + * Version 2, December 2004 + * + * Copyright (C) sponge + * Planet Earth + * Everyone is permitted to copy and distribute verbatim or modified + * copies of this license document, and changing it is allowed as long + * as the name is changed. + * + * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + * TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + * + * 0. You just DO WHAT THE FUCK YOU WANT TO. + * + * See... + * + * http://sam.zoy.org/wtfpl/ + * and + * http://en.wikipedia.org/wiki/WTFPL + * + * ...for any additional details and liscense questions. + */ + +package net.i2p.i2ptunnel; + +import java.util.ArrayList; + +import net.i2p.client.streaming.I2PSocketManager; +import net.i2p.util.EventDispatcher; + + +/** + * Reuse HTTP server's I2PSocketManager for a proxy with no outproxy capability. + * + * @author sponge + */ +public class I2PTunnelHTTPBidirProxy extends I2PTunnelHTTPClient implements Runnable { + + + /** + * @throws IllegalArgumentException if the I2PTunnel does not contain + * valid config to contact the router + */ + public I2PTunnelHTTPBidirProxy(int localPort, Logging l, I2PSocketManager sockMgr, I2PTunnel tunnel, EventDispatcher notifyThis, long clientId) { + super(localPort, l, sockMgr, tunnel, notifyThis, clientId); + proxyList = new ArrayList(); + + setName(getLocalPort() + " -> HTTPClient [NO PROXIES]"); + startRunning(); + + notifyEvent("openHTTPClientResult", "ok"); + } +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPBidirServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPBidirServer.java new file mode 100644 index 000000000..616867bb2 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPBidirServer.java @@ -0,0 +1,44 @@ +/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java) + * (c) 2003 - 2004 mihi + */ +package net.i2p.i2ptunnel; + +import java.io.File; +import java.io.InputStream; +import java.net.InetAddress; + +import net.i2p.util.EventDispatcher; +import net.i2p.util.Log; + +public class I2PTunnelHTTPBidirServer extends I2PTunnelHTTPServer { + private final static Log log = new Log(I2PTunnelHTTPBidirServer.class); + + public I2PTunnelHTTPBidirServer(InetAddress host, int port, int proxyport, String privData, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) { + super(host, port, privData, spoofHost, l, notifyThis, tunnel); + I2PTunnelHTTPBidirServerSet(tunnel, l, proxyport); + } + + public I2PTunnelHTTPBidirServer(InetAddress host, int port, int proxyport, File privkey, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) { + super(host, port, privkey, privkeyname, spoofHost, l, notifyThis, tunnel); + I2PTunnelHTTPBidirServerSet(tunnel, l, proxyport); + } + + public I2PTunnelHTTPBidirServer(InetAddress host, int port, int proxyport, InputStream privData, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) { + super(host, port, privData, privkeyname, spoofHost, l, notifyThis, tunnel); + I2PTunnelHTTPBidirServerSet(tunnel, l, proxyport); + } + + private void I2PTunnelHTTPBidirServerSet(I2PTunnel tunnel, Logging l, int proxyport) { + + localPort = proxyport; + bidir = true; + + /* start the httpclient */ + task = new I2PTunnelHTTPBidirProxy(localPort, l, sockMgr, getTunnel(), getEventDispatcher(), __serverId); + sockMgr.setName("Server"); // TO-DO: Need to change this to "Bidir"! + getTunnel().addSession(sockMgr.getSession()); + l.log("Ready!"); + notifyEvent("openServerResult", "ok"); + } +} + diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java index a01f30664..3a24741c5 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java @@ -57,7 +57,7 @@ import net.i2p.util.Translate; public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable { private static final Log _log = new Log(I2PTunnelHTTPClient.class); - private final List proxyList; + protected List proxyList; private HashMap addressHelpers = new HashMap(); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java index af9f1c1ec..f0790ac38 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java @@ -39,21 +39,21 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { public I2PTunnelHTTPServer(InetAddress host, int port, String privData, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) { super(host, port, privData, l, notifyThis, tunnel); - _spoofHost = spoofHost; - getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 }); - getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpNullWorkaround", "How often an http server works around a streaming lib or i2ptunnel bug", "I2PTunnel", new long[] { 60*1000, 10*60*1000 }); + I2PTunnelHTTPServerSet(spoofHost); } public I2PTunnelHTTPServer(InetAddress host, int port, File privkey, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) { super(host, port, privkey, privkeyname, l, notifyThis, tunnel); - _spoofHost = spoofHost; - getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 }); - getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpNullWorkaround", "How often an http server works around a streaming lib or i2ptunnel bug", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000 }); + I2PTunnelHTTPServerSet(spoofHost); } public I2PTunnelHTTPServer(InetAddress host, int port, InputStream privData, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) { super(host, port, privData, privkeyname, l, notifyThis, tunnel); - _spoofHost = spoofHost; + I2PTunnelHTTPServerSet(spoofHost); + } + + private void I2PTunnelHTTPServerSet(String spoofHost) { + _spoofHost = spoofHost; getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 }); getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpNullWorkaround", "How often an http server works around a streaming lib or i2ptunnel bug", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000 }); } diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java index 455856448..ff21c27c4 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java @@ -48,47 +48,29 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { protected long readTimeout = DEFAULT_READ_TIMEOUT; private static final boolean DEFAULT_USE_POOL = false; + protected static volatile long __serverId = 0; + private static final String PROP_HANDLER_COUNT = "i2ptunnel.blockingHandlerCount"; + private static final int DEFAULT_HANDLER_COUNT = 10; - private I2PTunnelTask task = null; - private boolean bidir = false; - private int localPort = 4445; - private int DEFAULT_LOCALPORT = 4445; + + protected I2PTunnelTask task = null; + protected boolean bidir = false; + + private int DEFAULT_LOCALPORT = 4488; + protected int localPort = DEFAULT_LOCALPORT; public I2PTunnelServer(InetAddress host, int port, String privData, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) { super(host + ":" + port + " <- " + privData, notifyThis, tunnel); ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(privData)); - String usePool = tunnel.getClientOptions().getProperty("i2ptunnel.usePool"); - if (usePool != null) - _usePool = "true".equalsIgnoreCase(usePool); - else - _usePool = DEFAULT_USE_POOL; - String biDir = tunnel.getClientOptions().getProperty("i2ptunnel.bidir"); - if (biDir != null) { - bidir = "true".equalsIgnoreCase(biDir); - String lp = tunnel.getClientOptions().getProperty("i2ptunnel.bidir.port"); - if (lp != null) - localPort = Integer.parseInt(lp); - else - localPort = DEFAULT_LOCALPORT; - } else - bidir = false; + SetUsePool(tunnel); init(host, port, bais, privData, l); } public I2PTunnelServer(InetAddress host, int port, File privkey, String privkeyname, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) { super(host + ":" + port + " <- " + privkeyname, notifyThis, tunnel); - String usePool = tunnel.getClientOptions().getProperty("i2ptunnel.usePool"); - if (usePool != null) - _usePool = "true".equalsIgnoreCase(usePool); - else - _usePool = DEFAULT_USE_POOL; - String biDir = tunnel.getClientOptions().getProperty("i2ptunnel.bidir"); - if (biDir != null) - bidir = "true".equalsIgnoreCase(biDir); - else - bidir = false; + SetUsePool(tunnel); FileInputStream fis = null; try { fis = new FileInputStream(privkey); @@ -104,20 +86,18 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { public I2PTunnelServer(InetAddress host, int port, InputStream privData, String privkeyname, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) { super(host + ":" + port + " <- " + privkeyname, notifyThis, tunnel); - String usePool = tunnel.getClientOptions().getProperty("i2ptunnel.usePool"); - if (usePool != null) - _usePool = "true".equalsIgnoreCase(usePool); - else - _usePool = DEFAULT_USE_POOL; - String biDir = tunnel.getClientOptions().getProperty("i2ptunnel.bidir"); - if (biDir != null) - bidir = "true".equalsIgnoreCase(biDir); - else - bidir = false; + SetUsePool(tunnel); init(host, port, privData, privkeyname, l); } + private void SetUsePool(I2PTunnel Tunnel) { + String usePool = Tunnel.getClientOptions().getProperty("i2ptunnel.usePool"); + if (usePool != null) + _usePool = "true".equalsIgnoreCase(usePool); + else + _usePool = DEFAULT_USE_POOL; + } private void init(InetAddress host, int port, InputStream privData, String privkeyname, Logging l) { this.l = l; this.remoteHost = host; @@ -144,10 +124,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { try { Thread.sleep(10*1000); } catch (InterruptedException ie) {} } } - if(bidir == true) { - /* start the httpclient */ - task = new I2PTunnelHTTPClient(localPort, l, sockMgr, getTunnel(), getEventDispatcher(), __serverId); - } + sockMgr.setName("Server"); getTunnel().addSession(sockMgr.getSession()); l.log("Ready!"); @@ -156,8 +133,6 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { } - private static volatile long __serverId = 0; - /** * Start running the I2PTunnelServer. * @@ -215,9 +190,6 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { } } - private static final String PROP_HANDLER_COUNT = "i2ptunnel.blockingHandlerCount"; - private static final int DEFAULT_HANDLER_COUNT = 10; - protected int getHandlerCount() { int rv = DEFAULT_HANDLER_COUNT; String cnt = getTunnel().getClientOptions().getProperty(PROP_HANDLER_COUNT); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java index 06beffae5..4084b702c 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java @@ -154,6 +154,8 @@ public class TunnelController implements Logging { startServer(); } else if ("httpserver".equals(type)) { startHttpServer(); + } else if ("httpbidirserver".equals(type)) { + startHttpBidirServer(); } else if ("ircserver".equals(type)) { startIrcServer(); } else if ("streamrserver".equals(type)) { @@ -294,6 +296,16 @@ public class TunnelController implements Logging { _tunnel.runHttpServer(new String[] { targetHost, targetPort, spoofedHost, privKeyFile }, this); } + private void startHttpBidirServer() { + setListenOn(); + String targetHost = getTargetHost(); + String targetPort = getTargetPort(); + String listenPort = getListenPort(); + String spoofedHost = getSpoofedHost(); + String privKeyFile = getPrivKeyFile(); + _tunnel.runHttpBidirServer(new String[] { targetHost, targetPort, listenPort, spoofedHost, privKeyFile }, this); + } + private void startIrcServer() { String targetHost = getTargetHost(); String targetPort = getTargetPort(); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java index 6951286dc..ac1f7c77f 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java @@ -389,6 +389,7 @@ public class IndexBean { else if ("ircserver".equals(internalType)) return _("IRC server"); else if ("streamrclient".equals(internalType)) return _("Streamr client"); else if ("streamrserver".equals(internalType)) return _("Streamr server"); + else if ("httpbidirserver".equals(internalType)) return _("HTTP bidir"); else return internalType; } @@ -779,8 +780,11 @@ public class IndexBean { config.setProperty("listenPort", _port); if (_reachableByOther != null) config.setProperty("interface", _reachableByOther); - else + else if (_reachableBy != null) config.setProperty("interface", _reachableBy); + else + config.setProperty("interface", ""); + config.setProperty("sharedClient", _sharedClient + ""); for (String p : _booleanClientOpts) config.setProperty("option." + p, "" + _booleanOptions.contains(p)); @@ -806,11 +810,22 @@ public class IndexBean { } else if ("ircclient".equals(_type) || "client".equals(_type) || "streamrclient".equals(_type)) { if (_targetDestination != null) config.setProperty("targetDestination", _targetDestination); - } else if ("httpserver".equals(_type)) { + } else if ("httpserver".equals(_type) || "httpbidirserver".equals(_type)) { if (_spoofedHost != null) config.setProperty("spoofedHost", _spoofedHost); } - + if ("httpbidirserver".equals(_type)) { + if (_port != null) + config.setProperty("listenPort", _port); + if (_reachableByOther != null) + config.setProperty("interface", _reachableByOther); + else if (_reachableBy != null) + config.setProperty("interface", _reachableBy); + else if (_targetHost != null) + config.setProperty("interface", _targetHost); + else + config.setProperty("interface", ""); + } return config; } diff --git a/apps/i2ptunnel/jsp/editServer.jsp b/apps/i2ptunnel/jsp/editServer.jsp index 24f6b0d03..90a7614f2 100644 --- a/apps/i2ptunnel/jsp/editServer.jsp +++ b/apps/i2ptunnel/jsp/editServer.jsp @@ -113,11 +113,58 @@ + <% if ("httpbidirserver".equals(tunnelType)) { + %> +

+
+
+
+ +
+
+ + +
+ <% String otherInterface = ""; + String clientInterface = editBean.getClientInterface(curTunnel); + %> +
+ + +
+
+ + +
+ <% } %>

- <% if ("httpserver".equals(tunnelType)) { + <% if (("httpserver".equals(tunnelType)) || ("httpbidirserver".equals(tunnelType))) { %>