diff --git a/apps/sam/java/src/net/i2p/sam/client/SAMStreamSend.java b/apps/sam/java/src/net/i2p/sam/client/SAMStreamSend.java index 08a7c057529a65aec3f1fd84200d73b08103fda0..bab944162e5f5c646b3775f53ed009aad984fdc2 100644 --- a/apps/sam/java/src/net/i2p/sam/client/SAMStreamSend.java +++ b/apps/sam/java/src/net/i2p/sam/client/SAMStreamSend.java @@ -6,8 +6,10 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; +import java.security.GeneralSecurityException; import java.util.HashMap; import java.util.Map; +import javax.net.ssl.SSLSocket; import gnu.getopt.Getopt; @@ -15,6 +17,7 @@ import net.i2p.I2PAppContext; import net.i2p.data.Base32; import net.i2p.data.DataHelper; import net.i2p.util.I2PAppThread; +import net.i2p.util.I2PSSLSocketFactory; import net.i2p.util.Log; import net.i2p.util.VersionComparator; @@ -38,19 +41,22 @@ public class SAMStreamSend { //private boolean _dead; /** Connection id (Integer) to peer (Flooder) */ private final Map<String, Sender> _remotePeers; + private static I2PSSLSocketFactory _sslSocketFactory; private static final int STREAM=0, DG=1, V1DG=2, RAW=3, V1RAW=4; - private static final String USAGE = "Usage: SAMStreamSend [-s] [-m mode] [-v version] [-b samHost] [-p samPort] peerDestFile dataDir\n" + + private static final String USAGE = "Usage: SAMStreamSend [-s] [-m mode] [-v version] [-b samHost] [-p samPort] [-u user] [-w password] peerDestFile dataDir\n" + " modes: stream: 0; datagram: 1; v1datagram: 2; raw: 3; v1raw: 4\n" + " -s: use SSL"; public static void main(String args[]) { - Getopt g = new Getopt("SAM", args, "sb:m:p:v:"); + Getopt g = new Getopt("SAM", args, "sb:m:p:u:v:w:"); boolean isSSL = false; int mode = STREAM; String version = "1.0"; String host = "127.0.0.1"; String port = "7656"; + String user = null; + String password = null; int c; while ((c = g.getopt()) != -1) { switch (c) { @@ -78,6 +84,14 @@ public class SAMStreamSend { port = g.getOptarg(); break; + case 'u': + user = g.getOptarg(); + break; + + case 'w': + password = g.getOptarg(); + break; + case 'h': case '?': case ':': @@ -92,10 +106,19 @@ public class SAMStreamSend { System.err.println(USAGE); return; } + if ((user == null && password != null) || + (user != null && password == null)) { + System.err.println("both user and password or neither"); + return; + } + if (user != null && password != null && VersionComparator.comp(version, "3.2") < 0) { + System.err.println("user/password require 3.2"); + return; + } I2PAppContext ctx = I2PAppContext.getGlobalContext(); SAMStreamSend sender = new SAMStreamSend(ctx, host, port, args[startArgs], args[startArgs + 1]); - sender.startup(version, isSSL, mode); + sender.startup(version, isSSL, mode, user, password); } public SAMStreamSend(I2PAppContext ctx, String samHost, String samPort, String destFile, String dataFile) { @@ -110,7 +133,7 @@ public class SAMStreamSend { _remotePeers = new HashMap<String, Sender>(); } - public void startup(String version, boolean isSSL, int mode) { + public void startup(String version, boolean isSSL, int mode, String user, String password) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Starting up"); try { @@ -121,7 +144,7 @@ public class SAMStreamSend { if (_log.shouldLog(Log.DEBUG)) _log.debug("Reader created"); OutputStream out = sock.getOutputStream(); - String ourDest = handshake(out, version, true, eventHandler, mode); + String ourDest = handshake(out, version, true, eventHandler, mode, user, password); if (ourDest == null) throw new IOException("handshake failed"); if (_log.shouldLog(Log.DEBUG)) @@ -134,7 +157,7 @@ public class SAMStreamSend { if (_log.shouldLog(Log.DEBUG)) _log.debug("Reader2 created"); out = sock2.getOutputStream(); - String ok = handshake(out, version, false, eventHandler, mode); + String ok = handshake(out, version, false, eventHandler, mode, user, password); if (ok == null) throw new IOException("2nd handshake failed"); if (_log.shouldLog(Log.DEBUG)) @@ -172,14 +195,33 @@ public class SAMStreamSend { } private Socket connect(boolean isSSL) throws IOException { - return new Socket(_samHost, Integer.parseInt(_samPort)); + int port = Integer.parseInt(_samPort); + if (!isSSL) + return new Socket(_samHost, port); + synchronized(SAMStreamSink.class) { + if (_sslSocketFactory == null) { + try { + _sslSocketFactory = new I2PSSLSocketFactory( + _context, true, "certificates/sam"); + } catch (GeneralSecurityException gse) { + throw new IOException("SSL error", gse); + } + } + } + SSLSocket sock = (SSLSocket) _sslSocketFactory.createSocket(_samHost, port); + I2PSSLSocketFactory.verifyHostname(_context, sock, _samHost); + return sock; } /** @return our b64 dest or null */ - private String handshake(OutputStream samOut, String version, boolean isMaster, SAMEventHandler eventHandler, int mode) { + private String handshake(OutputStream samOut, String version, boolean isMaster, + SAMEventHandler eventHandler, int mode, String user, String password) { synchronized (samOut) { try { - samOut.write(("HELLO VERSION MIN=1.0 MAX=" + version + '\n').getBytes()); + if (user != null && password != null) + samOut.write(("HELLO VERSION MIN=1.0 MAX=" + version + " USER=" + user + " PASSWORD=" + password + '\n').getBytes()); + else + samOut.write(("HELLO VERSION MIN=1.0 MAX=" + version + '\n').getBytes()); samOut.flush(); if (_log.shouldLog(Log.DEBUG)) _log.debug("Hello sent"); diff --git a/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java b/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java index b582fa8069fa9bdcfbd2017d1314e8150a26f5db..19c7e21d34b379f613d28a455cda30d58fb47043 100644 --- a/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java +++ b/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java @@ -6,9 +6,11 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; +import java.security.GeneralSecurityException; import java.util.HashMap; import java.util.Map; import java.util.Properties; +import javax.net.ssl.SSLSocket; import gnu.getopt.Getopt; @@ -16,6 +18,7 @@ import net.i2p.I2PAppContext; import net.i2p.data.Base32; import net.i2p.data.DataHelper; import net.i2p.util.I2PAppThread; +import net.i2p.util.I2PSSLSocketFactory; import net.i2p.util.Log; import net.i2p.util.VersionComparator; @@ -41,19 +44,22 @@ public class SAMStreamSink { //private boolean _dead; /** Connection id (Integer) to peer (Flooder) */ private final Map<String, Sink> _remotePeers; + private static I2PSSLSocketFactory _sslSocketFactory; private static final int STREAM=0, DG=1, V1DG=2, RAW=3, V1RAW=4; - private static final String USAGE = "Usage: SAMStreamSink [-s] [-m mode] [-v version] [-b samHost] [-p samPort] myDestFile sinkDir\n" + + private static final String USAGE = "Usage: SAMStreamSink [-s] [-m mode] [-v version] [-b samHost] [-p samPort] [-u user] [-w password] myDestFile sinkDir\n" + " modes: stream: 0; datagram: 1; v1datagram: 2; raw: 3; v1raw: 4\n" + " -s: use SSL"; public static void main(String args[]) { - Getopt g = new Getopt("SAM", args, "sb:m:p:v:"); + Getopt g = new Getopt("SAM", args, "sb:m:p:u:v:w:"); boolean isSSL = false; int mode = STREAM; String version = "1.0"; String host = "127.0.0.1"; String port = "7656"; + String user = null; + String password = null; int c; while ((c = g.getopt()) != -1) { switch (c) { @@ -81,6 +87,14 @@ public class SAMStreamSink { port = g.getOptarg(); break; + case 'u': + user = g.getOptarg(); + break; + + case 'w': + password = g.getOptarg(); + break; + case 'h': case '?': case ':': @@ -95,10 +109,19 @@ public class SAMStreamSink { System.err.println(USAGE); return; } + if ((user == null && password != null) || + (user != null && password == null)) { + System.err.println("both user and password or neither"); + return; + } + if (user != null && password != null && VersionComparator.comp(version, "3.2") < 0) { + System.err.println("user/password require 3.2"); + return; + } I2PAppContext ctx = I2PAppContext.getGlobalContext(); SAMStreamSink sink = new SAMStreamSink(ctx, host, port, args[startArgs], args[startArgs + 1]); - sink.startup(version, isSSL, mode); + sink.startup(version, isSSL, mode, user, password); } public SAMStreamSink(I2PAppContext ctx, String samHost, String samPort, String destFile, String sinkDir) { @@ -113,7 +136,7 @@ public class SAMStreamSink { _remotePeers = new HashMap<String, Sink>(); } - public void startup(String version, boolean isSSL, int mode) { + public void startup(String version, boolean isSSL, int mode, String user, String password) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Starting up"); try { @@ -124,7 +147,7 @@ public class SAMStreamSink { _reader.startReading(); if (_log.shouldLog(Log.DEBUG)) _log.debug("Reader created"); - String ourDest = handshake(out, version, true, eventHandler, mode); + String ourDest = handshake(out, version, true, eventHandler, mode, user, password); if (ourDest == null) throw new IOException("handshake failed"); if (_log.shouldLog(Log.DEBUG)) @@ -142,7 +165,7 @@ public class SAMStreamSink { _reader2.startReading(); if (_log.shouldLog(Log.DEBUG)) _log.debug("Reader2 created"); - String ok = handshake(out, version, false, eventHandler, mode); + String ok = handshake(out, version, false, eventHandler, mode, user, password); if (ok == null) throw new IOException("2nd handshake failed"); if (_log.shouldLog(Log.DEBUG)) @@ -350,14 +373,33 @@ public class SAMStreamSink { } private Socket connect(boolean isSSL) throws IOException { - return new Socket(_samHost, Integer.parseInt(_samPort)); + int port = Integer.parseInt(_samPort); + if (!isSSL) + return new Socket(_samHost, port); + synchronized(SAMStreamSink.class) { + if (_sslSocketFactory == null) { + try { + _sslSocketFactory = new I2PSSLSocketFactory( + _context, true, "certificates/sam"); + } catch (GeneralSecurityException gse) { + throw new IOException("SSL error", gse); + } + } + } + SSLSocket sock = (SSLSocket) _sslSocketFactory.createSocket(_samHost, port); + I2PSSLSocketFactory.verifyHostname(_context, sock, _samHost); + return sock; } /** @return our b64 dest or null */ - private String handshake(OutputStream samOut, String version, boolean isMaster, SAMEventHandler eventHandler, int mode) { + private String handshake(OutputStream samOut, String version, boolean isMaster, + SAMEventHandler eventHandler, int mode, String user, String password) { synchronized (samOut) { try { - samOut.write(("HELLO VERSION MIN=1.0 MAX=" + version + '\n').getBytes()); + if (user != null && password != null) + samOut.write(("HELLO VERSION MIN=1.0 MAX=" + version + " USER=" + user + " PASSWORD=" + password + '\n').getBytes()); + else + samOut.write(("HELLO VERSION MIN=1.0 MAX=" + version + '\n').getBytes()); samOut.flush(); if (_log.shouldLog(Log.DEBUG)) _log.debug("Hello sent");