From 6d46a21f9fbbaebe55b28fa348c3261e14bae80e Mon Sep 17 00:00:00 2001 From: dev Date: Sun, 23 May 2010 16:04:22 +0000 Subject: [PATCH 1/2] implemented WEBIRC support in the I2PTunnel IRC server --- .../net/i2p/i2ptunnel/I2PTunnelIRCServer.java | 49 +++++++++++++++++-- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java index 6e0427095..3e8d927f2 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java @@ -31,6 +31,9 @@ import net.i2p.util.Log; * * There are three options for mangling the desthash. Put the option in the * "custom options" section of i2ptunnel. + * - ircserver.method unset: Defaults to user. + * - ircserver.method=user: Use method described above. + * - ircserver.method=webirc: Use the WEBIRC protocol. * - ircserver.cloakKey unset: Cloak with a random value that is persistent for * the life of this tunnel. This is the default. * - ircserver.cloakKey=somepassphrase: Cloak with the hash of the passphrase. Use this to @@ -39,6 +42,8 @@ import net.i2p.util.Log; * be able to track users even when they switch servers. * Note: don't quote or put spaces in the passphrase, * the i2ptunnel gui can't handle it. + * - ircserver.webircPassword=password The password to use for the WEBIRC protocol. + * - ircserver.webircSpoofIP=IP The IP * - ircserver.fakeHostname=%f.b32.i2p: Set the fake hostname sent by I2PTunnel, * %f is the full B32 destination hash * %c is the cloaked hash. @@ -48,7 +53,12 @@ import net.i2p.util.Log; * @author zzz */ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable { + public static final String PROP_METHOD="ircserver.method"; + public static final String PROP_METHOD_DEFAULT="user"; public static final String PROP_CLOAK="ircserver.cloakKey"; + public static final String PROP_WEBIRC_PASSWORD="ircserver.webircPassword"; + public static final String PROP_WEBIRC_SPOOF_IP="ircserver.webircSpoofIP"; + public static final String PROP_WEBIRC_SPOOF_IP_DEFAULT="127.0.0.1"; public static final String PROP_HOSTNAME="ircserver.fakeHostname"; public static final String PROP_HOSTNAME_DEFAULT="%f.b32.i2p"; @@ -67,7 +77,20 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable { /** generate a random 32 bytes, or the hash of the passphrase */ private void initCloak(I2PTunnel tunnel) { + // get the properties of this server-tunnel Properties opts = tunnel.getClientOptions(); + + // get method of host faking + this.method = opts.getProperty(PROP_METHOD, PROP_METHOD_DEFAULT); + assert this.method != null; + + // get the password for the webirc method + this.webircPassword = opts.getProperty(PROP_WEBIRC_PASSWORD); + + // get the spoof IP for the webirc method + this.webircSpoofIP = opts.getProperty(PROP_WEBIRC_SPOOF_IP, PROP_WEBIRC_SPOOF_IP_DEFAULT); + + // get the cloaking passphrase String passphrase = opts.getProperty(PROP_CLOAK); if (passphrase == null) { this.cloakKey = new byte[Hash.HASH_LENGTH]; @@ -76,17 +99,30 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable { this.cloakKey = SHA256Generator.getInstance().calculateHash(passphrase.trim().getBytes()).getData(); } + // get the fake hostmask to use this.hostname = opts.getProperty(PROP_HOSTNAME, PROP_HOSTNAME_DEFAULT); } @Override protected void blockingHandle(I2PSocket socket) { try { - // give them 15 seconds to send in the request - socket.setReadTimeout(15*1000); - InputStream in = socket.getInputStream(); - String modifiedRegistration = filterRegistration(in, cloakDest(socket.getPeerDestination())); - socket.setReadTimeout(readTimeout); + String modifiedRegistration; + if(!this.method.equals("webirc")) { + // give them 15 seconds to send in the request + socket.setReadTimeout(15*1000); + InputStream in = socket.getInputStream(); + modifiedRegistration = filterRegistration(in, cloakDest(socket.getPeerDestination())); + socket.setReadTimeout(readTimeout); + } else { + StringBuffer buf = new StringBuffer("WEBIRC "); + buf.append(this.webircPassword); + buf.append(" cgiirc "); + buf.append(cloakDest(socket.getPeerDestination())); + buf.append(' '); + buf.append(this.webircSpoofIP); + buf.append("\r\n"); + modifiedRegistration = buf.toString(); + } Socket s = new Socket(remoteHost, remotePort); new I2PTunnelRunner(s, socket, slock, null, modifiedRegistration.getBytes(), null); } catch (SocketException ex) { @@ -185,4 +221,7 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable { private byte[] cloakKey; // 32 bytes of stuff to scramble the dest with private String hostname; + private String method; + private String webircPassword; + private String webircSpoofIP; } From 2f9364db2b404760ca24d76cb80644796472f84f Mon Sep 17 00:00:00 2001 From: dev Date: Sun, 23 May 2010 17:04:37 +0000 Subject: [PATCH 2/2] fixed a major bug in the datagram dissector, improved performance a little bit and added a utility method to get the already calculated hash of the payload --- .../client/datagram/I2PDatagramDissector.java | 72 ++++++++++++------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java b/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java index d456f9ab5..cb16f2ab4 100644 --- a/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java +++ b/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java @@ -14,7 +14,9 @@ import java.io.IOException; import net.i2p.crypto.DSAEngine; import net.i2p.crypto.SHA256Generator; import net.i2p.data.DataFormatException; +import net.i2p.data.DataHelper; import net.i2p.data.Destination; +import net.i2p.data.Hash; import net.i2p.data.Signature; import net.i2p.util.Log; @@ -33,7 +35,7 @@ public final class I2PDatagramDissector { private DSAEngine dsaEng = DSAEngine.getInstance(); private SHA256Generator hashGen = SHA256Generator.getInstance(); - private byte[] rxHashBytes = null; + private Hash rxHash = null; private Signature rxSign = new Signature(); @@ -62,21 +64,27 @@ public final class I2PDatagramDissector { ByteArrayInputStream dgStream = new ByteArrayInputStream(dgram); byte[] rxTrimmedPayload; + // set invalid(very important!) + this.valid = false; + try { + // read destination rxDest.readBytes(dgStream); + // read signature rxSign.readBytes(dgStream); + // read payload rxPayloadLen = dgStream.read(rxPayload); - - // FIXME: hashGen.calculateHash(source, offset, len) would rock... - rxTrimmedPayload = new byte[rxPayloadLen]; - System.arraycopy(rxPayload, 0, rxTrimmedPayload, 0, rxPayloadLen); - - rxHashBytes =hashGen.calculateHash(rxTrimmedPayload).toByteArray(); + + // calculate the hash of the payload + this.rxHash = hashGen.calculateHash(rxPayload, 0, rxPayloadLen); + assert this.hashGen.calculateHash(this.extractPayload()).equals(this.rxHash); } catch (IOException e) { _log.error("Caught IOException - INCONSISTENT STATE!", e); - } + } catch(AssertionError e) { + _log.error("Assertion failed!", e); + } //_log.debug("Datagram payload size: " + rxPayloadLen + "; content:\n" // + HexDump.dump(rxPayload, 0, rxPayloadLen)); @@ -93,10 +101,7 @@ public final class I2PDatagramDissector { public byte[] getPayload() throws I2PInvalidDatagramException { this.verifySignature(); - byte[] retPayload = new byte[rxPayloadLen]; - System.arraycopy(rxPayload, 0, retPayload, 0, rxPayloadLen); - - return retPayload; + return this.extractPayload(); } /** @@ -110,17 +115,23 @@ public final class I2PDatagramDissector { public Destination getSender() throws I2PInvalidDatagramException { this.verifySignature(); - Destination retDest = new Destination(); - try { - retDest.fromByteArray(rxDest.toByteArray()); - } catch (DataFormatException e) { - _log.error("Caught DataFormatException", e); - return null; - } - - return retDest; + return this.extractSender(); } + /** + * Extract the hash of the payload of an I2P repliable datagram (previously + * loaded with the loadI2PDatagram() method), verifying the datagram + * signature. + * @return The hash of the payload of the I2P repliable datagram + * @throws I2PInvalidDatagramException if the signature verification fails + */ + public Hash getHash() throws I2PInvalidDatagramException { + // make sure it has a valid signature + this.verifySignature(); + + return this.extractHash(); + } + /** * Extract the payload carried by an I2P repliable datagram (previously * loaded with the loadI2PDatagram() method), without verifying the @@ -129,8 +140,8 @@ public final class I2PDatagramDissector { * @return A byte array containing the datagram payload */ public byte[] extractPayload() { - byte[] retPayload = new byte[rxPayloadLen]; - System.arraycopy(rxPayload, 0, retPayload, 0, rxPayloadLen); + byte[] retPayload = new byte[this.rxPayloadLen]; + System.arraycopy(this.rxPayload, 0, retPayload, 0, this.rxPayloadLen); return retPayload; } @@ -144,7 +155,7 @@ public final class I2PDatagramDissector { public Destination extractSender() { Destination retDest = new Destination(); try { - retDest.fromByteArray(rxDest.toByteArray()); + retDest.fromByteArray(this.rxDest.toByteArray()); } catch (DataFormatException e) { _log.error("Caught DataFormatException", e); return null; @@ -152,10 +163,21 @@ public final class I2PDatagramDissector { return retDest; } + + /** + * Extract the hash of the payload of an I2P repliable datagram (previously + * loaded with the loadI2PDatagram() method), without verifying the datagram + * signature. + * @return The hash of the payload of the I2P repliable datagram + */ + public Hash extractHash() { + return this.rxHash; + } /** * Verify the signature of this datagram (previously loaded with the * loadI2PDatagram() method) + * @throws I2PInvalidDatagramException if the signature is invalid */ public void verifySignature() throws I2PInvalidDatagramException { // first check if it already got validated @@ -163,7 +185,7 @@ public final class I2PDatagramDissector { return; // now validate - if (!dsaEng.verifySignature(rxSign, rxHashBytes, rxDest.getSigningPublicKey())) + if (!this.dsaEng.verifySignature(rxSign, rxHash.getData(), rxDest.getSigningPublicKey())) throw new I2PInvalidDatagramException("Incorrect I2P repliable datagram signature"); // set validated