diff --git a/router/java/src/net/i2p/data/i2np/DatabaseStoreMessage.java b/router/java/src/net/i2p/data/i2np/DatabaseStoreMessage.java index 7862960e6..aedba8613 100644 --- a/router/java/src/net/i2p/data/i2np/DatabaseStoreMessage.java +++ b/router/java/src/net/i2p/data/i2np/DatabaseStoreMessage.java @@ -144,7 +144,8 @@ public class DatabaseStoreMessage extends I2NPMessageImpl { byte compressed[] = new byte[compressedSize]; int read = DataHelper.read(in, compressed); if (read != compressedSize) - throw new I2NPMessageException("Invalid compressed data size"); + throw new I2NPMessageException("Invalid compressed data size (expected " + + compressedSize + " read " + read + ")"); ByteArrayInputStream bais = new ByteArrayInputStream(DataHelper.decompress(compressed)); _info.readBytes(bais); } else { diff --git a/router/java/src/net/i2p/data/i2np/GarlicClove.java b/router/java/src/net/i2p/data/i2np/GarlicClove.java index 36bc88ca2..32d2463d6 100644 --- a/router/java/src/net/i2p/data/i2np/GarlicClove.java +++ b/router/java/src/net/i2p/data/i2np/GarlicClove.java @@ -34,25 +34,8 @@ public class GarlicClove extends DataStructureImpl { private long _cloveId; private Date _expiration; private Certificate _certificate; - private int _replyAction; - private SourceRouteBlock _sourceRouteBlock; private I2NPMessageHandler _handler; - /** No action requested with the source route block */ - public final static int ACTION_NONE = 0; - /** - * A DeliveryStatusMessage is requested with the source route block using - * the cloveId as the id received - * - */ - public final static int ACTION_STATUS = 1; - /** - * No DeliveryStatusMessage is requested, but the source route block is - * included for message specific replies - * - */ - public final static int ACTION_MESSAGE_SPECIFIC = 2; - public GarlicClove(RouterContext context) { _context = context; _log = context.logManager().getLog(GarlicClove.class); @@ -62,8 +45,6 @@ public class GarlicClove extends DataStructureImpl { setCloveId(-1); setExpiration(null); setCertificate(null); - setSourceRouteBlockAction(ACTION_NONE); - setSourceRouteBlock(null); } public DeliveryInstructions getInstructions() { return _instructions; } @@ -76,10 +57,6 @@ public class GarlicClove extends DataStructureImpl { public void setExpiration(Date exp) { _expiration = exp; } public Certificate getCertificate() { return _certificate; } public void setCertificate(Certificate cert) { _certificate = cert; } - public int getSourceRouteBlockAction() { return _replyAction; } - public void setSourceRouteBlockAction(int action) { _replyAction = action; } - public SourceRouteBlock getSourceRouteBlock() { return _sourceRouteBlock; } - public void setSourceRouteBlock(SourceRouteBlock block) { _sourceRouteBlock = block; } public void readBytes(InputStream in) throws DataFormatException, IOException { _instructions = new DeliveryInstructions(); @@ -99,12 +76,6 @@ public class GarlicClove extends DataStructureImpl { _certificate.readBytes(in); if (_log.shouldLog(Log.DEBUG)) _log.debug("Read cert: " + _certificate); - int replyStyle = (int)DataHelper.readLong(in, 1); - setSourceRouteBlockAction(replyStyle); - if (replyStyle != ACTION_NONE) { - _sourceRouteBlock = new SourceRouteBlock(); - _sourceRouteBlock.readBytes(in); - } } public void writeBytes(OutputStream out) throws DataFormatException, IOException { @@ -119,28 +90,23 @@ public class GarlicClove extends DataStructureImpl { error.append("Expiration is null "); if (_certificate == null) error.append("Certificate is null "); - if (_replyAction < 0) - error.append("Reply action is < 0 [").append(_replyAction).append("] ");; - if (error.length() > 0) - throw new DataFormatException(error.toString()); - if ( (_replyAction != 0) && (_sourceRouteBlock == null) ) - throw new DataFormatException("Source route block must be specified for non-null action"); - _instructions.writeBytes(out); - - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Wrote instructions: " + _instructions); - _msg.writeBytes(out); - DataHelper.writeLong(out, 4, _cloveId); - DataHelper.writeDate(out, _expiration); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("CloveID written: " + _cloveId + " expiration written: " - + _expiration); - _certificate.writeBytes(out); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Written cert: " + _certificate); - DataHelper.writeLong(out, 1, _replyAction); - if ( (_replyAction != 0) && (_sourceRouteBlock != null) ) - _sourceRouteBlock.writeBytes(out); + + if (error.length() > 0) + throw new DataFormatException(error.toString()); + + _instructions.writeBytes(out); + + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Wrote instructions: " + _instructions); + _msg.writeBytes(out); + DataHelper.writeLong(out, 4, _cloveId); + DataHelper.writeDate(out, _expiration); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("CloveID written: " + _cloveId + " expiration written: " + + _expiration); + _certificate.writeBytes(out); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Written cert: " + _certificate); } public boolean equals(Object obj) { @@ -148,22 +114,18 @@ public class GarlicClove extends DataStructureImpl { return false; GarlicClove clove = (GarlicClove)obj; return DataHelper.eq(getCertificate(), clove.getCertificate()) && - DataHelper.eq(getCloveId(), clove.getCloveId()) && - DataHelper.eq(getData(), clove.getData()) && - DataHelper.eq(getExpiration(), clove.getExpiration()) && - DataHelper.eq(getInstructions(), clove.getInstructions()) && - DataHelper.eq(getSourceRouteBlock(), clove.getSourceRouteBlock()) && - (getSourceRouteBlockAction() == clove.getSourceRouteBlockAction()); + DataHelper.eq(getCloveId(), clove.getCloveId()) && + DataHelper.eq(getData(), clove.getData()) && + DataHelper.eq(getExpiration(), clove.getExpiration()) && + DataHelper.eq(getInstructions(), clove.getInstructions()); } public int hashCode() { return DataHelper.hashCode(getCertificate()) + - (int)getCloveId() + - DataHelper.hashCode(getData()) + - DataHelper.hashCode(getExpiration()) + - DataHelper.hashCode(getInstructions()) + - DataHelper.hashCode(getSourceRouteBlock()) + - getSourceRouteBlockAction(); + (int)getCloveId() + + DataHelper.hashCode(getData()) + + DataHelper.hashCode(getExpiration()) + + DataHelper.hashCode(getInstructions()); } public String toString() { @@ -173,8 +135,6 @@ public class GarlicClove extends DataStructureImpl { buf.append("\n\tCertificate: ").append(getCertificate()); buf.append("\n\tClove ID: ").append(getCloveId()); buf.append("\n\tExpiration: ").append(getExpiration()); - buf.append("\n\tSource route style: ").append(getSourceRouteBlockAction()); - buf.append("\n\tSource route block: ").append(getSourceRouteBlock()); buf.append("\n\tData: ").append(getData()); buf.append("]"); return buf.toString(); diff --git a/router/java/src/net/i2p/data/i2np/I2NPMessageHandler.java b/router/java/src/net/i2p/data/i2np/I2NPMessageHandler.java index 12395382b..c9ea316f8 100644 --- a/router/java/src/net/i2p/data/i2np/I2NPMessageHandler.java +++ b/router/java/src/net/i2p/data/i2np/I2NPMessageHandler.java @@ -73,8 +73,6 @@ public class I2NPMessageHandler { return new TunnelMessage(_context); case DataMessage.MESSAGE_TYPE: return new DataMessage(_context); - case SourceRouteReplyMessage.MESSAGE_TYPE: - return new SourceRouteReplyMessage(_context); case TunnelCreateMessage.MESSAGE_TYPE: return new TunnelCreateMessage(_context); case TunnelCreateStatusMessage.MESSAGE_TYPE: diff --git a/router/java/src/net/i2p/data/i2np/SourceRouteBlock.java b/router/java/src/net/i2p/data/i2np/SourceRouteBlock.java deleted file mode 100644 index 2d8c61ca9..000000000 --- a/router/java/src/net/i2p/data/i2np/SourceRouteBlock.java +++ /dev/null @@ -1,228 +0,0 @@ -package net.i2p.data.i2np; -/* - * free (adj.): unencumbered; not under the control of others - * Written by jrandom in 2003 and released into the public domain - * with no warranty of any kind, either expressed or implied. - * It probably won't make your computer catch on fire, or eat - * your children, but it might. Use at your own risk. - * - */ - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Date; - -import net.i2p.I2PAppContext; -import net.i2p.data.Certificate; -import net.i2p.data.DataFormatException; -import net.i2p.data.DataHelper; -import net.i2p.data.DataStructureImpl; -import net.i2p.data.Hash; -import net.i2p.data.PublicKey; -import net.i2p.data.SessionKey; -import net.i2p.data.SessionTag; -import net.i2p.util.Log; - - -/** - * Defines a single hop of a source routed message, as usable for building a - * SourceRouteReplyMessage - * - * @author jrandom - */ -public class SourceRouteBlock extends DataStructureImpl { - private final static Log _log = new Log(SourceRouteBlock.class); - private Hash _router; - private byte[] _data; - private SessionKey _key; - private byte[] _tag; - private DeliveryInstructions _decryptedInstructions; - private long _decryptedMessageId; - private Certificate _decryptedCertificate; - private long _decryptedExpiration; - - public SourceRouteBlock() { - setRouter(null); - setData(null); - setKey(null); - setTag((byte[])null); - _decryptedInstructions = null; - _decryptedMessageId = -1; - _decryptedCertificate = null; - _decryptedExpiration = -1; - } - - /** - * Get the router through which replies using this source route block must - * be sent (as the getData() is encrypted for their eyes only) - * - */ - public Hash getRouter() { return _router; } - public void setRouter(Hash router) { _router= router; } - /** - * Get the encrypted header. After decryption (via ElGamal+AES as defined - * in the data structures spec), this array contains: - * DeliveryInstructions - * 4 byte Integer for a message ID - * Certificate - * Date of expiration for replies - * - */ - public byte[] getData() { return _data; } - private void setData(byte data[]) { _data = data; } - /** - * Retrieve the session key which may be used in conjunction with the tag - * to encrypt a garlic message and send it as a reply to this message. - * The encryption would follow scenario 2 of the ElGamal+AES encryption method - * defined in the data structures spec. - * - */ - public SessionKey getKey() { return _key; } - public void setKey(SessionKey key) { _key = key; } - /** - * Get the tag made available for use in conjunction with the getKey() to - * ElGamal+AES encrypt a garlic message without knowing the public key to - * which the message is destined - * - */ - public byte[] getTag() { return _tag; } - public void setTag(SessionTag tag) { setTag(tag.getData()); } - public void setTag(byte tag[]) { - if ( (tag != null) && (tag.length != SessionTag.BYTE_LENGTH) ) - throw new IllegalArgumentException("Tag must be either null or 32 bytes"); - _tag = tag; - } - - /** - * After decryptData, this contains the delivery instructions for this block - */ - public DeliveryInstructions getDecryptedInstructions() { return _decryptedInstructions; } - /** - * After decryptData, this contains the message ID to be used with this block - */ - public long getDecryptedMessageId() { return _decryptedMessageId; } - /** - * After decryptData, this contains the Certificate 'paying' for the forwarding according to - * this block - */ - public Certificate getDecryptedCertificate() { return _decryptedCertificate; } - /** - * After decryptData, this contains the date after which this block should not be forwarded - */ - public long getDecryptedExpiration() { return _decryptedExpiration; } - - /** - * Set the raw data with the formatted and encrypted options specified - * - * @param instructions Where a message bearing this block should be sent - * @param messageId ID of the message for this block (not repeatable) - * @param expiration date after which this block expires - * @param replyThrough Encryption key of the router to whom this block is specified (not - * the router specified in the delivery instructions!) - * - * @throws DataFormatException if the data is invalid or could not be encrypted - */ - public void setData(I2PAppContext ctx, DeliveryInstructions instructions, - long messageId, Certificate cert, long expiration, - PublicKey replyThrough) throws DataFormatException { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(64); - - _decryptedInstructions = instructions; - _decryptedMessageId = messageId; - _decryptedCertificate = cert; - _decryptedExpiration = expiration; - - instructions.writeBytes(baos); - DataHelper.writeLong(baos, 4, messageId); - cert.writeBytes(baos); - DataHelper.writeDate(baos, new Date(expiration)); - - int paddedSize = 256; - SessionKey sessKey = null; - SessionTag tag = null; - if (instructions.getDelayRequested()) { - // always use a new key if we're delaying, since the reply block may not be used within the - // window of a session - sessKey = ctx.keyGenerator().generateSessionKey(); - tag = null; - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Delay requested - creating a new session key"); - } else { - sessKey = ctx.sessionKeyManager().getCurrentKey(replyThrough); - if (sessKey == null) { - sessKey = ctx.keyGenerator().generateSessionKey(); - tag = null; - if (_log.shouldLog(Log.DEBUG)) - _log.debug("No delay requested, but no session key is known"); - } else { - tag = ctx.sessionKeyManager().consumeNextAvailableTag(replyThrough, sessKey); - } - } - byte encData[] = ctx.elGamalAESEngine().encrypt(baos.toByteArray(), replyThrough, - sessKey, null, tag, paddedSize); - setData(encData); - } catch (IOException ioe) { - throw new DataFormatException("Error writing out the source route block data", ioe); - } catch (DataFormatException dfe) { - throw new DataFormatException("Error writing out the source route block data", dfe); - } - } - - - public void readBytes(InputStream in) throws DataFormatException, IOException { - _router = new Hash(); - _router.readBytes(in); - int size = (int)DataHelper.readLong(in, 2); - _data = new byte[size]; - int read = read(in, _data); - if (read != _data.length) - throw new DataFormatException("Incorrect # of bytes read for source route block: " + read); - _key = new SessionKey(); - _key.readBytes(in); - _tag = new byte[32]; - read = read(in, _tag); - if (read != _tag.length) - throw new DataFormatException("Incorrect # of bytes read for session tag: " + read); - } - - public void writeBytes(OutputStream out) throws DataFormatException, IOException { - if ( (_router == null) || (_data == null) || (_key == null) || (_tag == null) || (_tag.length != 32) ) - throw new DataFormatException("Insufficient data to write"); - _router.writeBytes(out); - DataHelper.writeLong(out, 2, _data.length); - out.write(_data); - _key.writeBytes(out); - out.write(_tag); - } - - public boolean equals(Object obj) { - if ( (obj == null) || !(obj instanceof SourceRouteBlock)) - return false; - SourceRouteBlock block = (SourceRouteBlock)obj; - return DataHelper.eq(getRouter(), block.getRouter()) && - DataHelper.eq(getData(), block.getData()) && - DataHelper.eq(getKey(), block.getKey()) && - DataHelper.eq(getTag(), block.getTag()); - } - - public int hashCode() { - return DataHelper.hashCode(getRouter()) + - DataHelper.hashCode(getData()) + - DataHelper.hashCode(getKey()) + - DataHelper.hashCode(getTag()); - } - - public String toString() { - StringBuffer buf = new StringBuffer(128); - buf.append("[SourceRouteBlock: "); - buf.append("\n\tRouter: ").append(getRouter()); - buf.append("\n\tData: ").append(DataHelper.toString(getData(), getData().length)); - buf.append("\n\tTag: ").append(DataHelper.toString(getTag(), (getTag() != null ? getTag().length : 0))); - buf.append("\n\tKey: ").append(getKey()); - buf.append("]"); - return buf.toString(); - } -} diff --git a/router/java/src/net/i2p/data/i2np/SourceRouteReplyMessage.java b/router/java/src/net/i2p/data/i2np/SourceRouteReplyMessage.java deleted file mode 100644 index ccc167da6..000000000 --- a/router/java/src/net/i2p/data/i2np/SourceRouteReplyMessage.java +++ /dev/null @@ -1,164 +0,0 @@ -package net.i2p.data.i2np; -/* - * free (adj.): unencumbered; not under the control of others - * Written by jrandom in 2003 and released into the public domain - * with no warranty of any kind, either expressed or implied. - * It probably won't make your computer catch on fire, or eat - * your children, but it might. Use at your own risk. - * - */ - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; - -import net.i2p.I2PAppContext; -import net.i2p.data.Certificate; -import net.i2p.data.DataFormatException; -import net.i2p.data.DataHelper; -import net.i2p.data.PrivateKey; -import net.i2p.util.Log; - -/** - * Defines a message directed by a source route block to deliver a message to an - * unknown location. - * - * @author jrandom - */ -public class SourceRouteReplyMessage extends I2NPMessageImpl { - private final static Log _log = new Log(SourceRouteReplyMessage.class); - public final static int MESSAGE_TYPE = 13; - private byte _encryptedHeader[]; - private I2NPMessage _message; - private DeliveryInstructions _decryptedInstructions; - private long _decryptedMessageId; - private Certificate _decryptedCertificate; - private long _decryptedExpiration; - private I2NPMessageHandler _handler; - - public SourceRouteReplyMessage(I2PAppContext context) { - super(context); - _handler = new I2NPMessageHandler(context); - _encryptedHeader = null; - _message = null; - _decryptedInstructions = null; - _decryptedMessageId = -1; - _decryptedCertificate = null; - _decryptedExpiration = -1; - } - - /** - * Retrieve the message being sent as a reply - */ - public I2NPMessage getMessage() { return _message; } - public void setMessage(I2NPMessage message) { _message = message; } - - public void setEncryptedHeader(byte header[]) { _encryptedHeader = header; } - - /** - * After decryptHeader, this contains the delivery instructions for this block - */ - public DeliveryInstructions getDecryptedInstructions() { return _decryptedInstructions; } - /** - * After decryptHeader, this contains the message ID to be used with this block - */ - public long getDecryptedMessageId() { return _decryptedMessageId; } - /** - * After decryptHeader, this contains the Certificate 'paying' for the forwarding according to - * this block - */ - public Certificate getDecryptedCertificate() { return _decryptedCertificate; } - /** - * After decryptHeader, this contains the date after which this block should not be forwarded - */ - public long getDecryptedExpiration() { return _decryptedExpiration; } - - /** - * Decrypt the header and store it in the various getDecryptedXYZ() properties - * - * @throws DataFormatException if the decryption fails or if the data is somehow malformed - */ - public void decryptHeader(PrivateKey key) throws DataFormatException { - if ( (_encryptedHeader == null) || (_encryptedHeader.length <= 0) ) - throw new DataFormatException("No header to decrypt"); - - byte decr[] = _context.elGamalAESEngine().decrypt(_encryptedHeader, key); - - if (decr == null) - throw new DataFormatException("Decrypted data is null"); - - try { - ByteArrayInputStream bais = new ByteArrayInputStream(decr); - - _decryptedInstructions = new DeliveryInstructions(); - _decryptedInstructions.readBytes(bais); - _decryptedMessageId = DataHelper.readLong(bais, 4); - _decryptedCertificate = new Certificate(); - _decryptedCertificate.readBytes(bais); - _decryptedExpiration = DataHelper.readDate(bais).getTime(); - - } catch (IOException ioe) { - throw new DataFormatException("Error reading the source route reply header", ioe); - } catch (DataFormatException dfe) { - throw new DataFormatException("Error reading the source route reply header", dfe); - } - } - - public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException { - if (type != MESSAGE_TYPE) - throw new I2NPMessageException("Message type is incorrect for this message"); - try { - int headerSize = (int)DataHelper.readLong(in, 2); - _encryptedHeader = new byte[headerSize]; - int read = read(in, _encryptedHeader); - if (read != headerSize) - throw new DataFormatException("Not enough bytes to read the header (read = " + read - + ", required = " + headerSize + ")"); - _message = _handler.readMessage(in); - } catch (DataFormatException dfe) { - throw new I2NPMessageException("Unable to load the message data", dfe); - } - } - - protected byte[] writeMessage() throws I2NPMessageException, IOException { - if ( (_encryptedHeader == null) || (_message == null) ) - throw new I2NPMessageException("Not enough data to write out"); - - ByteArrayOutputStream os = new ByteArrayOutputStream(1024); - try { - DataHelper.writeLong(os, 2, _encryptedHeader.length); - os.write(_encryptedHeader); - _message.writeBytes(os); - } catch (DataFormatException dfe) { - throw new I2NPMessageException("Error writing out the message data", dfe); - } - return os.toByteArray(); - } - - public int getType() { return MESSAGE_TYPE; } - - public int hashCode() { - return DataHelper.hashCode(_encryptedHeader) + - DataHelper.hashCode(_message); - } - - public boolean equals(Object object) { - if ( (object != null) && (object instanceof SourceRouteReplyMessage) ) { - SourceRouteReplyMessage msg = (SourceRouteReplyMessage)object; - return DataHelper.eq(_message,msg._message) && - DataHelper.eq(_encryptedHeader,msg._encryptedHeader); - } else { - return false; - } - } - - public String toString() { - StringBuffer buf = new StringBuffer(); - buf.append("[SourceRouteReplyMessage: "); - buf.append("\n\tHeader: ").append(DataHelper.toString(_encryptedHeader, 64)); - buf.append("\n\tMessage: ").append(_message); - buf.append("]"); - return buf.toString(); - } -} diff --git a/router/java/src/net/i2p/data/i2np/TunnelCreateMessage.java b/router/java/src/net/i2p/data/i2np/TunnelCreateMessage.java index b58e67cf9..179c028f5 100644 --- a/router/java/src/net/i2p/data/i2np/TunnelCreateMessage.java +++ b/router/java/src/net/i2p/data/i2np/TunnelCreateMessage.java @@ -18,6 +18,8 @@ import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; import net.i2p.data.Hash; import net.i2p.data.TunnelId; +import net.i2p.data.SessionKey; +import net.i2p.data.SessionTag; import net.i2p.util.Log; /** @@ -30,8 +32,9 @@ public class TunnelCreateMessage extends I2NPMessageImpl { private final static Log _log = new Log(TunnelCreateMessage.class); public final static int MESSAGE_TYPE = 6; private int _participantType; - private Hash _nextRouter; private TunnelId _tunnelId; + private Hash _nextRouter; + private TunnelId _nextTunnelId; private long _tunnelDuration; private TunnelConfigurationSessionKey _configKey; private long _maxPeakMessagesPerMin; @@ -44,7 +47,10 @@ public class TunnelCreateMessage extends I2NPMessageImpl { private TunnelSigningPrivateKey _verificationPrivKey; private TunnelSessionKey _tunnelKey; private Certificate _certificate; - private SourceRouteBlock _replyBlock; + private SessionTag _replyTag; + private SessionKey _replyKey; + private TunnelId _replyTunnel; + private Hash _replyPeer; public static final int PARTICIPANT_TYPE_GATEWAY = 1; public static final int PARTICIPANT_TYPE_ENDPOINT = 2; @@ -57,6 +63,7 @@ public class TunnelCreateMessage extends I2NPMessageImpl { super(context); setParticipantType(-1); setNextRouter(null); + setNextTunnelId(null); setTunnelId(null); setTunnelDurationSeconds(-1); setConfigurationKey(null); @@ -70,13 +77,18 @@ public class TunnelCreateMessage extends I2NPMessageImpl { setVerificationPrivateKey(null); setTunnelKey(null); setCertificate(null); - setReplyBlock(null); + setReplyTag(null); + setReplyKey(null); + setReplyTunnel(null); + setReplyPeer(null); } public void setParticipantType(int type) { _participantType = type; } public int getParticipantType() { return _participantType; } public void setNextRouter(Hash routerIdentityHash) { _nextRouter = routerIdentityHash; } public Hash getNextRouter() { return _nextRouter; } + public void setNextTunnelId(TunnelId id) { _nextTunnelId = id; } + public TunnelId getNextTunnelId() { return _nextTunnelId; } public void setTunnelId(TunnelId id) { _tunnelId = id; } public TunnelId getTunnelId() { return _tunnelId; } public void setTunnelDurationSeconds(long durationSeconds) { _tunnelDuration = durationSeconds; } @@ -103,8 +115,14 @@ public class TunnelCreateMessage extends I2NPMessageImpl { public TunnelSessionKey getTunnelKey() { return _tunnelKey; } public void setCertificate(Certificate cert) { _certificate = cert; } public Certificate getCertificate() { return _certificate; } - public void setReplyBlock(SourceRouteBlock block) { _replyBlock = block; } - public SourceRouteBlock getReplyBlock() { return _replyBlock; } + public void setReplyTag(SessionTag tag) { _replyTag = tag; } + public SessionTag getReplyTag() { return _replyTag; } + public void setReplyKey(SessionKey key) { _replyKey = key; } + public SessionKey getReplyKey() { return _replyKey; } + public void setReplyTunnel(TunnelId id) { _replyTunnel = id; } + public TunnelId getReplyTunnel() { return _replyTunnel; } + public void setReplyPeer(Hash peer) { _replyPeer = peer; } + public Hash getReplyPeer() { return _replyPeer; } public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException { if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message"); @@ -113,6 +131,8 @@ public class TunnelCreateMessage extends I2NPMessageImpl { if (_participantType != PARTICIPANT_TYPE_ENDPOINT) { _nextRouter = new Hash(); _nextRouter.readBytes(in); + _nextTunnelId = new TunnelId(); + _nextTunnelId.readBytes(in); } _tunnelId = new TunnelId(); _tunnelId.readBytes(in); @@ -140,8 +160,14 @@ public class TunnelCreateMessage extends I2NPMessageImpl { } _certificate = new Certificate(); _certificate.readBytes(in); - _replyBlock = new SourceRouteBlock(); - _replyBlock.readBytes(in); + _replyTag = new SessionTag(); + _replyTag.readBytes(in); + _replyKey = new SessionKey(); + _replyKey.readBytes(in); + _replyTunnel = new TunnelId(); + _replyTunnel.readBytes(in); + _replyPeer = new Hash(); + _replyPeer.readBytes(in); } catch (DataFormatException dfe) { throw new I2NPMessageException("Unable to load the message data", dfe); } @@ -153,6 +179,7 @@ public class TunnelCreateMessage extends I2NPMessageImpl { DataHelper.writeLong(os, 1, _participantType); if (_participantType != PARTICIPANT_TYPE_ENDPOINT) { _nextRouter.writeBytes(os); + _nextTunnelId.writeBytes(os); } _tunnelId.writeBytes(os); DataHelper.writeLong(os, 4, _tunnelDuration); @@ -174,7 +201,10 @@ public class TunnelCreateMessage extends I2NPMessageImpl { _tunnelKey.writeBytes(os); } _certificate.writeBytes(os); - _replyBlock.writeBytes(os); + _replyTag.writeBytes(os); + _replyKey.writeBytes(os); + _replyTunnel.writeBytes(os); + _replyPeer.writeBytes(os); } catch (Throwable t) { throw new I2NPMessageException("Error writing out the message data", t); } @@ -201,21 +231,23 @@ public class TunnelCreateMessage extends I2NPMessageImpl { public int hashCode() { return (int)(DataHelper.hashCode(getCertificate()) + - DataHelper.hashCode(getConfigurationKey()) + - DataHelper.hashCode(getNextRouter()) + - DataHelper.hashCode(getReplyBlock()) + - DataHelper.hashCode(getTunnelId()) + - DataHelper.hashCode(getTunnelKey()) + - DataHelper.hashCode(getVerificationPrivateKey()) + - DataHelper.hashCode(getVerificationPublicKey()) + - (getIncludeDummyTraffic() ? 1 : 0) + - getMaxAvgBytesPerMin() + - getMaxAvgMessagesPerMin() + - getMaxPeakBytesPerMin() + - getMaxPeakMessagesPerMin() + - getParticipantType() + - (getReorderMessages() ? 1 : 0) + - getTunnelDurationSeconds()); + DataHelper.hashCode(getConfigurationKey()) + + DataHelper.hashCode(getNextRouter()) + + DataHelper.hashCode(getNextTunnelId()) + + DataHelper.hashCode(getReplyPeer()) + + DataHelper.hashCode(getReplyTunnel()) + + DataHelper.hashCode(getTunnelId()) + + DataHelper.hashCode(getTunnelKey()) + + DataHelper.hashCode(getVerificationPrivateKey()) + + DataHelper.hashCode(getVerificationPublicKey()) + + (getIncludeDummyTraffic() ? 1 : 0) + + getMaxAvgBytesPerMin() + + getMaxAvgMessagesPerMin() + + getMaxPeakBytesPerMin() + + getMaxPeakMessagesPerMin() + + getParticipantType() + + (getReorderMessages() ? 1 : 0) + + getTunnelDurationSeconds()); } public boolean equals(Object object) { @@ -224,7 +256,11 @@ public class TunnelCreateMessage extends I2NPMessageImpl { return DataHelper.eq(getCertificate(), msg.getCertificate()) && DataHelper.eq(getConfigurationKey(), msg.getConfigurationKey()) && DataHelper.eq(getNextRouter(), msg.getNextRouter()) && - DataHelper.eq(getReplyBlock(), msg.getReplyBlock()) && + DataHelper.eq(getNextTunnelId(), msg.getNextTunnelId()) && + DataHelper.eq(getReplyTag(), msg.getReplyTag()) && + DataHelper.eq(getReplyKey(), msg.getReplyKey()) && + DataHelper.eq(getReplyTunnel(), msg.getReplyTunnel()) && + DataHelper.eq(getReplyPeer(), msg.getReplyPeer()) && DataHelper.eq(getTunnelId(), msg.getTunnelId()) && DataHelper.eq(getTunnelKey(), msg.getTunnelKey()) && DataHelper.eq(getVerificationPrivateKey(), msg.getVerificationPrivateKey()) && @@ -249,7 +285,11 @@ public class TunnelCreateMessage extends I2NPMessageImpl { buf.append("\n\tCertificate: ").append(getCertificate()); buf.append("\n\tConfiguration Key: ").append(getConfigurationKey()); buf.append("\n\tNext Router: ").append(getNextRouter()); - buf.append("\n\tReply Block: ").append(getReplyBlock()); + buf.append("\n\tNext Tunnel: ").append(getNextTunnelId()); + buf.append("\n\tReply Tag: ").append(getReplyTag()); + buf.append("\n\tReply Key: ").append(getReplyKey()); + buf.append("\n\tReply Tunnel: ").append(getReplyTunnel()); + buf.append("\n\tReply Peer: ").append(getReplyPeer()); buf.append("\n\tTunnel ID: ").append(getTunnelId()); buf.append("\n\tTunnel Key: ").append(getTunnelKey()); buf.append("\n\tVerification Private Key: ").append(getVerificationPrivateKey()); diff --git a/router/java/src/net/i2p/router/HandlerJobBuilder.java b/router/java/src/net/i2p/router/HandlerJobBuilder.java index 2119c2058..c1c9832cd 100644 --- a/router/java/src/net/i2p/router/HandlerJobBuilder.java +++ b/router/java/src/net/i2p/router/HandlerJobBuilder.java @@ -11,7 +11,6 @@ package net.i2p.router; import net.i2p.data.Hash; import net.i2p.data.RouterIdentity; import net.i2p.data.i2np.I2NPMessage; -import net.i2p.data.i2np.SourceRouteBlock; /** * Defines a class that builds jobs to handle a particular message - these @@ -27,9 +26,8 @@ public interface HandlerJobBuilder { * @param receivedMessage I2NP message received * @param from router that sent the message (if available) * @param fromHash hash of the routerIdentity of the router that sent the message (if available) - * @param replyBlock block with which a reply could be sent (if available) * @return a job or null if no particular job is appropriate (in which case, * the message should go into the inbound message pool) */ - public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock); + public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash); } diff --git a/router/java/src/net/i2p/router/InNetMessage.java b/router/java/src/net/i2p/router/InNetMessage.java index c9faa6555..cd02548a7 100644 --- a/router/java/src/net/i2p/router/InNetMessage.java +++ b/router/java/src/net/i2p/router/InNetMessage.java @@ -11,7 +11,6 @@ package net.i2p.router; import net.i2p.data.Hash; import net.i2p.data.RouterIdentity; import net.i2p.data.i2np.I2NPMessage; -import net.i2p.data.i2np.SourceRouteBlock; /** * Wrap an I2NP message received from the network prior to handling and processing. @@ -22,7 +21,6 @@ public class InNetMessage { private I2NPMessage _message; private RouterIdentity _fromRouter; private Hash _fromRouterHash; - private SourceRouteBlock _replyBlock; private long _created; public InNetMessage(RouterContext context) { @@ -30,7 +28,6 @@ public class InNetMessage { setMessage(null); setFromRouter(null); setFromRouterHash(null); - setReplyBlock(null); context.messageStateMonitor().inboundMessageAdded(); _created = context.clock().now(); _context.statManager().createRateStat("inNetMessage.timeToDiscard", @@ -59,15 +56,6 @@ public class InNetMessage { public RouterIdentity getFromRouter() { return _fromRouter; } public void setFromRouter(RouterIdentity router) { _fromRouter = router; } - /** - * Retrieve any source route block supplied with this message for replies - * - * @return source route block, or null if it was not supplied /or/ if it was already - * used in an ack - */ - public SourceRouteBlock getReplyBlock() { return _replyBlock; } - public void setReplyBlock(SourceRouteBlock block) { _replyBlock = block; } - /** * Call this after we're done dealing with this message (when we no * longer need its data) diff --git a/router/java/src/net/i2p/router/InNetMessagePool.java b/router/java/src/net/i2p/router/InNetMessagePool.java index 2919e1196..07965d0af 100644 --- a/router/java/src/net/i2p/router/InNetMessagePool.java +++ b/router/java/src/net/i2p/router/InNetMessagePool.java @@ -88,7 +88,7 @@ public class InNetMessagePool { if (builder != null) { Job job = builder.createJob(messageBody, msg.getFromRouter(), - msg.getFromRouterHash(), msg.getReplyBlock()); + msg.getFromRouterHash()); if (job != null) { _context.jobQueue().addJob(job); synchronized (_messages) { diff --git a/router/java/src/net/i2p/router/JobQueue.java b/router/java/src/net/i2p/router/JobQueue.java index 10fa1153f..f11b6170a 100644 --- a/router/java/src/net/i2p/router/JobQueue.java +++ b/router/java/src/net/i2p/router/JobQueue.java @@ -19,7 +19,6 @@ import java.util.Iterator; import java.util.SortedMap; import java.util.TreeMap; -import net.i2p.router.message.HandleSourceRouteReplyMessageJob; import net.i2p.router.networkdb.HandleDatabaseLookupMessageJob; import net.i2p.router.tunnelmanager.HandleTunnelCreateMessageJob; import net.i2p.router.tunnelmanager.RequestTunnelJob; @@ -221,14 +220,6 @@ public class JobQueue { if (!_allowParallelOperation) return false; // dont drop during startup [duh] Class cls = job.getClass(); if (numReady > _maxWaitingJobs) { - - // heavy cpu load, plus we're allowed to be unreliable with these two - // [but garlics can contain our payloads, so lets not drop them] - //if (cls == HandleGarlicMessageJob.class) - // return true; - if (cls == HandleSourceRouteReplyMessageJob.class) - return true; - // lets not try to drop too many tunnel messages... //if (cls == HandleTunnelMessageJob.class) // return true; diff --git a/router/java/src/net/i2p/router/MessageHistory.java b/router/java/src/net/i2p/router/MessageHistory.java index a38d4b61e..b9afc866d 100644 --- a/router/java/src/net/i2p/router/MessageHistory.java +++ b/router/java/src/net/i2p/router/MessageHistory.java @@ -128,11 +128,10 @@ public class MessageHistory { * @param outTunnel tunnel we are sending this request out * @param peerRequested peer asked to participate in the tunnel * @param nextPeer who peerRequested should forward messages to (or null if it is the endpoint) - * @param sourceRoutePeer to whom peerRequested should forward its TunnelCreateStatusMessage through * @param replyTunnel the tunnel sourceRoutePeer should forward the source routed message to * @param replyThrough the gateway of the tunnel that the sourceRoutePeer will be sending to */ - public void requestTunnelCreate(TunnelId createTunnel, TunnelId outTunnel, Hash peerRequested, Hash nextPeer, Hash sourceRoutePeer, TunnelId replyTunnel, Hash replyThrough) { + public void requestTunnelCreate(TunnelId createTunnel, TunnelId outTunnel, Hash peerRequested, Hash nextPeer, TunnelId replyTunnel, Hash replyThrough) { if (!_doLog) return; StringBuffer buf = new StringBuffer(128); buf.append(getPrefix()); @@ -142,8 +141,6 @@ public class MessageHistory { buf.append("(next [").append(getName(nextPeer)).append("]) "); if (outTunnel != null) buf.append("via [").append(outTunnel.getTunnelId()).append("] "); - if (sourceRoutePeer != null) - buf.append("with replies routed through [").append(getName(sourceRoutePeer)).append("] "); if ( (replyTunnel != null) && (replyThrough != null) ) buf.append("who forwards it through [").append(replyTunnel.getTunnelId()).append("] on [").append(getName(replyThrough)).append("]"); addEntry(buf.toString()); @@ -258,15 +255,13 @@ public class MessageHistory { * of a timeout or an explicit refusal). * */ - public void tunnelRequestTimedOut(Hash peer, TunnelId tunnel, Hash replyThrough) { + public void tunnelRequestTimedOut(Hash peer, TunnelId tunnel) { if (!_doLog) return; if ( (tunnel == null) || (peer == null) ) return; StringBuffer buf = new StringBuffer(128); buf.append(getPrefix()); buf.append("tunnel [").append(tunnel.getTunnelId()).append("] timed out on ["); buf.append(getName(peer)).append("]"); - if (replyThrough != null) - buf.append(" with their reply intended to come through [").append(getName(replyThrough)).append("]"); addEntry(buf.toString()); } diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java index 56f3c3b19..ec1d92469 100644 --- a/router/java/src/net/i2p/router/Router.java +++ b/router/java/src/net/i2p/router/Router.java @@ -28,10 +28,8 @@ import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; import net.i2p.data.RouterInfo; import net.i2p.data.i2np.GarlicMessage; -import net.i2p.data.i2np.SourceRouteReplyMessage; import net.i2p.data.i2np.TunnelMessage; import net.i2p.router.message.GarlicMessageHandler; -import net.i2p.router.message.SourceRouteReplyMessageHandler; import net.i2p.router.message.TunnelMessageHandler; import net.i2p.router.startup.StartupJob; import net.i2p.stat.Rate; @@ -218,7 +216,6 @@ public class Router { private void setupHandlers() { _context.inNetMessagePool().registerHandlerJobBuilder(GarlicMessage.MESSAGE_TYPE, new GarlicMessageHandler(_context)); _context.inNetMessagePool().registerHandlerJobBuilder(TunnelMessage.MESSAGE_TYPE, new TunnelMessageHandler(_context)); - _context.inNetMessagePool().registerHandlerJobBuilder(SourceRouteReplyMessage.MESSAGE_TYPE, new SourceRouteReplyMessageHandler(_context)); } public void renderStatusHTML(OutputStream out) throws IOException { diff --git a/router/java/src/net/i2p/router/message/GarlicMessageBuilder.java b/router/java/src/net/i2p/router/message/GarlicMessageBuilder.java index 33fce5cd4..be16c1a8e 100644 --- a/router/java/src/net/i2p/router/message/GarlicMessageBuilder.java +++ b/router/java/src/net/i2p/router/message/GarlicMessageBuilder.java @@ -22,7 +22,6 @@ import net.i2p.data.SessionTag; import net.i2p.data.i2np.GarlicClove; import net.i2p.data.i2np.GarlicMessage; import net.i2p.data.i2np.I2NPMessage; -import net.i2p.data.i2np.SourceRouteBlock; import net.i2p.router.RouterContext; import net.i2p.util.Log; @@ -36,9 +35,6 @@ public class GarlicMessageBuilder { } public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags) { Log log = ctx.logManager().getLog(GarlicMessageBuilder.class); - if (config == null) - throw new IllegalArgumentException("Null config specified"); - PublicKey key = config.getRecipientPublicKey(); if (key == null) { if (config.getRecipient() == null) { @@ -50,20 +46,16 @@ public class GarlicMessageBuilder { } else key = config.getRecipient().getIdentity().getPublicKey(); } - GarlicMessage msg = new GarlicMessage(ctx); - - noteWrap(ctx, msg, config); if (log.shouldLog(Log.INFO)) log.info("Encrypted with public key " + key + " to expire on " + new Date(config.getExpiration())); - byte cloveSet[] = buildCloveSet(ctx, config); - SessionKey curKey = ctx.sessionKeyManager().getCurrentKey(key); if (curKey == null) curKey = ctx.sessionKeyManager().createSession(key); - wrappedKey.setData(curKey.getData()); + SessionTag curTag = ctx.sessionKeyManager().consumeNextAvailableTag(key, curKey); + int availTags = ctx.sessionKeyManager().getAvailableTags(key, curKey); if (log.shouldLog(Log.DEBUG)) log.debug("Available tags for encryption to " + key + ": " + availTags); @@ -83,8 +75,34 @@ public class GarlicMessageBuilder { // always tack on at least one more - not necessary. //wrappedTags.add(new SessionTag(true)); } - SessionTag curTag = ctx.sessionKeyManager().consumeNextAvailableTag(key, curKey); - byte encData[] = ctx.elGamalAESEngine().encrypt(cloveSet, key, curKey, wrappedTags, curTag, 256); + + wrappedKey.setData(curKey.getData()); + + return buildMessage(ctx, config, wrappedKey, wrappedTags, key, curKey, curTag); + } + + /** + * @param ctx scope + * @param config how/what to wrap + * @param wrappedKey output parameter that will be filled with the sessionKey used + * @param wrappedTags output parameter that will be filled with the sessionTags used + * @param target public key of the location being garlic routed to (may be null if we + * know the encryptKey and encryptTag) + * @param encryptKey sessionKey used to encrypt the current message + * @param encryptTag sessionTag used to encrypt the current message + */ + public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags, PublicKey target, SessionKey encryptKey, SessionTag encryptTag) { + Log log = ctx.logManager().getLog(GarlicMessageBuilder.class); + if (config == null) + throw new IllegalArgumentException("Null config specified"); + + GarlicMessage msg = new GarlicMessage(ctx); + + noteWrap(ctx, msg, config); + + byte cloveSet[] = buildCloveSet(ctx, config); + + byte encData[] = ctx.elGamalAESEngine().encrypt(cloveSet, target, encryptKey, wrappedTags, encryptTag, 128); msg.setData(encData); Date exp = new Date(config.getExpiration()); msg.setMessageExpiration(exp); @@ -168,46 +186,8 @@ public class GarlicMessageBuilder { clove.setCloveId(config.getId()); clove.setExpiration(new Date(config.getExpiration())); clove.setInstructions(config.getDeliveryInstructions()); - specifySourceRouteBlock(ctx, clove, config); ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); clove.writeBytes(baos); return baos.toByteArray(); } - - private static void specifySourceRouteBlock(RouterContext ctx, GarlicClove clove, GarlicConfig config) throws DataFormatException { - Log log = ctx.logManager().getLog(GarlicMessageBuilder.class); - boolean includeBlock = false; - if (config.getRequestAck()) { - clove.setSourceRouteBlockAction(GarlicClove.ACTION_STATUS); - includeBlock = true; - } else if (config.getReplyInstructions() != null) { - clove.setSourceRouteBlockAction(GarlicClove.ACTION_MESSAGE_SPECIFIC); - includeBlock = true; - } else { - clove.setSourceRouteBlockAction(GarlicClove.ACTION_NONE); - } - - if (includeBlock) { - log.debug("Specifying source route block"); - - SessionKey replySessionKey = ctx.keyGenerator().generateSessionKey(); - SessionTag tag = new SessionTag(true); - - // make it so we'll read the session tag correctly and use the right session key - HashSet tags = new HashSet(1); - tags.add(tag); - ctx.sessionKeyManager().tagsReceived(replySessionKey, tags); - - SourceRouteBlock block = new SourceRouteBlock(); - PublicKey pk = config.getReplyThroughRouter().getIdentity().getPublicKey(); - block.setData(ctx, config.getReplyInstructions(), config.getReplyBlockMessageId(), - config.getReplyBlockCertificate(), config.getReplyBlockExpiration(), pk); - block.setRouter(config.getReplyThroughRouter().getIdentity().getHash()); - block.setKey(replySessionKey); - block.setTag(tag); - clove.setSourceRouteBlock(block); - } else { - clove.setSourceRouteBlock(null); - } - } } diff --git a/router/java/src/net/i2p/router/message/GarlicMessageHandler.java b/router/java/src/net/i2p/router/message/GarlicMessageHandler.java index 3274e2315..1db82eddb 100644 --- a/router/java/src/net/i2p/router/message/GarlicMessageHandler.java +++ b/router/java/src/net/i2p/router/message/GarlicMessageHandler.java @@ -12,7 +12,6 @@ import net.i2p.data.Hash; import net.i2p.data.RouterIdentity; import net.i2p.data.i2np.GarlicMessage; import net.i2p.data.i2np.I2NPMessage; -import net.i2p.data.i2np.SourceRouteBlock; import net.i2p.router.HandlerJobBuilder; import net.i2p.router.Job; import net.i2p.router.RouterContext; @@ -28,8 +27,7 @@ public class GarlicMessageHandler implements HandlerJobBuilder { _context = context; } - public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) { - // ignore the reply block for the moment + public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) { HandleGarlicMessageJob job = new HandleGarlicMessageJob(_context, (GarlicMessage)receivedMessage, from, fromHash); return job; } diff --git a/router/java/src/net/i2p/router/message/HandleGarlicMessageJob.java b/router/java/src/net/i2p/router/message/HandleGarlicMessageJob.java index 7931febec..0b5ee85fe 100644 --- a/router/java/src/net/i2p/router/message/HandleGarlicMessageJob.java +++ b/router/java/src/net/i2p/router/message/HandleGarlicMessageJob.java @@ -165,10 +165,8 @@ public class HandleGarlicMessageJob extends JobImpl { _log.warn("Invalid clove " + clove); return; } - boolean requestAck = (clove.getSourceRouteBlockAction() == GarlicClove.ACTION_STATUS); long sendExpiration = clove.getExpiration().getTime(); _handler.handleMessage(clove.getInstructions(), clove.getData(), - requestAck, clove.getSourceRouteBlock(), clove.getCloveId(), _from, _fromHash, sendExpiration, FORWARD_PRIORITY); } diff --git a/router/java/src/net/i2p/router/message/HandleSourceRouteReplyMessageJob.java b/router/java/src/net/i2p/router/message/HandleSourceRouteReplyMessageJob.java deleted file mode 100644 index 7e18f3151..000000000 --- a/router/java/src/net/i2p/router/message/HandleSourceRouteReplyMessageJob.java +++ /dev/null @@ -1,156 +0,0 @@ -package net.i2p.router.message; -/* - * free (adj.): unencumbered; not under the control of others - * Written by jrandom in 2003 and released into the public domain - * with no warranty of any kind, either expressed or implied. - * It probably won't make your computer catch on fire, or eat - * your children, but it might. Use at your own risk. - * - */ - -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import net.i2p.data.DataFormatException; -import net.i2p.data.Hash; -import net.i2p.data.RouterIdentity; -import net.i2p.data.i2np.DeliveryInstructions; -import net.i2p.data.i2np.SourceRouteReplyMessage; -import net.i2p.router.JobImpl; -import net.i2p.router.Router; -import net.i2p.router.RouterContext; -import net.i2p.util.Log; - -/** - * Handle a source route reply - decrypt the instructions and forward the message - * accordingly - * - */ -public class HandleSourceRouteReplyMessageJob extends JobImpl { - private Log _log; - private SourceRouteReplyMessage _message; - private RouterIdentity _from; - private Hash _fromHash; - private Map _seenMessages; // Long msgId --> Date seen - private MessageHandler _handler; - - public final static int PRIORITY = 150; - - public HandleSourceRouteReplyMessageJob(RouterContext context, SourceRouteReplyMessage msg, RouterIdentity from, Hash fromHash) { - super(context); - _log = getContext().logManager().getLog(HandleSourceRouteReplyMessageJob.class); - _message = msg; - _from = from; - _fromHash = fromHash; - _seenMessages = new HashMap(); - _handler = new MessageHandler(context); - } - - public String getName() { return "Handle Source Route Reply Message"; } - public void runJob() { - try { - long before = getContext().clock().now(); - _message.decryptHeader(getContext().keyManager().getPrivateKey()); - long after = getContext().clock().now(); - if ( (after-before) > 1000) { - if (_log.shouldLog(Log.WARN)) - _log.warn("Took more than a second (" + (after-before) - + ") to decrypt the sourceRoute header"); - } else { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Took LESS than a second (" + (after-before) - + ") to decrypt the sourceRoute header"); - } - } catch (DataFormatException dfe) { - if (_log.shouldLog(Log.ERROR)) - _log.error("Error decrypting the source route message's header (message " - + _message.getUniqueId() + ")", dfe); - if (_log.shouldLog(Log.WARN)) - _log.warn("Message header could not be decrypted: " + _message, getAddedBy()); - getContext().messageHistory().messageProcessingError(_message.getUniqueId(), - _message.getClass().getName(), - "Source route message header could not be decrypted"); - return; - } - - if (!isValid()) { - if (_log.shouldLog(Log.ERROR)) - _log.error("Error validating source route message, dropping: " + _message); - return; - } - - DeliveryInstructions instructions = _message.getDecryptedInstructions(); - - long now = getContext().clock().now(); - long expiration = _message.getDecryptedExpiration(); - // if its expiring really soon, jack the expiration 30 seconds - if (expiration < now+10*1000) - expiration = now + 60*1000; - - boolean requestAck = false; - _handler.handleMessage(instructions, _message.getMessage(), requestAck, null, - _message.getDecryptedMessageId(), _from, _fromHash, expiration, PRIORITY); - } - - private boolean isValid() { - long now = getContext().clock().now(); - if (_message.getDecryptedExpiration() < now) { - if (_message.getDecryptedExpiration() < now + Router.CLOCK_FUDGE_FACTOR) { - _log.info("Expired message received, but within our fudge factor"); - } else { - _log.error("Source route reply message expired. Replay attack? msgId = " - + _message.getDecryptedMessageId() + " expiration = " - + new Date(_message.getDecryptedExpiration())); - return false; - } - } - if (!isValidMessageId(_message.getDecryptedMessageId(), _message.getDecryptedExpiration())) { - _log.error("Source route reply message already received! Replay attack? msgId = " - + _message.getDecryptedMessageId() + " expiration = " - + new Date(_message.getDecryptedExpiration())); - return false; - } - return true; - } - - private boolean isValidMessageId(long msgId, long expiration) { - synchronized (_seenMessages) { - if (_seenMessages.containsKey(new Long(msgId))) - return false; - - _seenMessages.put(new Long(msgId), new Date(expiration)); - } - // essentially random - if ((msgId % 10) == 0) { - cleanupMessages(); - } - return true; - } - - private void cleanupMessages() { - // this should be in its own thread perhaps, or job? and maybe _seenMessages should be - // synced to disk? - List toRemove = new ArrayList(32); - long now = getContext().clock().now()-Router.CLOCK_FUDGE_FACTOR; - synchronized (_seenMessages) { - for (Iterator iter = _seenMessages.keySet().iterator(); iter.hasNext();) { - Long id = (Long)iter.next(); - Date exp = (Date)_seenMessages.get(id); - if (now > exp.getTime()) - toRemove.add(id); - } - for (int i = 0; i < toRemove.size(); i++) - _seenMessages.remove(toRemove.get(i)); - } - } - - public void dropped() { - getContext().messageHistory().messageProcessingError(_message.getUniqueId(), - _message.getClass().getName(), - "Dropped due to overload"); - } -} diff --git a/router/java/src/net/i2p/router/message/MessageHandler.java b/router/java/src/net/i2p/router/message/MessageHandler.java index 605e38b86..6798119d3 100644 --- a/router/java/src/net/i2p/router/message/MessageHandler.java +++ b/router/java/src/net/i2p/router/message/MessageHandler.java @@ -17,7 +17,6 @@ import net.i2p.data.TunnelId; import net.i2p.data.i2np.DataMessage; import net.i2p.data.i2np.DeliveryInstructions; import net.i2p.data.i2np.I2NPMessage; -import net.i2p.data.i2np.SourceRouteBlock; import net.i2p.data.i2np.TunnelMessage; import net.i2p.router.ClientMessage; import net.i2p.router.InNetMessage; @@ -41,7 +40,6 @@ class MessageHandler { } public void handleMessage(DeliveryInstructions instructions, I2NPMessage message, - boolean requestAck, SourceRouteBlock replyBlock, long replyId, RouterIdentity from, Hash fromHash, long expiration, int priority) { switch (instructions.getDeliveryMode()) { @@ -50,7 +48,7 @@ class MessageHandler { if (message.getType() == DataMessage.MESSAGE_TYPE) { handleLocalDestination(instructions, message, fromHash); } else { - handleLocalRouter(message, from, fromHash, replyBlock, requestAck); + handleLocalRouter(message, from, fromHash); } break; case DeliveryInstructions.DELIVERY_MODE_ROUTER: @@ -58,7 +56,7 @@ class MessageHandler { _log.debug("Instructions for ROUTER DELIVERY to " + instructions.getRouter().toBase64()); if (_context.routerHash().equals(instructions.getRouter())) { - handleLocalRouter(message, from, fromHash, replyBlock, requestAck); + handleLocalRouter(message, from, fromHash); } else { handleRemoteRouter(message, instructions, expiration, priority); } @@ -84,28 +82,14 @@ class MessageHandler { _log.error("Message has instructions that are not yet implemented: mode = " + instructions.getDeliveryMode()); } - if (requestAck) { - _log.debug("SEND ACK REQUESTED"); - sendAck(replyBlock, replyId); - } else { - _log.debug("No ack requested"); - } } - - private void sendAck(SourceRouteBlock replyBlock, long replyId) { - _log.info("Queueing up ack job via reply block " + replyBlock); - Job ackJob = new SendMessageAckJob(_context, replyBlock, replyId); - _context.jobQueue().addJob(ackJob); - } - - private void handleLocalRouter(I2NPMessage message, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock, boolean ackUsed) { + + private void handleLocalRouter(I2NPMessage message, RouterIdentity from, Hash fromHash) { _log.info("Handle " + message.getClass().getName() + " to a local router - toss it on the inbound network pool"); InNetMessage msg = new InNetMessage(_context); msg.setFromRouter(from); msg.setFromRouterHash(fromHash); msg.setMessage(message); - if (!ackUsed) - msg.setReplyBlock(replyBlock); _context.inNetMessagePool().add(msg); } diff --git a/router/java/src/net/i2p/router/message/SendMessageAckJob.java b/router/java/src/net/i2p/router/message/SendMessageAckJob.java deleted file mode 100644 index e122dda93..000000000 --- a/router/java/src/net/i2p/router/message/SendMessageAckJob.java +++ /dev/null @@ -1,58 +0,0 @@ -package net.i2p.router.message; -/* - * free (adj.): unencumbered; not under the control of others - * Written by jrandom in 2003 and released into the public domain - * with no warranty of any kind, either expressed or implied. - * It probably won't make your computer catch on fire, or eat - * your children, but it might. Use at your own risk. - * - */ - -import java.util.Date; - -import net.i2p.data.i2np.DeliveryStatusMessage; -import net.i2p.data.i2np.I2NPMessage; -import net.i2p.data.i2np.SourceRouteBlock; -import net.i2p.router.JobImpl; -import net.i2p.router.RouterContext; - -/** - * Send a DeliveryStatusMessage to the location specified in the source route block - * acknowledging the ackId given. This uses the simplest technique (don't garlic, and - * send direct to where the SourceRouteBlock requested), but it could instead garlic it - * and send it via a tunnel or garlic route it additionally) - * - */ -public class SendMessageAckJob extends JobImpl { - private SourceRouteBlock _block; - private long _ackId; - - public final static int ACK_PRIORITY = 100; - - public SendMessageAckJob(RouterContext ctx, SourceRouteBlock block, long ackId) { - super(ctx); - _block = block; - _ackId = ackId; - } - - public void runJob() { - getContext().jobQueue().addJob(new SendReplyMessageJob(getContext(), _block, createAckMessage(), ACK_PRIORITY)); - } - - /** - * Create whatever should be delivered to the intermediary hop so that - * a DeliveryStatusMessage gets to the intended recipient. - * - * Currently this doesn't garlic encrypt the DeliveryStatusMessage with - * the block's tag and sessionKey, but it could. - * - */ - protected I2NPMessage createAckMessage() { - DeliveryStatusMessage statusMessage = new DeliveryStatusMessage(getContext()); - statusMessage.setArrival(new Date(getContext().clock().now())); - statusMessage.setMessageId(_ackId); - return statusMessage; - } - - public String getName() { return "Send Message Ack"; } -} diff --git a/router/java/src/net/i2p/router/message/SendReplyMessageJob.java b/router/java/src/net/i2p/router/message/SendReplyMessageJob.java deleted file mode 100644 index 77d05e769..000000000 --- a/router/java/src/net/i2p/router/message/SendReplyMessageJob.java +++ /dev/null @@ -1,65 +0,0 @@ -package net.i2p.router.message; -/* - * free (adj.): unencumbered; not under the control of others - * Written by jrandom in 2003 and released into the public domain - * with no warranty of any kind, either expressed or implied. - * It probably won't make your computer catch on fire, or eat - * your children, but it might. Use at your own risk. - * - */ - -import net.i2p.data.i2np.I2NPMessage; -import net.i2p.data.i2np.SourceRouteBlock; -import net.i2p.data.i2np.SourceRouteReplyMessage; -import net.i2p.router.JobImpl; -import net.i2p.router.RouterContext; -import net.i2p.util.Log; - -/** - * Send a SourceRouteReplyMessage to the location specified in the source route block. - * This uses the simplest technique (don't garlic, and send direct to where the - * SourceRouteBlock requested), but it could instead garlic it and send it via a - * tunnel or garlic route it additionally) - * - */ -public class SendReplyMessageJob extends JobImpl { - private Log _log; - private SourceRouteBlock _block; - private I2NPMessage _message; - private int _priority; - - public SendReplyMessageJob(RouterContext context, SourceRouteBlock block, I2NPMessage message, int priority) { - super(context); - _log = context.logManager().getLog(SendReplyMessageJob.class); - _block = block; - _message = message; - _priority = priority; - } - - public void runJob() { - SourceRouteReplyMessage msg = new SourceRouteReplyMessage(getContext()); - msg.setMessage(_message); - msg.setEncryptedHeader(_block.getData()); - msg.setMessageExpiration(_message.getMessageExpiration()); - - send(msg); - } - - /** - * Send the message on its way.

- * - * This could garlic route the message to the _block.getRouter, or it could - * send it there via a tunnel, or it could just send it direct.

- * - * For simplicity, its currently going direct. - * - */ - protected void send(I2NPMessage msg) { - _log.info("Sending reply with " + _message.getClass().getName() + " in a sourceRouteeplyMessage to " + _block.getRouter().toBase64()); - int timeout = (int)(msg.getMessageExpiration().getTime()-getContext().clock().now()); - SendMessageDirectJob j = new SendMessageDirectJob(getContext(), msg, _block.getRouter(), timeout, _priority); - getContext().jobQueue().addJob(j); - } - - public String getName() { return "Send Reply Message"; } -} diff --git a/router/java/src/net/i2p/router/message/SendTunnelMessageJob.java b/router/java/src/net/i2p/router/message/SendTunnelMessageJob.java index 30a6f39fd..0cac9a056 100644 --- a/router/java/src/net/i2p/router/message/SendTunnelMessageJob.java +++ b/router/java/src/net/i2p/router/message/SendTunnelMessageJob.java @@ -488,7 +488,6 @@ public class SendTunnelMessageJob extends JobImpl { msg.setFromRouter(ident); msg.setFromRouterHash(ident.getHash()); msg.setMessage(_message); - msg.setReplyBlock(null); getContext().inNetMessagePool().add(msg); } else { if (_log.shouldLog(Log.DEBUG)) diff --git a/router/java/src/net/i2p/router/message/SourceRouteReplyMessageHandler.java b/router/java/src/net/i2p/router/message/SourceRouteReplyMessageHandler.java deleted file mode 100644 index 286215d17..000000000 --- a/router/java/src/net/i2p/router/message/SourceRouteReplyMessageHandler.java +++ /dev/null @@ -1,36 +0,0 @@ -package net.i2p.router.message; -/* - * free (adj.): unencumbered; not under the control of others - * Written by jrandom in 2003 and released into the public domain - * with no warranty of any kind, either expressed or implied. - * It probably won't make your computer catch on fire, or eat - * your children, but it might. Use at your own risk. - * - */ - -import net.i2p.data.Hash; -import net.i2p.data.RouterIdentity; -import net.i2p.data.i2np.I2NPMessage; -import net.i2p.data.i2np.SourceRouteBlock; -import net.i2p.data.i2np.SourceRouteReplyMessage; -import net.i2p.router.HandlerJobBuilder; -import net.i2p.router.Job; -import net.i2p.router.RouterContext; - -/** - * HandlerJobBuilder to build jobs to handle SourceRouteReplyMessages - * - */ -public class SourceRouteReplyMessageHandler implements HandlerJobBuilder { - private RouterContext _context; - public SourceRouteReplyMessageHandler(RouterContext context) { - _context = context; - } - - public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) { - // ignore the replyBlock for now - HandleSourceRouteReplyMessageJob job = new HandleSourceRouteReplyMessageJob(_context, (SourceRouteReplyMessage)receivedMessage, from, fromHash); - return job; - } - -} diff --git a/router/java/src/net/i2p/router/message/TunnelMessageHandler.java b/router/java/src/net/i2p/router/message/TunnelMessageHandler.java index 352828365..81351b48e 100644 --- a/router/java/src/net/i2p/router/message/TunnelMessageHandler.java +++ b/router/java/src/net/i2p/router/message/TunnelMessageHandler.java @@ -11,7 +11,6 @@ package net.i2p.router.message; import net.i2p.data.Hash; import net.i2p.data.RouterIdentity; import net.i2p.data.i2np.I2NPMessage; -import net.i2p.data.i2np.SourceRouteBlock; import net.i2p.data.i2np.TunnelMessage; import net.i2p.router.HandlerJobBuilder; import net.i2p.router.Job; @@ -27,8 +26,7 @@ public class TunnelMessageHandler implements HandlerJobBuilder { public TunnelMessageHandler(RouterContext context) { _context = context; } - public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) { - // ignore the replyBlock for now + public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) { HandleTunnelMessageJob job = new HandleTunnelMessageJob(_context, (TunnelMessage)receivedMessage, from, fromHash); return job; } diff --git a/router/java/src/net/i2p/router/networkdb/DatabaseLookupMessageHandler.java b/router/java/src/net/i2p/router/networkdb/DatabaseLookupMessageHandler.java index 52dee9a6e..8f103c5ab 100644 --- a/router/java/src/net/i2p/router/networkdb/DatabaseLookupMessageHandler.java +++ b/router/java/src/net/i2p/router/networkdb/DatabaseLookupMessageHandler.java @@ -12,7 +12,6 @@ import net.i2p.data.Hash; import net.i2p.data.RouterIdentity; import net.i2p.data.i2np.DatabaseLookupMessage; import net.i2p.data.i2np.I2NPMessage; -import net.i2p.data.i2np.SourceRouteBlock; import net.i2p.router.HandlerJobBuilder; import net.i2p.router.Job; import net.i2p.router.RouterContext; @@ -32,7 +31,7 @@ public class DatabaseLookupMessageHandler implements HandlerJobBuilder { _context.statManager().createRateStat("netDb.lookupsDropped", "How many netDb lookups did we drop due to throttling?", "Network Database", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); } - public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) { + public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) { _context.statManager().addRateData("netDb.lookupsReceived", 1, 0); if (_context.throttle().acceptNetDbLookupRequest(((DatabaseLookupMessage)receivedMessage).getSearchKey())) { diff --git a/router/java/src/net/i2p/router/networkdb/DatabaseStoreMessageHandler.java b/router/java/src/net/i2p/router/networkdb/DatabaseStoreMessageHandler.java index d1d311631..d64a2ec9a 100644 --- a/router/java/src/net/i2p/router/networkdb/DatabaseStoreMessageHandler.java +++ b/router/java/src/net/i2p/router/networkdb/DatabaseStoreMessageHandler.java @@ -12,7 +12,6 @@ import net.i2p.data.Hash; import net.i2p.data.RouterIdentity; import net.i2p.data.i2np.DatabaseStoreMessage; import net.i2p.data.i2np.I2NPMessage; -import net.i2p.data.i2np.SourceRouteBlock; import net.i2p.router.HandlerJobBuilder; import net.i2p.router.Job; import net.i2p.router.RouterContext; @@ -26,8 +25,7 @@ public class DatabaseStoreMessageHandler implements HandlerJobBuilder { public DatabaseStoreMessageHandler(RouterContext context) { _context = context; } - public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) { - // ignore the reply block for the moment + public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) { return new HandleDatabaseStoreMessageJob(_context, (DatabaseStoreMessage)receivedMessage, from, fromHash); } } diff --git a/router/java/src/net/i2p/router/tunnelmanager/HandleTunnelCreateMessageJob.java b/router/java/src/net/i2p/router/tunnelmanager/HandleTunnelCreateMessageJob.java index f86c150f2..ca7ee53e1 100644 --- a/router/java/src/net/i2p/router/tunnelmanager/HandleTunnelCreateMessageJob.java +++ b/router/java/src/net/i2p/router/tunnelmanager/HandleTunnelCreateMessageJob.java @@ -9,20 +9,32 @@ package net.i2p.router.tunnelmanager; */ import java.util.Date; +import java.util.List; +import net.i2p.data.Certificate; import net.i2p.data.Hash; import net.i2p.data.RouterIdentity; import net.i2p.data.RouterInfo; import net.i2p.data.TunnelId; -import net.i2p.data.i2np.SourceRouteBlock; import net.i2p.data.i2np.TunnelCreateMessage; +import net.i2p.data.i2np.DeliveryInstructions; +import net.i2p.data.i2np.GarlicClove; +import net.i2p.data.i2np.GarlicMessage; +import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.TunnelCreateStatusMessage; +import net.i2p.router.Job; import net.i2p.router.JobImpl; +import net.i2p.router.MessageSelector; +import net.i2p.router.ReplyJob; import net.i2p.router.RouterContext; import net.i2p.router.TunnelInfo; import net.i2p.router.TunnelSettings; +import net.i2p.router.TunnelSelectionCriteria; import net.i2p.router.message.BuildTestMessageJob; -import net.i2p.router.message.SendReplyMessageJob; +import net.i2p.router.message.GarlicConfig; +import net.i2p.router.message.GarlicMessageBuilder; +import net.i2p.router.message.PayloadGarlicConfig; +import net.i2p.router.message.SendTunnelMessageJob; import net.i2p.util.Log; public class HandleTunnelCreateMessageJob extends JobImpl { @@ -30,20 +42,18 @@ public class HandleTunnelCreateMessageJob extends JobImpl { private TunnelCreateMessage _message; private RouterIdentity _from; private Hash _fromHash; - private SourceRouteBlock _replyBlock; private final static long TIMEOUT = 30*1000; // 30 secs to contact a peer that will be our next hop private final static int PRIORITY = 123; HandleTunnelCreateMessageJob(RouterContext ctx, TunnelCreateMessage receivedMessage, - RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) { + RouterIdentity from, Hash fromHash) { super(ctx); _log = ctx.logManager().getLog(HandleTunnelCreateMessageJob.class); ctx.statManager().createRateStat("tunnel.rejectOverloaded", "How many tunnels did we deny due to throttling?", "Tunnels", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); _message = receivedMessage; _from = from; _fromHash = fromHash; - _replyBlock = replyBlock; } public void runJob() { @@ -119,15 +129,16 @@ public class HandleTunnelCreateMessageJob extends JobImpl { } } - + private static final long REPLY_TIMEOUT = 10*1000; private void sendReply(boolean ok) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Sending reply to a tunnel create of id " + _message.getTunnelId() - + " with ok (" + ok + ") to router " + _message.getReplyBlock().getRouter().toBase64()); + + " with ok (" + ok + ") to tunnel " + _message.getReplyTunnel() + + " on router " + _message.getReplyPeer()); getContext().messageHistory().receiveTunnelCreate(_message.getTunnelId(), _message.getNextRouter(), - new Date(getContext().clock().now() + 1000*_message.getTunnelDurationSeconds()), - ok, _message.getReplyBlock().getRouter()); + new Date(getContext().clock().now() + 1000*_message.getTunnelDurationSeconds()), + ok, _message.getReplyPeer()); TunnelCreateStatusMessage msg = new TunnelCreateStatusMessage(getContext()); msg.setFromHash(getContext().routerHash()); @@ -139,10 +150,88 @@ public class HandleTunnelCreateMessageJob extends JobImpl { msg.setStatus(TunnelCreateStatusMessage.STATUS_FAILED_OVERLOADED); } msg.setMessageExpiration(new Date(getContext().clock().now()+60*1000)); - SendReplyMessageJob job = new SendReplyMessageJob(getContext(), _message.getReplyBlock(), msg, PRIORITY); + + // put that message into a garlic + GarlicMessage reply = createReply(msg); + + TunnelId outTunnelId = selectReplyTunnel(); + + SendTunnelMessageJob job = new SendTunnelMessageJob(getContext(), reply, outTunnelId, + _message.getReplyPeer(), + _message.getReplyTunnel(), + (Job)null, (ReplyJob)null, + (Job)null, (MessageSelector)null, + REPLY_TIMEOUT, PRIORITY); getContext().jobQueue().addJob(job); } + private GarlicMessage createReply(TunnelCreateStatusMessage body) { + GarlicConfig cfg = createReplyConfig(body); + return GarlicMessageBuilder.buildMessage(getContext(), cfg, null, null, null, + _message.getReplyKey(), _message.getReplyTag()); + } + + private GarlicConfig createReplyConfig(TunnelCreateStatusMessage body) { + GarlicConfig config = new GarlicConfig(); + + PayloadGarlicConfig replyClove = buildReplyClove(body); + config.addClove(replyClove); + + DeliveryInstructions instructions = new DeliveryInstructions(); + instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_LOCAL); + instructions.setDelayRequested(false); + instructions.setDelaySeconds(0); + instructions.setEncrypted(false); + instructions.setEncryptionKey(null); + instructions.setRouter(null); + instructions.setTunnelId(null); + + config.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null)); + config.setDeliveryInstructions(instructions); + config.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE)); + config.setExpiration(REPLY_TIMEOUT+getContext().clock().now()); + config.setRecipient(null); + config.setRequestAck(false); + + return config; + } + + /** + * Build a clove that sends the tunnel create reply + */ + private PayloadGarlicConfig buildReplyClove(TunnelCreateStatusMessage body) { + PayloadGarlicConfig replyClove = new PayloadGarlicConfig(); + + DeliveryInstructions instructions = new DeliveryInstructions(); + instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_LOCAL); + instructions.setRouter(null); + instructions.setDelayRequested(false); + instructions.setDelaySeconds(0); + instructions.setEncrypted(false); + + replyClove.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null)); + replyClove.setDeliveryInstructions(instructions); + replyClove.setExpiration(REPLY_TIMEOUT+getContext().clock().now()); + replyClove.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE)); + replyClove.setPayload(body); + replyClove.setRecipient(null); + replyClove.setRequestAck(false); + + return replyClove; + } + + + private TunnelId selectReplyTunnel() { + TunnelSelectionCriteria crit = new TunnelSelectionCriteria(); + crit.setMinimumTunnelsRequired(1); + crit.setMaximumTunnelsRequired(1); + List ids = getContext().tunnelManager().selectOutboundTunnelIds(crit); + if ( (ids != null) && (ids.size() > 0) ) + return (TunnelId)ids.get(0); + else + return null; + } + public String getName() { return "Handle Tunnel Create Message"; } private class JoinJob extends JobImpl { diff --git a/router/java/src/net/i2p/router/tunnelmanager/RequestTunnelJob.java b/router/java/src/net/i2p/router/tunnelmanager/RequestTunnelJob.java index 5788b3003..c54643e1d 100644 --- a/router/java/src/net/i2p/router/tunnelmanager/RequestTunnelJob.java +++ b/router/java/src/net/i2p/router/tunnelmanager/RequestTunnelJob.java @@ -28,7 +28,6 @@ import net.i2p.data.i2np.DeliveryInstructions; import net.i2p.data.i2np.DeliveryStatusMessage; import net.i2p.data.i2np.GarlicMessage; import net.i2p.data.i2np.I2NPMessage; -import net.i2p.data.i2np.SourceRouteBlock; import net.i2p.data.i2np.TunnelCreateMessage; import net.i2p.data.i2np.TunnelCreateStatusMessage; import net.i2p.router.Job; @@ -162,19 +161,9 @@ public class RequestTunnelJob extends JobImpl { fail(); return; } - - // select reply peer [peer to which SourceRouteReply should be sent, and - // from which the reply will be forwarded to an inbound tunnel] - RouterInfo replyPeer = selectReplyPeer(participant); - if (replyPeer == null) { - if (_log.shouldLog(Log.WARN)) - _log.warn("No reply peers available! unable to request a new tunnel!"); - fail(); - return; - } // select inbound tunnel gateway - TunnelGateway inboundGateway = selectInboundGateway(participant, replyPeer); + TunnelGateway inboundGateway = selectInboundGateway(participant); if (inboundGateway == null) { if (_log.shouldLog(Log.ERROR)) _log.error("Unable to find an inbound gateway"); @@ -187,7 +176,7 @@ public class RequestTunnelJob extends JobImpl { PublicKey wrappedTo = new PublicKey(); RequestState state = new RequestState(wrappedKey, wrappedTags, wrappedTo, - participant, inboundGateway, replyPeer, + participant, inboundGateway, outboundTunnel, target); Request r = new Request(state); getContext().jobQueue().addJob(r); @@ -217,7 +206,6 @@ public class RequestTunnelJob extends JobImpl { _state.getOutboundTunnel(), _state.getParticipant().getThisHop(), _state.getParticipant().getNextHop(), - _state.getReplyPeer().getIdentity().getHash(), _state.getInboundGateway().getTunnelId(), _state.getInboundGateway().getGateway()); } @@ -239,54 +227,45 @@ public class RequestTunnelJob extends JobImpl { private Set _wrappedTags; private PublicKey _wrappedTo; private TunnelCreateMessage _createMsg; - private DeliveryStatusMessage _statusMsg; private GarlicMessage _garlicMessage; private TunnelInfo _participant; private TunnelGateway _inboundGateway; - private RouterInfo _replyPeer; private TunnelId _outboundTunnel; private RouterInfo _target; public RequestState(SessionKey wrappedKey, Set wrappedTags, PublicKey wrappedTo, TunnelInfo participant, TunnelGateway inboundGateway, - RouterInfo replyPeer, TunnelId outboundTunnel, RouterInfo target) { + TunnelId outboundTunnel, RouterInfo target) { _wrappedKey = wrappedKey; _wrappedTags = wrappedTags; _wrappedTo = wrappedTo; _participant = participant; _inboundGateway = inboundGateway; - _replyPeer = replyPeer; _outboundTunnel = outboundTunnel; _target = target; } public TunnelId getOutboundTunnel() { return _outboundTunnel; } public TunnelInfo getParticipant() { return _participant; } - public RouterInfo getReplyPeer() { return _replyPeer; } public TunnelGateway getInboundGateway() { return _inboundGateway; } public boolean doNext() { if (_createMsg == null) { - _createMsg = buildTunnelCreate(_participant, _inboundGateway, _replyPeer); - return true; - } else if (_statusMsg == null) { - _statusMsg = buildDeliveryStatusMessage(); + _createMsg = buildTunnelCreate(_participant, _inboundGateway); return true; } else if (_garlicMessage == null) { - _garlicMessage = buildGarlicMessage(_createMsg, _statusMsg, _replyPeer, - _inboundGateway, _target, _wrappedKey, - _wrappedTags, _wrappedTo); + _garlicMessage = buildGarlicMessage(_createMsg, _inboundGateway, _target, + _wrappedKey, _wrappedTags, _wrappedTo); return true; } else { // send the GarlicMessage if (_log.shouldLog(Log.INFO)) _log.info("Sending tunnel create to " + _target.getIdentity().getHash().toBase64() + - " with replies through " + _replyPeer.getIdentity().getHash().toBase64() + " to inbound gateway " + _inboundGateway.getGateway().toBase64() + " : " + _inboundGateway.getTunnelId().getTunnelId()); ReplyJob onReply = new Success(_participant, _wrappedKey, _wrappedTags, _wrappedTo); - Job onFail = new Failure(_participant, _replyPeer.getIdentity().getHash()); - MessageSelector selector = new Selector(_participant, _statusMsg.getMessageId()); + Job onFail = new Failure(_participant); + MessageSelector selector = new Selector(_participant); SendTunnelMessageJob j = new SendTunnelMessageJob(getContext(), _garlicMessage, _outboundTunnel, _target.getIdentity().getHash(), null, null, onReply, onFail, @@ -330,47 +309,10 @@ public class RequestTunnelJob extends JobImpl { } /** - * Select a peer to which the tunnelParticipant will send the SourceRouteReplyMessage - * containing a garlic wrapped TunnelCreateStatusMessage destined for the local router. - * - * Currently just a random peer - */ - private RouterInfo selectReplyPeer(TunnelInfo tunnelParticipant) { - PeerSelectionCriteria criteria = new PeerSelectionCriteria(); - criteria.setMaximumRequired(1); - criteria.setMinimumRequired(1); - criteria.setPurpose(PeerSelectionCriteria.PURPOSE_SOURCE_ROUTE); - List peerHashes = getContext().peerManager().selectPeers(criteria); - - RouterInfo peerInfo = null; - for (int i = 0; (i < peerHashes.size()) && (peerInfo == null); i++) { - Hash peerHash = (Hash)peerHashes.get(i); - peerInfo = getContext().netDb().lookupRouterInfoLocally(peerHash); - if (peerInfo == null) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Selected a peer [" + peerHash + "] we don't have info on locally... trying another"); - } else { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Peer [" + peerHash.toBase64() + "] is known locally, keep it in the list of replyPeers"); - break; - } - } - - if (peerInfo == null) { - if (_log.shouldLog(Log.WARN)) - _log.warn("No peers know for a reply (out of " + peerHashes.size() + ") - using ourself"); - return getContext().router().getRouterInfo(); - } else { - return peerInfo; - } - } - - /** - * Select an inbound tunnel to receive replies and acks from the participant by means of the - * replyPeer + * Select an inbound tunnel to receive replies and acks from the participant * */ - private TunnelGateway selectInboundGateway(TunnelInfo participant, RouterInfo replyPeer) { + private TunnelGateway selectInboundGateway(TunnelInfo participant) { TunnelSelectionCriteria criteria = new TunnelSelectionCriteria(); criteria.setAnonymityPriority(66); criteria.setReliabilityPriority(66); @@ -408,7 +350,7 @@ public class RequestTunnelJob extends JobImpl { /** * Build a TunnelCreateMessage to the participant */ - private TunnelCreateMessage buildTunnelCreate(TunnelInfo participant, TunnelGateway replyGateway, RouterInfo replyPeer) { + private TunnelCreateMessage buildTunnelCreate(TunnelInfo participant, TunnelGateway replyGateway) { TunnelCreateMessage msg = new TunnelCreateMessage(getContext()); msg.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null)); msg.setConfigurationKey(participant.getConfigurationKey()); @@ -418,6 +360,9 @@ public class RequestTunnelJob extends JobImpl { msg.setMaxPeakBytesPerMin(participant.getSettings().getBytesPerMinutePeak()); msg.setMaxPeakMessagesPerMin(participant.getSettings().getMessagesPerMinutePeak()); msg.setNextRouter(participant.getNextHop()); + // TODO: update the TunnelInfo structure so we can have the tunnel contain + // different tunnelIds per hop + msg.setNextTunnelId(participant.getTunnelId()); if (participant.getNextHop() == null) msg.setParticipantType(TunnelCreateMessage.PARTICIPANT_TYPE_ENDPOINT); else if (participant.getSigningKey() != null) @@ -426,11 +371,19 @@ public class RequestTunnelJob extends JobImpl { msg.setParticipantType(TunnelCreateMessage.PARTICIPANT_TYPE_OTHER); msg.setReorderMessages(participant.getSettings().getReorder()); - SourceRouteBlock replyBlock = buildReplyBlock(replyGateway, replyPeer); - if (replyBlock == null) - return null; - msg.setReplyBlock(replyBlock); + SessionKey replySessionKey = getContext().keyGenerator().generateSessionKey(); + SessionTag tag = new SessionTag(true); + Set tags = new HashSet(); + tags.add(tag); + // make it so we'll read the session tag correctly and use the right session key + getContext().sessionKeyManager().tagsReceived(replySessionKey, tags); + + msg.setReplyPeer(replyGateway.getGateway()); + msg.setReplyTunnel(replyGateway.getTunnelId()); + msg.setReplyKey(replySessionKey); + msg.setReplyTag(tag); + long duration = participant.getSettings().getExpiration() - getContext().clock().now(); if (duration == 0) duration = 1; msg.setTunnelDurationSeconds(duration/1000); @@ -442,97 +395,16 @@ public class RequestTunnelJob extends JobImpl { return msg; } - /** - * Build a source route block directing the reply through the gateway by means of the - * replyPeer - * - */ - private SourceRouteBlock buildReplyBlock(TunnelGateway gateway, RouterInfo replyPeer) { - if (replyPeer == null) { - if (_log.shouldLog(Log.ERROR)) - _log.error("No peer specified for reply!"); - return null; - } - - SessionKey replySessionKey = getContext().keyGenerator().generateSessionKey(); - SessionTag tag = new SessionTag(true); - Set tags = new HashSet(); - tags.add(tag); - // make it so we'll read the session tag correctly and use the right session key - getContext().sessionKeyManager().tagsReceived(replySessionKey, tags); - - PublicKey pk = replyPeer.getIdentity().getPublicKey(); - - DeliveryInstructions instructions = new DeliveryInstructions(); - instructions.setDelayRequested(false); - instructions.setDelaySeconds(0); - instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_TUNNEL); - instructions.setDestination(null); - instructions.setEncrypted(false); - instructions.setEncryptionKey(null); - instructions.setRouter(gateway.getGateway()); - instructions.setTunnelId(gateway.getTunnelId()); - - long replyId = getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE); - - Certificate replyCert = new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null); - - long expiration = getContext().clock().now() + _timeoutMs; // _expiration; - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Setting the expiration on the reply block to " + (new Date(expiration))); - SourceRouteBlock block = new SourceRouteBlock(); - try { - long begin = getContext().clock().now(); - block.setData(getContext(), instructions, replyId, replyCert, expiration, pk); - long end = getContext().clock().now(); - if ( (end - begin) > 1000) { - if (_log.shouldLog(Log.WARN)) - _log.warn("Took too long (" + (end-begin) + "ms) to build source route block"); - } else { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("did NOT take long (" + (end-begin) + "ms) to build source route block!"); - } - } catch (DataFormatException dfe) { - if (_log.shouldLog(Log.ERROR)) - _log.error("Error building the reply block", dfe); - return null; - } - - block.setRouter(replyPeer.getIdentity().getHash()); - block.setKey(replySessionKey); - block.setTag(tag); - - return block; - } - - /** - * Create a message containing a random id to check for after garlic routing - * it out so that we know the other message in the garlic has been received - * - */ - private DeliveryStatusMessage buildDeliveryStatusMessage() { - DeliveryStatusMessage msg = new DeliveryStatusMessage(getContext()); - msg.setArrival(new Date(getContext().clock().now())); - msg.setMessageId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE)); - Date exp = new Date(getContext().clock().now() + _timeoutMs); // _expiration); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Setting the expiration on the delivery status message to " + exp); - msg.setMessageExpiration(exp); - return msg; - } - - /** * Build a garlic message wrapping the data and status as cloves with both to be routed - * through the target, where the data is destined. The status however is to continue on - * to the replyPeer, where it is then sent down the replyTunnel to the local router. + * through the target, where the data is destined. * */ - private GarlicMessage buildGarlicMessage(I2NPMessage data, I2NPMessage status, - RouterInfo replyPeer, TunnelGateway replyTunnel, + private GarlicMessage buildGarlicMessage(I2NPMessage data, + TunnelGateway replyTunnel, RouterInfo target, SessionKey wrappedKey, Set wrappedTags, PublicKey wrappedTo) { - GarlicConfig config = buildGarlicConfig(data, status, replyPeer, replyTunnel, target); + GarlicConfig config = buildGarlicConfig(data, replyTunnel, target); PublicKey rcptKey = config.getRecipientPublicKey(); if (rcptKey == null) { @@ -562,16 +434,13 @@ public class RequestTunnelJob extends JobImpl { return message; } - private GarlicConfig buildGarlicConfig(I2NPMessage data, I2NPMessage status, - RouterInfo replyPeer, TunnelGateway replyTunnel, - RouterInfo target) { + private GarlicConfig buildGarlicConfig(I2NPMessage data, + TunnelGateway replyTunnel, RouterInfo target) { GarlicConfig config = new GarlicConfig(); long garlicExpiration = getContext().clock().now() + _timeoutMs; PayloadGarlicConfig dataClove = buildDataClove(data, target, garlicExpiration); config.addClove(dataClove); - PayloadGarlicConfig ackClove = buildAckClove(status, replyPeer, replyTunnel, garlicExpiration); - config.addClove(ackClove); DeliveryInstructions instructions = new DeliveryInstructions(); instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_ROUTER); @@ -590,40 +459,10 @@ public class RequestTunnelJob extends JobImpl { config.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE)); config.setExpiration(garlicExpiration); config.setRecipientPublicKey(target.getIdentity().getPublicKey()); - config.setRequestAck(false); return config; } - /** - * Build a clove that sends a DeliveryStatusMessage to us - */ - private PayloadGarlicConfig buildAckClove(I2NPMessage ackMsg, RouterInfo replyPeer, - TunnelGateway replyTunnel, long expiration) { - PayloadGarlicConfig ackClove = new PayloadGarlicConfig(); - - Hash replyToTunnelRouter = replyTunnel.getGateway(); // inbound tunnel gateway - TunnelId replyToTunnelId = replyTunnel.getTunnelId(); // tunnel id on that gateway - - DeliveryInstructions ackInstructions = new DeliveryInstructions(); - ackInstructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_TUNNEL); - ackInstructions.setRouter(replyToTunnelRouter); - ackInstructions.setTunnelId(replyToTunnelId); - ackInstructions.setDelayRequested(false); - ackInstructions.setDelaySeconds(0); - ackInstructions.setEncrypted(false); - - ackClove.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null)); - ackClove.setDeliveryInstructions(ackInstructions); - ackClove.setExpiration(expiration); - ackClove.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE)); - ackClove.setPayload(ackMsg); - ackClove.setRecipient(replyPeer); - ackClove.setRequestAck(false); - - return ackClove; - } - /** * Build a clove that sends the data to the target (which is local) */ @@ -808,12 +647,10 @@ public class RequestTunnelJob extends JobImpl { private class Failure extends JobImpl { private TunnelInfo _tunnel; - private Hash _replyThrough; private long _started; - public Failure(TunnelInfo tunnel, Hash replyThrough) { + public Failure(TunnelInfo tunnel) { super(RequestTunnelJob.this.getContext()); _tunnel = tunnel; - _replyThrough = replyThrough; _started = getContext().clock().now(); } @@ -829,9 +666,8 @@ public class RequestTunnelJob extends JobImpl { } synchronized (_failedTunnelParticipants) { _failedTunnelParticipants.add(_tunnel.getThisHop()); - _failedTunnelParticipants.add(_replyThrough); } - Failure.this.getContext().messageHistory().tunnelRequestTimedOut(_tunnel.getThisHop(), _tunnel.getTunnelId(), _replyThrough); + Failure.this.getContext().messageHistory().tunnelRequestTimedOut(_tunnel.getThisHop(), _tunnel.getTunnelId()); long responseTime = getContext().clock().now() - _started; // perhaps not an explicit reject, but an implicit one (due to dropped messages, tunnel failure, etc) getContext().profileManager().tunnelRejected(_tunnel.getThisHop(), responseTime, false); @@ -843,26 +679,20 @@ public class RequestTunnelJob extends JobImpl { private class Selector implements MessageSelector { private TunnelInfo _tunnel; - private long _ackId; private boolean _statusFound; - private boolean _ackFound; private long _attemptExpiration; - public Selector(TunnelInfo tunnel, long ackId) { + public Selector(TunnelInfo tunnel) { _tunnel = tunnel; - _ackId = ackId; _statusFound = false; - _ackFound = false; _attemptExpiration = getContext().clock().now() + _timeoutMs; } public boolean continueMatching() { if (_log.shouldLog(Log.DEBUG)) _log.debug("ContinueMatching looking for tunnel " + _tunnel.getTunnelId().getTunnelId() - + " from " + _tunnel.getThisHop().toBase64() + ": found? " + _statusFound - + " ackFound? " + _ackFound); - return !_statusFound || !_ackFound; - //return !_statusFound; // who cares about the ack if we get the status OK? + + " from " + _tunnel.getThisHop().toBase64() + ": found? " + _statusFound); + return !_statusFound; } public long getExpiration() { return _attemptExpiration; } public boolean isMatch(I2NPMessage message) { @@ -890,17 +720,6 @@ public class RequestTunnelJob extends JobImpl { + _tunnel.getThisHop().toBase64() + "]"); return false; } - } else if (message.getType() == DeliveryStatusMessage.MESSAGE_TYPE) { - if (((DeliveryStatusMessage)message).getMessageId() == _ackId) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Matches the ping message tied to the tunnel create status message"); - _ackFound = true; - return true; - } else { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Message is a delivery status message, but with the wrong id"); - return false; - } } else { //_log.debug("Message " + message.getClass().getName() // + " is not a delivery status or tunnel create status message [waiting for ok for tunnel " @@ -911,8 +730,8 @@ public class RequestTunnelJob extends JobImpl { public String toString() { return "Build Tunnel Job Selector for tunnel " + _tunnel.getTunnelId().getTunnelId() - + " at " + _tunnel.getThisHop().toBase64() + " [found=" + _statusFound + ", ack=" - + _ackFound + "] (@" + (new Date(getExpiration())) + ")"; + + " at " + _tunnel.getThisHop().toBase64() + " [found=" + _statusFound + "] (@" + + (new Date(getExpiration())) + ")"; } } } diff --git a/router/java/src/net/i2p/router/tunnelmanager/TunnelCreateMessageHandler.java b/router/java/src/net/i2p/router/tunnelmanager/TunnelCreateMessageHandler.java index 371ad329e..8e13decd8 100644 --- a/router/java/src/net/i2p/router/tunnelmanager/TunnelCreateMessageHandler.java +++ b/router/java/src/net/i2p/router/tunnelmanager/TunnelCreateMessageHandler.java @@ -11,7 +11,6 @@ package net.i2p.router.tunnelmanager; import net.i2p.data.Hash; import net.i2p.data.RouterIdentity; import net.i2p.data.i2np.I2NPMessage; -import net.i2p.data.i2np.SourceRouteBlock; import net.i2p.data.i2np.TunnelCreateMessage; import net.i2p.router.HandlerJobBuilder; import net.i2p.router.Job; @@ -22,8 +21,8 @@ class TunnelCreateMessageHandler implements HandlerJobBuilder { public TunnelCreateMessageHandler(RouterContext context) { _context = context; } - public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) { - return new HandleTunnelCreateMessageJob(_context, (TunnelCreateMessage)receivedMessage, from, fromHash, replyBlock); + public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) { + return new HandleTunnelCreateMessageJob(_context, (TunnelCreateMessage)receivedMessage, from, fromHash); } }