diff --git a/apps/sam/java/src/net/i2p/sam/SAMBridge.java b/apps/sam/java/src/net/i2p/sam/SAMBridge.java index 7f0678101c2a9578e20463f5dcae81f9285ff0ff..8d38f4f579be8216e1c1f9bda4630cb00dfb4a87 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMBridge.java +++ b/apps/sam/java/src/net/i2p/sam/SAMBridge.java @@ -12,6 +12,7 @@ import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; +import java.util.Properties; import net.i2p.util.I2PThread; import net.i2p.util.Log; @@ -25,81 +26,136 @@ public class SAMBridge implements Runnable { private final static Log _log = new Log(SAMBridge.class); private ServerSocket serverSocket; + private Properties i2cpProps; private boolean acceptConnections = true; private final static int SAM_LISTENPORT = 7656; - /** - * Build a new SAM bridge listening on 127.0.0.1. - * - * @param listenPort The port to listen on - */ - public SAMBridge(int listenPort) { - this((String)null, listenPort); - } - + private SAMBridge() {} + /** * Build a new SAM bridge. * - * @param listenHost The network interface to listen on - * @param listenPort The port to listen on + * @param listenHost hostname to listen for SAM connections on ("0.0.0.0" for all) + * @param listenPort port number to listen for SAM connections on + * @param i2cpProps set of I2CP properties for finding and communicating with the router */ - public SAMBridge(String listenHost, int listenPort) { - try { - if (listenHost != null) { - serverSocket = new ServerSocket(listenPort, 0, - InetAddress.getByName(listenHost)); - _log.debug("SAM bridge listening on " - + listenHost + ":" + listenPort); - } else { - serverSocket = new ServerSocket(listenPort); - _log.debug("SAM bridge listening on 0.0.0.0:" + listenPort); - } - } catch (Exception e) { - _log.error("Error starting SAM bridge on " - + (listenHost == null ? "0.0.0.0" : listenHost) - + ":" + listenPort, e); - } - + public SAMBridge(String listenHost, int listenPort, Properties i2cpProps) { + try { + if ( (listenHost != null) && !("0.0.0.0".equals(listenHost)) ) { + serverSocket = new ServerSocket(listenPort, 0, InetAddress.getByName(listenHost)); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("SAM bridge listening on " + + listenHost + ":" + listenPort); + } else { + serverSocket = new ServerSocket(listenPort); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("SAM bridge listening on 0.0.0.0:" + listenPort); + } + } catch (Exception e) { + if (_log.shouldLog(Log.ERROR)) + _log.error("Error starting SAM bridge on " + + (listenHost == null ? "0.0.0.0" : listenHost) + + ":" + listenPort, e); + } + + this.i2cpProps = i2cpProps; } + /** + * Usage: + * <pre>SAMBridge [[listenHost ]listenPort[ name=val]*]</pre> + * + * name=val options are passed to the I2CP code to build a session, + * allowing the bridge to specify an alternate I2CP host and port, tunnel + * depth, etc. + */ public static void main(String args[]) { - SAMBridge bridge = new SAMBridge(SAM_LISTENPORT); - I2PThread t = new I2PThread(bridge, "SAMListener"); - t.start(); + int port = SAM_LISTENPORT; + String host = "0.0.0.0"; + Properties opts = null; + if (args.length > 0) { + int portIndex = 0; + try { + port = Integer.parseInt(args[portIndex]); + } catch (NumberFormatException nfe) { + host = args[0]; + portIndex++; + try { + port = Integer.parseInt(args[portIndex]); + } catch (NumberFormatException nfe1) { + usage(); + return; + } + } + opts = parseOptions(args, portIndex+1); + } + SAMBridge bridge = new SAMBridge(host, port, opts); + I2PThread t = new I2PThread(bridge, "SAMListener"); + t.start(); } + private static Properties parseOptions(String args[], int startArgs) { + Properties props = new Properties(); + // skip over first few options + for (int i = startArgs; i < args.length; i++) { + int eq = args[i].indexOf('='); + if (eq <= 0) continue; + if (eq >= args[i].length()-1) continue; + String key = args[i].substring(0, eq); + String val = args[i].substring(eq+1); + key = key.trim(); + val = val.trim(); + if ( (key.length() > 0) && (val.length() > 0) ) + props.setProperty(key, val); + } + return props; + } + + private static void usage() { + System.err.println("Usage: SAMBridge [listenHost listenPortNum[ name=val]*]"); + System.err.println(" listenHost: interface to listen on (0.0.0.0 for all interfaces)"); + System.err.println(" listenPort: port to listen for SAM connections on (default 7656)"); + System.err.println(" name=val: options to pass when connecting via I2CP, such as "); + System.err.println(" i2cp.host=localhost and i2cp.port=7654"); + } + public void run() { - try { - while (acceptConnections) { - Socket s = serverSocket.accept(); - _log.debug("New connection from " - + s.getInetAddress().toString() + ":" - + s.getPort()); - - try { - SAMHandler handler = SAMHandlerFactory.createSAMHandler(s); - if (handler == null) { - _log.debug("SAM handler has not been instantiated"); - try { - s.close(); - } catch (IOException e) {} - continue; - } - handler.startHandling(); - } catch (SAMException e) { - _log.error("SAM error: " + e.getMessage()); - s.close(); - } - } - } catch (Exception e) { - _log.error("Unexpected error while listening for connections", e); - } finally { - try { - _log.debug("Shutting down, closing server socket"); - serverSocket.close(); - } catch (IOException e) {} - } + try { + while (acceptConnections) { + Socket s = serverSocket.accept(); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("New connection from " + + s.getInetAddress().toString() + ":" + + s.getPort()); + + try { + SAMHandler handler = SAMHandlerFactory.createSAMHandler(s, i2cpProps); + if (handler == null) { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("SAM handler has not been instantiated"); + try { + s.close(); + } catch (IOException e) {} + continue; + } + handler.startHandling(); + } catch (SAMException e) { + if (_log.shouldLog(Log.ERROR)) + _log.error("SAM error: " + e.getMessage(), e); + s.close(); + } + } + } catch (Exception e) { + if (_log.shouldLog(Log.ERROR)) + _log.error("Unexpected error while listening for connections", e); + } finally { + try { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Shutting down, closing server socket"); + serverSocket.close(); + } catch (IOException e) {} + } } } diff --git a/apps/sam/java/src/net/i2p/sam/SAMHandler.java b/apps/sam/java/src/net/i2p/sam/SAMHandler.java index be68e09e1169480c25294184c394b95e3305f212..f1084316c8f973ea0b7442dd656225223b191a71 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMHandler.java +++ b/apps/sam/java/src/net/i2p/sam/SAMHandler.java @@ -13,6 +13,8 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; +import java.util.Properties; + import net.i2p.util.I2PThread; import net.i2p.util.Log; @@ -35,6 +37,9 @@ public abstract class SAMHandler implements Runnable { protected int verMajor = 0; protected int verMinor = 0; + + /** I2CP options configuring the I2CP connection (port, host, numHops, etc) */ + protected Properties i2cpProps = null; private Object stopLock = new Object(); private boolean stopHandler = false; @@ -45,14 +50,16 @@ public abstract class SAMHandler implements Runnable { * @param s Socket attached to a SAM client * @param verMajor SAM major version to manage * @param verMinor SAM minor version to manage + * @param i2cpProps properties to configure the I2CP connection (host, port, etc) */ protected SAMHandler(Socket s, - int verMajor, int verMinor) throws IOException { + int verMajor, int verMinor, Properties i2cpProps) throws IOException { socket = s; socketOS = socket.getOutputStream(); this.verMajor = verMajor; this.verMinor = verMinor; + this.i2cpProps = i2cpProps; } /** diff --git a/apps/sam/java/src/net/i2p/sam/SAMHandlerFactory.java b/apps/sam/java/src/net/i2p/sam/SAMHandlerFactory.java index 26dbfd288fc3d2e8c2b8c87fa4cce416c0d8181a..6bb6f6d8642d170a852a849eff385e168c4dbfcc 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMHandlerFactory.java +++ b/apps/sam/java/src/net/i2p/sam/SAMHandlerFactory.java @@ -31,10 +31,11 @@ public class SAMHandlerFactory { * required by the client. * * @param s Socket attached to SAM client + * @param i2cpProps config options for our i2cp connection * * @return A SAM protocol handler */ - public static SAMHandler createSAMHandler(Socket s) throws SAMException { + public static SAMHandler createSAMHandler(Socket s, Properties i2cpProps) throws SAMException { BufferedReader br; String line; StringTokenizer tok; @@ -127,7 +128,7 @@ public class SAMHandlerFactory { try { switch (verMajor) { case 1: - handler = new SAMv1Handler(s, verMajor, verMinor); + handler = new SAMv1Handler(s, verMajor, verMinor, i2cpProps); break; default: _log.error("BUG! Trying to initialize the wrong SAM version!"); diff --git a/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java b/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java index b020ed86df645f20b0225b60cb30ba2703d0b1d4..b33c12943306046e32ba014185b581717a29a9f3 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java +++ b/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java @@ -55,7 +55,20 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag * @param verMinor SAM minor version to manage */ public SAMv1Handler(Socket s, int verMajor, int verMinor) throws SAMException, IOException { - super(s, verMajor, verMinor); + this(s, verMajor, verMinor, new Properties()); + } + /** + * Create a new SAM version 1 handler. This constructor expects + * that the SAM HELLO message has been still answered (and + * stripped) from the socket input stream. + * + * @param s Socket attached to a SAM client + * @param verMajor SAM major version to manage (should be 1) + * @param verMinor SAM minor version to manage + * @param i2cpProps properties to configure the I2CP connection (host, port, etc) + */ + public SAMv1Handler(Socket s, int verMajor, int verMinor, Properties i2cpProps) throws SAMException, IOException { + super(s, verMajor, verMinor, i2cpProps); _log.debug("SAM version 1 handler instantiated"); if ((this.verMajor != 1) || (this.verMinor != 0)) { @@ -113,6 +126,8 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag + "\"; opcode: \"" + opcode + "\")"); } props = SAMUtils.parseParams(tok); + if (i2cpProps != null) + props.putAll(i2cpProps); // make sure we've got the i2cp settings if (domain.equals("STREAM")) { canContinue = execStreamMessage(opcode, props);