if I'm making this backwards incompatible, I might as well clean up the rest, 'eh?

* removed SourceRouteBlock & SourceRouteReplyMessage, as they're a redundant concept
that 1) takes up bandwidth 2) takes up CPU 3) smell funny.
now the TunnelCreateMessage includes a replyTag, replyKey, replyTunnel, and
replyGateway that they garlic encrypt their ACK/NACK through and with.

* tunnelCreateMessage doesn't need a seperate ACK - either we get a
TunnelCreateStatusMessage back or we don't.

* message structure mods for unique tunnel ID per hop (though currently all hops have
the same tunnel ID)
This commit is contained in:
jrandom
2004-07-27 22:04:02 +00:00
committed by zzz
parent 0ed95bbdf1
commit 60c7db0733
27 changed files with 274 additions and 1152 deletions

View File

@ -144,7 +144,8 @@ public class DatabaseStoreMessage extends I2NPMessageImpl {
byte compressed[] = new byte[compressedSize]; byte compressed[] = new byte[compressedSize];
int read = DataHelper.read(in, compressed); int read = DataHelper.read(in, compressed);
if (read != compressedSize) 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)); ByteArrayInputStream bais = new ByteArrayInputStream(DataHelper.decompress(compressed));
_info.readBytes(bais); _info.readBytes(bais);
} else { } else {

View File

@ -34,25 +34,8 @@ public class GarlicClove extends DataStructureImpl {
private long _cloveId; private long _cloveId;
private Date _expiration; private Date _expiration;
private Certificate _certificate; private Certificate _certificate;
private int _replyAction;
private SourceRouteBlock _sourceRouteBlock;
private I2NPMessageHandler _handler; 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) { public GarlicClove(RouterContext context) {
_context = context; _context = context;
_log = context.logManager().getLog(GarlicClove.class); _log = context.logManager().getLog(GarlicClove.class);
@ -62,8 +45,6 @@ public class GarlicClove extends DataStructureImpl {
setCloveId(-1); setCloveId(-1);
setExpiration(null); setExpiration(null);
setCertificate(null); setCertificate(null);
setSourceRouteBlockAction(ACTION_NONE);
setSourceRouteBlock(null);
} }
public DeliveryInstructions getInstructions() { return _instructions; } public DeliveryInstructions getInstructions() { return _instructions; }
@ -76,10 +57,6 @@ public class GarlicClove extends DataStructureImpl {
public void setExpiration(Date exp) { _expiration = exp; } public void setExpiration(Date exp) { _expiration = exp; }
public Certificate getCertificate() { return _certificate; } public Certificate getCertificate() { return _certificate; }
public void setCertificate(Certificate cert) { _certificate = cert; } 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 { public void readBytes(InputStream in) throws DataFormatException, IOException {
_instructions = new DeliveryInstructions(); _instructions = new DeliveryInstructions();
@ -99,12 +76,6 @@ public class GarlicClove extends DataStructureImpl {
_certificate.readBytes(in); _certificate.readBytes(in);
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Read cert: " + _certificate); _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 { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
@ -119,12 +90,10 @@ public class GarlicClove extends DataStructureImpl {
error.append("Expiration is null "); error.append("Expiration is null ");
if (_certificate == null) if (_certificate == null)
error.append("Certificate is null "); error.append("Certificate is null ");
if (_replyAction < 0)
error.append("Reply action is < 0 [").append(_replyAction).append("] ");;
if (error.length() > 0) if (error.length() > 0)
throw new DataFormatException(error.toString()); 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); _instructions.writeBytes(out);
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
@ -138,9 +107,6 @@ public class GarlicClove extends DataStructureImpl {
_certificate.writeBytes(out); _certificate.writeBytes(out);
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Written cert: " + _certificate); _log.debug("Written cert: " + _certificate);
DataHelper.writeLong(out, 1, _replyAction);
if ( (_replyAction != 0) && (_sourceRouteBlock != null) )
_sourceRouteBlock.writeBytes(out);
} }
public boolean equals(Object obj) { public boolean equals(Object obj) {
@ -151,9 +117,7 @@ public class GarlicClove extends DataStructureImpl {
DataHelper.eq(getCloveId(), clove.getCloveId()) && DataHelper.eq(getCloveId(), clove.getCloveId()) &&
DataHelper.eq(getData(), clove.getData()) && DataHelper.eq(getData(), clove.getData()) &&
DataHelper.eq(getExpiration(), clove.getExpiration()) && DataHelper.eq(getExpiration(), clove.getExpiration()) &&
DataHelper.eq(getInstructions(), clove.getInstructions()) && DataHelper.eq(getInstructions(), clove.getInstructions());
DataHelper.eq(getSourceRouteBlock(), clove.getSourceRouteBlock()) &&
(getSourceRouteBlockAction() == clove.getSourceRouteBlockAction());
} }
public int hashCode() { public int hashCode() {
@ -161,9 +125,7 @@ public class GarlicClove extends DataStructureImpl {
(int)getCloveId() + (int)getCloveId() +
DataHelper.hashCode(getData()) + DataHelper.hashCode(getData()) +
DataHelper.hashCode(getExpiration()) + DataHelper.hashCode(getExpiration()) +
DataHelper.hashCode(getInstructions()) + DataHelper.hashCode(getInstructions());
DataHelper.hashCode(getSourceRouteBlock()) +
getSourceRouteBlockAction();
} }
public String toString() { public String toString() {
@ -173,8 +135,6 @@ public class GarlicClove extends DataStructureImpl {
buf.append("\n\tCertificate: ").append(getCertificate()); buf.append("\n\tCertificate: ").append(getCertificate());
buf.append("\n\tClove ID: ").append(getCloveId()); buf.append("\n\tClove ID: ").append(getCloveId());
buf.append("\n\tExpiration: ").append(getExpiration()); 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("\n\tData: ").append(getData());
buf.append("]"); buf.append("]");
return buf.toString(); return buf.toString();

View File

@ -73,8 +73,6 @@ public class I2NPMessageHandler {
return new TunnelMessage(_context); return new TunnelMessage(_context);
case DataMessage.MESSAGE_TYPE: case DataMessage.MESSAGE_TYPE:
return new DataMessage(_context); return new DataMessage(_context);
case SourceRouteReplyMessage.MESSAGE_TYPE:
return new SourceRouteReplyMessage(_context);
case TunnelCreateMessage.MESSAGE_TYPE: case TunnelCreateMessage.MESSAGE_TYPE:
return new TunnelCreateMessage(_context); return new TunnelCreateMessage(_context);
case TunnelCreateStatusMessage.MESSAGE_TYPE: case TunnelCreateStatusMessage.MESSAGE_TYPE:

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -18,6 +18,8 @@ import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.TunnelId; import net.i2p.data.TunnelId;
import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag;
import net.i2p.util.Log; import net.i2p.util.Log;
/** /**
@ -30,8 +32,9 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
private final static Log _log = new Log(TunnelCreateMessage.class); private final static Log _log = new Log(TunnelCreateMessage.class);
public final static int MESSAGE_TYPE = 6; public final static int MESSAGE_TYPE = 6;
private int _participantType; private int _participantType;
private Hash _nextRouter;
private TunnelId _tunnelId; private TunnelId _tunnelId;
private Hash _nextRouter;
private TunnelId _nextTunnelId;
private long _tunnelDuration; private long _tunnelDuration;
private TunnelConfigurationSessionKey _configKey; private TunnelConfigurationSessionKey _configKey;
private long _maxPeakMessagesPerMin; private long _maxPeakMessagesPerMin;
@ -44,7 +47,10 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
private TunnelSigningPrivateKey _verificationPrivKey; private TunnelSigningPrivateKey _verificationPrivKey;
private TunnelSessionKey _tunnelKey; private TunnelSessionKey _tunnelKey;
private Certificate _certificate; 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_GATEWAY = 1;
public static final int PARTICIPANT_TYPE_ENDPOINT = 2; public static final int PARTICIPANT_TYPE_ENDPOINT = 2;
@ -57,6 +63,7 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
super(context); super(context);
setParticipantType(-1); setParticipantType(-1);
setNextRouter(null); setNextRouter(null);
setNextTunnelId(null);
setTunnelId(null); setTunnelId(null);
setTunnelDurationSeconds(-1); setTunnelDurationSeconds(-1);
setConfigurationKey(null); setConfigurationKey(null);
@ -70,13 +77,18 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
setVerificationPrivateKey(null); setVerificationPrivateKey(null);
setTunnelKey(null); setTunnelKey(null);
setCertificate(null); setCertificate(null);
setReplyBlock(null); setReplyTag(null);
setReplyKey(null);
setReplyTunnel(null);
setReplyPeer(null);
} }
public void setParticipantType(int type) { _participantType = type; } public void setParticipantType(int type) { _participantType = type; }
public int getParticipantType() { return _participantType; } public int getParticipantType() { return _participantType; }
public void setNextRouter(Hash routerIdentityHash) { _nextRouter = routerIdentityHash; } public void setNextRouter(Hash routerIdentityHash) { _nextRouter = routerIdentityHash; }
public Hash getNextRouter() { return _nextRouter; } 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 void setTunnelId(TunnelId id) { _tunnelId = id; }
public TunnelId getTunnelId() { return _tunnelId; } public TunnelId getTunnelId() { return _tunnelId; }
public void setTunnelDurationSeconds(long durationSeconds) { _tunnelDuration = durationSeconds; } public void setTunnelDurationSeconds(long durationSeconds) { _tunnelDuration = durationSeconds; }
@ -103,8 +115,14 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
public TunnelSessionKey getTunnelKey() { return _tunnelKey; } public TunnelSessionKey getTunnelKey() { return _tunnelKey; }
public void setCertificate(Certificate cert) { _certificate = cert; } public void setCertificate(Certificate cert) { _certificate = cert; }
public Certificate getCertificate() { return _certificate; } public Certificate getCertificate() { return _certificate; }
public void setReplyBlock(SourceRouteBlock block) { _replyBlock = block; } public void setReplyTag(SessionTag tag) { _replyTag = tag; }
public SourceRouteBlock getReplyBlock() { return _replyBlock; } 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 { public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message"); 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) { if (_participantType != PARTICIPANT_TYPE_ENDPOINT) {
_nextRouter = new Hash(); _nextRouter = new Hash();
_nextRouter.readBytes(in); _nextRouter.readBytes(in);
_nextTunnelId = new TunnelId();
_nextTunnelId.readBytes(in);
} }
_tunnelId = new TunnelId(); _tunnelId = new TunnelId();
_tunnelId.readBytes(in); _tunnelId.readBytes(in);
@ -140,8 +160,14 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
} }
_certificate = new Certificate(); _certificate = new Certificate();
_certificate.readBytes(in); _certificate.readBytes(in);
_replyBlock = new SourceRouteBlock(); _replyTag = new SessionTag();
_replyBlock.readBytes(in); _replyTag.readBytes(in);
_replyKey = new SessionKey();
_replyKey.readBytes(in);
_replyTunnel = new TunnelId();
_replyTunnel.readBytes(in);
_replyPeer = new Hash();
_replyPeer.readBytes(in);
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
throw new I2NPMessageException("Unable to load the message data", 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); DataHelper.writeLong(os, 1, _participantType);
if (_participantType != PARTICIPANT_TYPE_ENDPOINT) { if (_participantType != PARTICIPANT_TYPE_ENDPOINT) {
_nextRouter.writeBytes(os); _nextRouter.writeBytes(os);
_nextTunnelId.writeBytes(os);
} }
_tunnelId.writeBytes(os); _tunnelId.writeBytes(os);
DataHelper.writeLong(os, 4, _tunnelDuration); DataHelper.writeLong(os, 4, _tunnelDuration);
@ -174,7 +201,10 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
_tunnelKey.writeBytes(os); _tunnelKey.writeBytes(os);
} }
_certificate.writeBytes(os); _certificate.writeBytes(os);
_replyBlock.writeBytes(os); _replyTag.writeBytes(os);
_replyKey.writeBytes(os);
_replyTunnel.writeBytes(os);
_replyPeer.writeBytes(os);
} catch (Throwable t) { } catch (Throwable t) {
throw new I2NPMessageException("Error writing out the message data", t); throw new I2NPMessageException("Error writing out the message data", t);
} }
@ -203,7 +233,9 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
return (int)(DataHelper.hashCode(getCertificate()) + return (int)(DataHelper.hashCode(getCertificate()) +
DataHelper.hashCode(getConfigurationKey()) + DataHelper.hashCode(getConfigurationKey()) +
DataHelper.hashCode(getNextRouter()) + DataHelper.hashCode(getNextRouter()) +
DataHelper.hashCode(getReplyBlock()) + DataHelper.hashCode(getNextTunnelId()) +
DataHelper.hashCode(getReplyPeer()) +
DataHelper.hashCode(getReplyTunnel()) +
DataHelper.hashCode(getTunnelId()) + DataHelper.hashCode(getTunnelId()) +
DataHelper.hashCode(getTunnelKey()) + DataHelper.hashCode(getTunnelKey()) +
DataHelper.hashCode(getVerificationPrivateKey()) + DataHelper.hashCode(getVerificationPrivateKey()) +
@ -224,7 +256,11 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
return DataHelper.eq(getCertificate(), msg.getCertificate()) && return DataHelper.eq(getCertificate(), msg.getCertificate()) &&
DataHelper.eq(getConfigurationKey(), msg.getConfigurationKey()) && DataHelper.eq(getConfigurationKey(), msg.getConfigurationKey()) &&
DataHelper.eq(getNextRouter(), msg.getNextRouter()) && 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(getTunnelId(), msg.getTunnelId()) &&
DataHelper.eq(getTunnelKey(), msg.getTunnelKey()) && DataHelper.eq(getTunnelKey(), msg.getTunnelKey()) &&
DataHelper.eq(getVerificationPrivateKey(), msg.getVerificationPrivateKey()) && DataHelper.eq(getVerificationPrivateKey(), msg.getVerificationPrivateKey()) &&
@ -249,7 +285,11 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
buf.append("\n\tCertificate: ").append(getCertificate()); buf.append("\n\tCertificate: ").append(getCertificate());
buf.append("\n\tConfiguration Key: ").append(getConfigurationKey()); buf.append("\n\tConfiguration Key: ").append(getConfigurationKey());
buf.append("\n\tNext Router: ").append(getNextRouter()); 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 ID: ").append(getTunnelId());
buf.append("\n\tTunnel Key: ").append(getTunnelKey()); buf.append("\n\tTunnel Key: ").append(getTunnelKey());
buf.append("\n\tVerification Private Key: ").append(getVerificationPrivateKey()); buf.append("\n\tVerification Private Key: ").append(getVerificationPrivateKey());

View File

@ -11,7 +11,6 @@ package net.i2p.router;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.RouterIdentity; import net.i2p.data.RouterIdentity;
import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.SourceRouteBlock;
/** /**
* Defines a class that builds jobs to handle a particular message - these * 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 receivedMessage I2NP message received
* @param from router that sent the message (if available) * @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 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, * @return a job or null if no particular job is appropriate (in which case,
* the message should go into the inbound message pool) * 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);
} }

View File

@ -11,7 +11,6 @@ package net.i2p.router;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.RouterIdentity; import net.i2p.data.RouterIdentity;
import net.i2p.data.i2np.I2NPMessage; 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. * Wrap an I2NP message received from the network prior to handling and processing.
@ -22,7 +21,6 @@ public class InNetMessage {
private I2NPMessage _message; private I2NPMessage _message;
private RouterIdentity _fromRouter; private RouterIdentity _fromRouter;
private Hash _fromRouterHash; private Hash _fromRouterHash;
private SourceRouteBlock _replyBlock;
private long _created; private long _created;
public InNetMessage(RouterContext context) { public InNetMessage(RouterContext context) {
@ -30,7 +28,6 @@ public class InNetMessage {
setMessage(null); setMessage(null);
setFromRouter(null); setFromRouter(null);
setFromRouterHash(null); setFromRouterHash(null);
setReplyBlock(null);
context.messageStateMonitor().inboundMessageAdded(); context.messageStateMonitor().inboundMessageAdded();
_created = context.clock().now(); _created = context.clock().now();
_context.statManager().createRateStat("inNetMessage.timeToDiscard", _context.statManager().createRateStat("inNetMessage.timeToDiscard",
@ -59,15 +56,6 @@ public class InNetMessage {
public RouterIdentity getFromRouter() { return _fromRouter; } public RouterIdentity getFromRouter() { return _fromRouter; }
public void setFromRouter(RouterIdentity router) { _fromRouter = router; } 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 * Call this after we're done dealing with this message (when we no
* longer need its data) * longer need its data)

View File

@ -88,7 +88,7 @@ public class InNetMessagePool {
if (builder != null) { if (builder != null) {
Job job = builder.createJob(messageBody, msg.getFromRouter(), Job job = builder.createJob(messageBody, msg.getFromRouter(),
msg.getFromRouterHash(), msg.getReplyBlock()); msg.getFromRouterHash());
if (job != null) { if (job != null) {
_context.jobQueue().addJob(job); _context.jobQueue().addJob(job);
synchronized (_messages) { synchronized (_messages) {

View File

@ -19,7 +19,6 @@ import java.util.Iterator;
import java.util.SortedMap; import java.util.SortedMap;
import java.util.TreeMap; import java.util.TreeMap;
import net.i2p.router.message.HandleSourceRouteReplyMessageJob;
import net.i2p.router.networkdb.HandleDatabaseLookupMessageJob; import net.i2p.router.networkdb.HandleDatabaseLookupMessageJob;
import net.i2p.router.tunnelmanager.HandleTunnelCreateMessageJob; import net.i2p.router.tunnelmanager.HandleTunnelCreateMessageJob;
import net.i2p.router.tunnelmanager.RequestTunnelJob; import net.i2p.router.tunnelmanager.RequestTunnelJob;
@ -221,14 +220,6 @@ public class JobQueue {
if (!_allowParallelOperation) return false; // dont drop during startup [duh] if (!_allowParallelOperation) return false; // dont drop during startup [duh]
Class cls = job.getClass(); Class cls = job.getClass();
if (numReady > _maxWaitingJobs) { 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... // lets not try to drop too many tunnel messages...
//if (cls == HandleTunnelMessageJob.class) //if (cls == HandleTunnelMessageJob.class)
// return true; // return true;

View File

@ -128,11 +128,10 @@ public class MessageHistory {
* @param outTunnel tunnel we are sending this request out * @param outTunnel tunnel we are sending this request out
* @param peerRequested peer asked to participate in the tunnel * @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 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 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 * @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; if (!_doLog) return;
StringBuffer buf = new StringBuffer(128); StringBuffer buf = new StringBuffer(128);
buf.append(getPrefix()); buf.append(getPrefix());
@ -142,8 +141,6 @@ public class MessageHistory {
buf.append("(next [").append(getName(nextPeer)).append("]) "); buf.append("(next [").append(getName(nextPeer)).append("]) ");
if (outTunnel != null) if (outTunnel != null)
buf.append("via [").append(outTunnel.getTunnelId()).append("] "); 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) ) if ( (replyTunnel != null) && (replyThrough != null) )
buf.append("who forwards it through [").append(replyTunnel.getTunnelId()).append("] on [").append(getName(replyThrough)).append("]"); buf.append("who forwards it through [").append(replyTunnel.getTunnelId()).append("] on [").append(getName(replyThrough)).append("]");
addEntry(buf.toString()); addEntry(buf.toString());
@ -258,15 +255,13 @@ public class MessageHistory {
* of a timeout or an explicit refusal). * 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 (!_doLog) return;
if ( (tunnel == null) || (peer == null) ) return; if ( (tunnel == null) || (peer == null) ) return;
StringBuffer buf = new StringBuffer(128); StringBuffer buf = new StringBuffer(128);
buf.append(getPrefix()); buf.append(getPrefix());
buf.append("tunnel [").append(tunnel.getTunnelId()).append("] timed out on ["); buf.append("tunnel [").append(tunnel.getTunnelId()).append("] timed out on [");
buf.append(getName(peer)).append("]"); buf.append(getName(peer)).append("]");
if (replyThrough != null)
buf.append(" with their reply intended to come through [").append(getName(replyThrough)).append("]");
addEntry(buf.toString()); addEntry(buf.toString());
} }

View File

@ -28,10 +28,8 @@ import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.RouterInfo; import net.i2p.data.RouterInfo;
import net.i2p.data.i2np.GarlicMessage; import net.i2p.data.i2np.GarlicMessage;
import net.i2p.data.i2np.SourceRouteReplyMessage;
import net.i2p.data.i2np.TunnelMessage; import net.i2p.data.i2np.TunnelMessage;
import net.i2p.router.message.GarlicMessageHandler; import net.i2p.router.message.GarlicMessageHandler;
import net.i2p.router.message.SourceRouteReplyMessageHandler;
import net.i2p.router.message.TunnelMessageHandler; import net.i2p.router.message.TunnelMessageHandler;
import net.i2p.router.startup.StartupJob; import net.i2p.router.startup.StartupJob;
import net.i2p.stat.Rate; import net.i2p.stat.Rate;
@ -218,7 +216,6 @@ public class Router {
private void setupHandlers() { private void setupHandlers() {
_context.inNetMessagePool().registerHandlerJobBuilder(GarlicMessage.MESSAGE_TYPE, new GarlicMessageHandler(_context)); _context.inNetMessagePool().registerHandlerJobBuilder(GarlicMessage.MESSAGE_TYPE, new GarlicMessageHandler(_context));
_context.inNetMessagePool().registerHandlerJobBuilder(TunnelMessage.MESSAGE_TYPE, new TunnelMessageHandler(_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 { public void renderStatusHTML(OutputStream out) throws IOException {

View File

@ -22,7 +22,6 @@ import net.i2p.data.SessionTag;
import net.i2p.data.i2np.GarlicClove; import net.i2p.data.i2np.GarlicClove;
import net.i2p.data.i2np.GarlicMessage; import net.i2p.data.i2np.GarlicMessage;
import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.SourceRouteBlock;
import net.i2p.router.RouterContext; import net.i2p.router.RouterContext;
import net.i2p.util.Log; import net.i2p.util.Log;
@ -36,9 +35,6 @@ public class GarlicMessageBuilder {
} }
public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags) { public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags) {
Log log = ctx.logManager().getLog(GarlicMessageBuilder.class); Log log = ctx.logManager().getLog(GarlicMessageBuilder.class);
if (config == null)
throw new IllegalArgumentException("Null config specified");
PublicKey key = config.getRecipientPublicKey(); PublicKey key = config.getRecipientPublicKey();
if (key == null) { if (key == null) {
if (config.getRecipient() == null) { if (config.getRecipient() == null) {
@ -50,19 +46,15 @@ public class GarlicMessageBuilder {
} else } else
key = config.getRecipient().getIdentity().getPublicKey(); key = config.getRecipient().getIdentity().getPublicKey();
} }
GarlicMessage msg = new GarlicMessage(ctx);
noteWrap(ctx, msg, config);
if (log.shouldLog(Log.INFO)) if (log.shouldLog(Log.INFO))
log.info("Encrypted with public key " + key + " to expire on " + new Date(config.getExpiration())); 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); SessionKey curKey = ctx.sessionKeyManager().getCurrentKey(key);
if (curKey == null) if (curKey == null)
curKey = ctx.sessionKeyManager().createSession(key); curKey = ctx.sessionKeyManager().createSession(key);
wrappedKey.setData(curKey.getData());
SessionTag curTag = ctx.sessionKeyManager().consumeNextAvailableTag(key, curKey);
int availTags = ctx.sessionKeyManager().getAvailableTags(key, curKey); int availTags = ctx.sessionKeyManager().getAvailableTags(key, curKey);
if (log.shouldLog(Log.DEBUG)) if (log.shouldLog(Log.DEBUG))
@ -83,8 +75,34 @@ public class GarlicMessageBuilder {
// always tack on at least one more - not necessary. // always tack on at least one more - not necessary.
//wrappedTags.add(new SessionTag(true)); //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); msg.setData(encData);
Date exp = new Date(config.getExpiration()); Date exp = new Date(config.getExpiration());
msg.setMessageExpiration(exp); msg.setMessageExpiration(exp);
@ -168,46 +186,8 @@ public class GarlicMessageBuilder {
clove.setCloveId(config.getId()); clove.setCloveId(config.getId());
clove.setExpiration(new Date(config.getExpiration())); clove.setExpiration(new Date(config.getExpiration()));
clove.setInstructions(config.getDeliveryInstructions()); clove.setInstructions(config.getDeliveryInstructions());
specifySourceRouteBlock(ctx, clove, config);
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
clove.writeBytes(baos); clove.writeBytes(baos);
return baos.toByteArray(); 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);
}
}
} }

View File

@ -12,7 +12,6 @@ import net.i2p.data.Hash;
import net.i2p.data.RouterIdentity; import net.i2p.data.RouterIdentity;
import net.i2p.data.i2np.GarlicMessage; import net.i2p.data.i2np.GarlicMessage;
import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.SourceRouteBlock;
import net.i2p.router.HandlerJobBuilder; import net.i2p.router.HandlerJobBuilder;
import net.i2p.router.Job; import net.i2p.router.Job;
import net.i2p.router.RouterContext; import net.i2p.router.RouterContext;
@ -28,8 +27,7 @@ public class GarlicMessageHandler implements HandlerJobBuilder {
_context = context; _context = context;
} }
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) { public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) {
// ignore the reply block for the moment
HandleGarlicMessageJob job = new HandleGarlicMessageJob(_context, (GarlicMessage)receivedMessage, from, fromHash); HandleGarlicMessageJob job = new HandleGarlicMessageJob(_context, (GarlicMessage)receivedMessage, from, fromHash);
return job; return job;
} }

View File

@ -165,10 +165,8 @@ public class HandleGarlicMessageJob extends JobImpl {
_log.warn("Invalid clove " + clove); _log.warn("Invalid clove " + clove);
return; return;
} }
boolean requestAck = (clove.getSourceRouteBlockAction() == GarlicClove.ACTION_STATUS);
long sendExpiration = clove.getExpiration().getTime(); long sendExpiration = clove.getExpiration().getTime();
_handler.handleMessage(clove.getInstructions(), clove.getData(), _handler.handleMessage(clove.getInstructions(), clove.getData(),
requestAck, clove.getSourceRouteBlock(),
clove.getCloveId(), _from, _fromHash, clove.getCloveId(), _from, _fromHash,
sendExpiration, FORWARD_PRIORITY); sendExpiration, FORWARD_PRIORITY);
} }

View File

@ -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");
}
}

View File

@ -17,7 +17,6 @@ import net.i2p.data.TunnelId;
import net.i2p.data.i2np.DataMessage; import net.i2p.data.i2np.DataMessage;
import net.i2p.data.i2np.DeliveryInstructions; import net.i2p.data.i2np.DeliveryInstructions;
import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.SourceRouteBlock;
import net.i2p.data.i2np.TunnelMessage; import net.i2p.data.i2np.TunnelMessage;
import net.i2p.router.ClientMessage; import net.i2p.router.ClientMessage;
import net.i2p.router.InNetMessage; import net.i2p.router.InNetMessage;
@ -41,7 +40,6 @@ class MessageHandler {
} }
public void handleMessage(DeliveryInstructions instructions, I2NPMessage message, public void handleMessage(DeliveryInstructions instructions, I2NPMessage message,
boolean requestAck, SourceRouteBlock replyBlock,
long replyId, RouterIdentity from, Hash fromHash, long replyId, RouterIdentity from, Hash fromHash,
long expiration, int priority) { long expiration, int priority) {
switch (instructions.getDeliveryMode()) { switch (instructions.getDeliveryMode()) {
@ -50,7 +48,7 @@ class MessageHandler {
if (message.getType() == DataMessage.MESSAGE_TYPE) { if (message.getType() == DataMessage.MESSAGE_TYPE) {
handleLocalDestination(instructions, message, fromHash); handleLocalDestination(instructions, message, fromHash);
} else { } else {
handleLocalRouter(message, from, fromHash, replyBlock, requestAck); handleLocalRouter(message, from, fromHash);
} }
break; break;
case DeliveryInstructions.DELIVERY_MODE_ROUTER: case DeliveryInstructions.DELIVERY_MODE_ROUTER:
@ -58,7 +56,7 @@ class MessageHandler {
_log.debug("Instructions for ROUTER DELIVERY to " _log.debug("Instructions for ROUTER DELIVERY to "
+ instructions.getRouter().toBase64()); + instructions.getRouter().toBase64());
if (_context.routerHash().equals(instructions.getRouter())) { if (_context.routerHash().equals(instructions.getRouter())) {
handleLocalRouter(message, from, fromHash, replyBlock, requestAck); handleLocalRouter(message, from, fromHash);
} else { } else {
handleRemoteRouter(message, instructions, expiration, priority); handleRemoteRouter(message, instructions, expiration, priority);
} }
@ -84,28 +82,14 @@ class MessageHandler {
_log.error("Message has instructions that are not yet implemented: mode = " + instructions.getDeliveryMode()); _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) { private void handleLocalRouter(I2NPMessage message, RouterIdentity from, Hash fromHash) {
_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) {
_log.info("Handle " + message.getClass().getName() + " to a local router - toss it on the inbound network pool"); _log.info("Handle " + message.getClass().getName() + " to a local router - toss it on the inbound network pool");
InNetMessage msg = new InNetMessage(_context); InNetMessage msg = new InNetMessage(_context);
msg.setFromRouter(from); msg.setFromRouter(from);
msg.setFromRouterHash(fromHash); msg.setFromRouterHash(fromHash);
msg.setMessage(message); msg.setMessage(message);
if (!ackUsed)
msg.setReplyBlock(replyBlock);
_context.inNetMessagePool().add(msg); _context.inNetMessagePool().add(msg);
} }

View File

@ -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"; }
}

View File

@ -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. <p />
*
* 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. <p />
*
* 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"; }
}

View File

@ -488,7 +488,6 @@ public class SendTunnelMessageJob extends JobImpl {
msg.setFromRouter(ident); msg.setFromRouter(ident);
msg.setFromRouterHash(ident.getHash()); msg.setFromRouterHash(ident.getHash());
msg.setMessage(_message); msg.setMessage(_message);
msg.setReplyBlock(null);
getContext().inNetMessagePool().add(msg); getContext().inNetMessagePool().add(msg);
} else { } else {
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))

View File

@ -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;
}
}

View File

@ -11,7 +11,6 @@ package net.i2p.router.message;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.RouterIdentity; import net.i2p.data.RouterIdentity;
import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.SourceRouteBlock;
import net.i2p.data.i2np.TunnelMessage; import net.i2p.data.i2np.TunnelMessage;
import net.i2p.router.HandlerJobBuilder; import net.i2p.router.HandlerJobBuilder;
import net.i2p.router.Job; import net.i2p.router.Job;
@ -27,8 +26,7 @@ public class TunnelMessageHandler implements HandlerJobBuilder {
public TunnelMessageHandler(RouterContext context) { public TunnelMessageHandler(RouterContext context) {
_context = context; _context = context;
} }
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) { public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) {
// ignore the replyBlock for now
HandleTunnelMessageJob job = new HandleTunnelMessageJob(_context, (TunnelMessage)receivedMessage, from, fromHash); HandleTunnelMessageJob job = new HandleTunnelMessageJob(_context, (TunnelMessage)receivedMessage, from, fromHash);
return job; return job;
} }

View File

@ -12,7 +12,6 @@ import net.i2p.data.Hash;
import net.i2p.data.RouterIdentity; import net.i2p.data.RouterIdentity;
import net.i2p.data.i2np.DatabaseLookupMessage; import net.i2p.data.i2np.DatabaseLookupMessage;
import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.SourceRouteBlock;
import net.i2p.router.HandlerJobBuilder; import net.i2p.router.HandlerJobBuilder;
import net.i2p.router.Job; import net.i2p.router.Job;
import net.i2p.router.RouterContext; 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 }); _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); _context.statManager().addRateData("netDb.lookupsReceived", 1, 0);
if (_context.throttle().acceptNetDbLookupRequest(((DatabaseLookupMessage)receivedMessage).getSearchKey())) { if (_context.throttle().acceptNetDbLookupRequest(((DatabaseLookupMessage)receivedMessage).getSearchKey())) {

View File

@ -12,7 +12,6 @@ import net.i2p.data.Hash;
import net.i2p.data.RouterIdentity; import net.i2p.data.RouterIdentity;
import net.i2p.data.i2np.DatabaseStoreMessage; import net.i2p.data.i2np.DatabaseStoreMessage;
import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.SourceRouteBlock;
import net.i2p.router.HandlerJobBuilder; import net.i2p.router.HandlerJobBuilder;
import net.i2p.router.Job; import net.i2p.router.Job;
import net.i2p.router.RouterContext; import net.i2p.router.RouterContext;
@ -26,8 +25,7 @@ public class DatabaseStoreMessageHandler implements HandlerJobBuilder {
public DatabaseStoreMessageHandler(RouterContext context) { public DatabaseStoreMessageHandler(RouterContext context) {
_context = context; _context = context;
} }
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) { public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) {
// ignore the reply block for the moment
return new HandleDatabaseStoreMessageJob(_context, (DatabaseStoreMessage)receivedMessage, from, fromHash); return new HandleDatabaseStoreMessageJob(_context, (DatabaseStoreMessage)receivedMessage, from, fromHash);
} }
} }

View File

@ -9,20 +9,32 @@ package net.i2p.router.tunnelmanager;
*/ */
import java.util.Date; import java.util.Date;
import java.util.List;
import net.i2p.data.Certificate;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.RouterIdentity; import net.i2p.data.RouterIdentity;
import net.i2p.data.RouterInfo; import net.i2p.data.RouterInfo;
import net.i2p.data.TunnelId; import net.i2p.data.TunnelId;
import net.i2p.data.i2np.SourceRouteBlock;
import net.i2p.data.i2np.TunnelCreateMessage; 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.data.i2np.TunnelCreateStatusMessage;
import net.i2p.router.Job;
import net.i2p.router.JobImpl; import net.i2p.router.JobImpl;
import net.i2p.router.MessageSelector;
import net.i2p.router.ReplyJob;
import net.i2p.router.RouterContext; import net.i2p.router.RouterContext;
import net.i2p.router.TunnelInfo; import net.i2p.router.TunnelInfo;
import net.i2p.router.TunnelSettings; import net.i2p.router.TunnelSettings;
import net.i2p.router.TunnelSelectionCriteria;
import net.i2p.router.message.BuildTestMessageJob; 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; import net.i2p.util.Log;
public class HandleTunnelCreateMessageJob extends JobImpl { public class HandleTunnelCreateMessageJob extends JobImpl {
@ -30,20 +42,18 @@ public class HandleTunnelCreateMessageJob extends JobImpl {
private TunnelCreateMessage _message; private TunnelCreateMessage _message;
private RouterIdentity _from; private RouterIdentity _from;
private Hash _fromHash; 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 long TIMEOUT = 30*1000; // 30 secs to contact a peer that will be our next hop
private final static int PRIORITY = 123; private final static int PRIORITY = 123;
HandleTunnelCreateMessageJob(RouterContext ctx, TunnelCreateMessage receivedMessage, HandleTunnelCreateMessageJob(RouterContext ctx, TunnelCreateMessage receivedMessage,
RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) { RouterIdentity from, Hash fromHash) {
super(ctx); super(ctx);
_log = ctx.logManager().getLog(HandleTunnelCreateMessageJob.class); _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 }); 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; _message = receivedMessage;
_from = from; _from = from;
_fromHash = fromHash; _fromHash = fromHash;
_replyBlock = replyBlock;
} }
public void runJob() { 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) { private void sendReply(boolean ok) {
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending reply to a tunnel create of id " + _message.getTunnelId() _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(), getContext().messageHistory().receiveTunnelCreate(_message.getTunnelId(), _message.getNextRouter(),
new Date(getContext().clock().now() + 1000*_message.getTunnelDurationSeconds()), new Date(getContext().clock().now() + 1000*_message.getTunnelDurationSeconds()),
ok, _message.getReplyBlock().getRouter()); ok, _message.getReplyPeer());
TunnelCreateStatusMessage msg = new TunnelCreateStatusMessage(getContext()); TunnelCreateStatusMessage msg = new TunnelCreateStatusMessage(getContext());
msg.setFromHash(getContext().routerHash()); msg.setFromHash(getContext().routerHash());
@ -139,10 +150,88 @@ public class HandleTunnelCreateMessageJob extends JobImpl {
msg.setStatus(TunnelCreateStatusMessage.STATUS_FAILED_OVERLOADED); msg.setStatus(TunnelCreateStatusMessage.STATUS_FAILED_OVERLOADED);
} }
msg.setMessageExpiration(new Date(getContext().clock().now()+60*1000)); 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); 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"; } public String getName() { return "Handle Tunnel Create Message"; }
private class JoinJob extends JobImpl { private class JoinJob extends JobImpl {

View File

@ -28,7 +28,6 @@ import net.i2p.data.i2np.DeliveryInstructions;
import net.i2p.data.i2np.DeliveryStatusMessage; import net.i2p.data.i2np.DeliveryStatusMessage;
import net.i2p.data.i2np.GarlicMessage; import net.i2p.data.i2np.GarlicMessage;
import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.SourceRouteBlock;
import net.i2p.data.i2np.TunnelCreateMessage; import net.i2p.data.i2np.TunnelCreateMessage;
import net.i2p.data.i2np.TunnelCreateStatusMessage; import net.i2p.data.i2np.TunnelCreateStatusMessage;
import net.i2p.router.Job; import net.i2p.router.Job;
@ -163,18 +162,8 @@ public class RequestTunnelJob extends JobImpl {
return; 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 // select inbound tunnel gateway
TunnelGateway inboundGateway = selectInboundGateway(participant, replyPeer); TunnelGateway inboundGateway = selectInboundGateway(participant);
if (inboundGateway == null) { if (inboundGateway == null) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR))
_log.error("Unable to find an inbound gateway"); _log.error("Unable to find an inbound gateway");
@ -187,7 +176,7 @@ public class RequestTunnelJob extends JobImpl {
PublicKey wrappedTo = new PublicKey(); PublicKey wrappedTo = new PublicKey();
RequestState state = new RequestState(wrappedKey, wrappedTags, wrappedTo, RequestState state = new RequestState(wrappedKey, wrappedTags, wrappedTo,
participant, inboundGateway, replyPeer, participant, inboundGateway,
outboundTunnel, target); outboundTunnel, target);
Request r = new Request(state); Request r = new Request(state);
getContext().jobQueue().addJob(r); getContext().jobQueue().addJob(r);
@ -217,7 +206,6 @@ public class RequestTunnelJob extends JobImpl {
_state.getOutboundTunnel(), _state.getOutboundTunnel(),
_state.getParticipant().getThisHop(), _state.getParticipant().getThisHop(),
_state.getParticipant().getNextHop(), _state.getParticipant().getNextHop(),
_state.getReplyPeer().getIdentity().getHash(),
_state.getInboundGateway().getTunnelId(), _state.getInboundGateway().getTunnelId(),
_state.getInboundGateway().getGateway()); _state.getInboundGateway().getGateway());
} }
@ -239,54 +227,45 @@ public class RequestTunnelJob extends JobImpl {
private Set _wrappedTags; private Set _wrappedTags;
private PublicKey _wrappedTo; private PublicKey _wrappedTo;
private TunnelCreateMessage _createMsg; private TunnelCreateMessage _createMsg;
private DeliveryStatusMessage _statusMsg;
private GarlicMessage _garlicMessage; private GarlicMessage _garlicMessage;
private TunnelInfo _participant; private TunnelInfo _participant;
private TunnelGateway _inboundGateway; private TunnelGateway _inboundGateway;
private RouterInfo _replyPeer;
private TunnelId _outboundTunnel; private TunnelId _outboundTunnel;
private RouterInfo _target; private RouterInfo _target;
public RequestState(SessionKey wrappedKey, Set wrappedTags, PublicKey wrappedTo, public RequestState(SessionKey wrappedKey, Set wrappedTags, PublicKey wrappedTo,
TunnelInfo participant, TunnelGateway inboundGateway, TunnelInfo participant, TunnelGateway inboundGateway,
RouterInfo replyPeer, TunnelId outboundTunnel, RouterInfo target) { TunnelId outboundTunnel, RouterInfo target) {
_wrappedKey = wrappedKey; _wrappedKey = wrappedKey;
_wrappedTags = wrappedTags; _wrappedTags = wrappedTags;
_wrappedTo = wrappedTo; _wrappedTo = wrappedTo;
_participant = participant; _participant = participant;
_inboundGateway = inboundGateway; _inboundGateway = inboundGateway;
_replyPeer = replyPeer;
_outboundTunnel = outboundTunnel; _outboundTunnel = outboundTunnel;
_target = target; _target = target;
} }
public TunnelId getOutboundTunnel() { return _outboundTunnel; } public TunnelId getOutboundTunnel() { return _outboundTunnel; }
public TunnelInfo getParticipant() { return _participant; } public TunnelInfo getParticipant() { return _participant; }
public RouterInfo getReplyPeer() { return _replyPeer; }
public TunnelGateway getInboundGateway() { return _inboundGateway; } public TunnelGateway getInboundGateway() { return _inboundGateway; }
public boolean doNext() { public boolean doNext() {
if (_createMsg == null) { if (_createMsg == null) {
_createMsg = buildTunnelCreate(_participant, _inboundGateway, _replyPeer); _createMsg = buildTunnelCreate(_participant, _inboundGateway);
return true;
} else if (_statusMsg == null) {
_statusMsg = buildDeliveryStatusMessage();
return true; return true;
} else if (_garlicMessage == null) { } else if (_garlicMessage == null) {
_garlicMessage = buildGarlicMessage(_createMsg, _statusMsg, _replyPeer, _garlicMessage = buildGarlicMessage(_createMsg, _inboundGateway, _target,
_inboundGateway, _target, _wrappedKey, _wrappedKey, _wrappedTags, _wrappedTo);
_wrappedTags, _wrappedTo);
return true; return true;
} else { } else {
// send the GarlicMessage // send the GarlicMessage
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO))
_log.info("Sending tunnel create to " + _target.getIdentity().getHash().toBase64() + _log.info("Sending tunnel create to " + _target.getIdentity().getHash().toBase64() +
" with replies through " + _replyPeer.getIdentity().getHash().toBase64() +
" to inbound gateway " + _inboundGateway.getGateway().toBase64() + " to inbound gateway " + _inboundGateway.getGateway().toBase64() +
" : " + _inboundGateway.getTunnelId().getTunnelId()); " : " + _inboundGateway.getTunnelId().getTunnelId());
ReplyJob onReply = new Success(_participant, _wrappedKey, _wrappedTags, _wrappedTo); ReplyJob onReply = new Success(_participant, _wrappedKey, _wrappedTags, _wrappedTo);
Job onFail = new Failure(_participant, _replyPeer.getIdentity().getHash()); Job onFail = new Failure(_participant);
MessageSelector selector = new Selector(_participant, _statusMsg.getMessageId()); MessageSelector selector = new Selector(_participant);
SendTunnelMessageJob j = new SendTunnelMessageJob(getContext(), _garlicMessage, SendTunnelMessageJob j = new SendTunnelMessageJob(getContext(), _garlicMessage,
_outboundTunnel, _target.getIdentity().getHash(), _outboundTunnel, _target.getIdentity().getHash(),
null, null, onReply, onFail, null, null, onReply, onFail,
@ -330,47 +309,10 @@ public class RequestTunnelJob extends JobImpl {
} }
/** /**
* Select a peer to which the tunnelParticipant will send the SourceRouteReplyMessage * Select an inbound tunnel to receive replies and acks from the participant
* 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
* *
*/ */
private TunnelGateway selectInboundGateway(TunnelInfo participant, RouterInfo replyPeer) { private TunnelGateway selectInboundGateway(TunnelInfo participant) {
TunnelSelectionCriteria criteria = new TunnelSelectionCriteria(); TunnelSelectionCriteria criteria = new TunnelSelectionCriteria();
criteria.setAnonymityPriority(66); criteria.setAnonymityPriority(66);
criteria.setReliabilityPriority(66); criteria.setReliabilityPriority(66);
@ -408,7 +350,7 @@ public class RequestTunnelJob extends JobImpl {
/** /**
* Build a TunnelCreateMessage to the participant * 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()); TunnelCreateMessage msg = new TunnelCreateMessage(getContext());
msg.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null)); msg.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
msg.setConfigurationKey(participant.getConfigurationKey()); msg.setConfigurationKey(participant.getConfigurationKey());
@ -418,6 +360,9 @@ public class RequestTunnelJob extends JobImpl {
msg.setMaxPeakBytesPerMin(participant.getSettings().getBytesPerMinutePeak()); msg.setMaxPeakBytesPerMin(participant.getSettings().getBytesPerMinutePeak());
msg.setMaxPeakMessagesPerMin(participant.getSettings().getMessagesPerMinutePeak()); msg.setMaxPeakMessagesPerMin(participant.getSettings().getMessagesPerMinutePeak());
msg.setNextRouter(participant.getNextHop()); 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) if (participant.getNextHop() == null)
msg.setParticipantType(TunnelCreateMessage.PARTICIPANT_TYPE_ENDPOINT); msg.setParticipantType(TunnelCreateMessage.PARTICIPANT_TYPE_ENDPOINT);
else if (participant.getSigningKey() != null) else if (participant.getSigningKey() != null)
@ -426,11 +371,19 @@ public class RequestTunnelJob extends JobImpl {
msg.setParticipantType(TunnelCreateMessage.PARTICIPANT_TYPE_OTHER); msg.setParticipantType(TunnelCreateMessage.PARTICIPANT_TYPE_OTHER);
msg.setReorderMessages(participant.getSettings().getReorder()); 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(); long duration = participant.getSettings().getExpiration() - getContext().clock().now();
if (duration == 0) duration = 1; if (duration == 0) duration = 1;
msg.setTunnelDurationSeconds(duration/1000); msg.setTunnelDurationSeconds(duration/1000);
@ -442,97 +395,16 @@ public class RequestTunnelJob extends JobImpl {
return msg; 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 * 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 * through the target, where the data is destined.
* to the replyPeer, where it is then sent down the replyTunnel to the local router.
* *
*/ */
private GarlicMessage buildGarlicMessage(I2NPMessage data, I2NPMessage status, private GarlicMessage buildGarlicMessage(I2NPMessage data,
RouterInfo replyPeer, TunnelGateway replyTunnel, TunnelGateway replyTunnel,
RouterInfo target, SessionKey wrappedKey, RouterInfo target, SessionKey wrappedKey,
Set wrappedTags, PublicKey wrappedTo) { Set wrappedTags, PublicKey wrappedTo) {
GarlicConfig config = buildGarlicConfig(data, status, replyPeer, replyTunnel, target); GarlicConfig config = buildGarlicConfig(data, replyTunnel, target);
PublicKey rcptKey = config.getRecipientPublicKey(); PublicKey rcptKey = config.getRecipientPublicKey();
if (rcptKey == null) { if (rcptKey == null) {
@ -562,16 +434,13 @@ public class RequestTunnelJob extends JobImpl {
return message; return message;
} }
private GarlicConfig buildGarlicConfig(I2NPMessage data, I2NPMessage status, private GarlicConfig buildGarlicConfig(I2NPMessage data,
RouterInfo replyPeer, TunnelGateway replyTunnel, TunnelGateway replyTunnel, RouterInfo target) {
RouterInfo target) {
GarlicConfig config = new GarlicConfig(); GarlicConfig config = new GarlicConfig();
long garlicExpiration = getContext().clock().now() + _timeoutMs; long garlicExpiration = getContext().clock().now() + _timeoutMs;
PayloadGarlicConfig dataClove = buildDataClove(data, target, garlicExpiration); PayloadGarlicConfig dataClove = buildDataClove(data, target, garlicExpiration);
config.addClove(dataClove); config.addClove(dataClove);
PayloadGarlicConfig ackClove = buildAckClove(status, replyPeer, replyTunnel, garlicExpiration);
config.addClove(ackClove);
DeliveryInstructions instructions = new DeliveryInstructions(); DeliveryInstructions instructions = new DeliveryInstructions();
instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_ROUTER); 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.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE));
config.setExpiration(garlicExpiration); config.setExpiration(garlicExpiration);
config.setRecipientPublicKey(target.getIdentity().getPublicKey()); config.setRecipientPublicKey(target.getIdentity().getPublicKey());
config.setRequestAck(false);
return config; 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) * 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 class Failure extends JobImpl {
private TunnelInfo _tunnel; private TunnelInfo _tunnel;
private Hash _replyThrough;
private long _started; private long _started;
public Failure(TunnelInfo tunnel, Hash replyThrough) { public Failure(TunnelInfo tunnel) {
super(RequestTunnelJob.this.getContext()); super(RequestTunnelJob.this.getContext());
_tunnel = tunnel; _tunnel = tunnel;
_replyThrough = replyThrough;
_started = getContext().clock().now(); _started = getContext().clock().now();
} }
@ -829,9 +666,8 @@ public class RequestTunnelJob extends JobImpl {
} }
synchronized (_failedTunnelParticipants) { synchronized (_failedTunnelParticipants) {
_failedTunnelParticipants.add(_tunnel.getThisHop()); _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; long responseTime = getContext().clock().now() - _started;
// perhaps not an explicit reject, but an implicit one (due to dropped messages, tunnel failure, etc) // perhaps not an explicit reject, but an implicit one (due to dropped messages, tunnel failure, etc)
getContext().profileManager().tunnelRejected(_tunnel.getThisHop(), responseTime, false); getContext().profileManager().tunnelRejected(_tunnel.getThisHop(), responseTime, false);
@ -843,26 +679,20 @@ public class RequestTunnelJob extends JobImpl {
private class Selector implements MessageSelector { private class Selector implements MessageSelector {
private TunnelInfo _tunnel; private TunnelInfo _tunnel;
private long _ackId;
private boolean _statusFound; private boolean _statusFound;
private boolean _ackFound;
private long _attemptExpiration; private long _attemptExpiration;
public Selector(TunnelInfo tunnel, long ackId) { public Selector(TunnelInfo tunnel) {
_tunnel = tunnel; _tunnel = tunnel;
_ackId = ackId;
_statusFound = false; _statusFound = false;
_ackFound = false;
_attemptExpiration = getContext().clock().now() + _timeoutMs; _attemptExpiration = getContext().clock().now() + _timeoutMs;
} }
public boolean continueMatching() { public boolean continueMatching() {
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("ContinueMatching looking for tunnel " + _tunnel.getTunnelId().getTunnelId() _log.debug("ContinueMatching looking for tunnel " + _tunnel.getTunnelId().getTunnelId()
+ " from " + _tunnel.getThisHop().toBase64() + ": found? " + _statusFound + " from " + _tunnel.getThisHop().toBase64() + ": found? " + _statusFound);
+ " ackFound? " + _ackFound); return !_statusFound;
return !_statusFound || !_ackFound;
//return !_statusFound; // who cares about the ack if we get the status OK?
} }
public long getExpiration() { return _attemptExpiration; } public long getExpiration() { return _attemptExpiration; }
public boolean isMatch(I2NPMessage message) { public boolean isMatch(I2NPMessage message) {
@ -890,17 +720,6 @@ public class RequestTunnelJob extends JobImpl {
+ _tunnel.getThisHop().toBase64() + "]"); + _tunnel.getThisHop().toBase64() + "]");
return false; 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 { } else {
//_log.debug("Message " + message.getClass().getName() //_log.debug("Message " + message.getClass().getName()
// + " is not a delivery status or tunnel create status message [waiting for ok for tunnel " // + " 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() { public String toString() {
return "Build Tunnel Job Selector for tunnel " + _tunnel.getTunnelId().getTunnelId() return "Build Tunnel Job Selector for tunnel " + _tunnel.getTunnelId().getTunnelId()
+ " at " + _tunnel.getThisHop().toBase64() + " [found=" + _statusFound + ", ack=" + " at " + _tunnel.getThisHop().toBase64() + " [found=" + _statusFound + "] (@"
+ _ackFound + "] (@" + (new Date(getExpiration())) + ")"; + (new Date(getExpiration())) + ")";
} }
} }
} }

View File

@ -11,7 +11,6 @@ package net.i2p.router.tunnelmanager;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.RouterIdentity; import net.i2p.data.RouterIdentity;
import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.SourceRouteBlock;
import net.i2p.data.i2np.TunnelCreateMessage; import net.i2p.data.i2np.TunnelCreateMessage;
import net.i2p.router.HandlerJobBuilder; import net.i2p.router.HandlerJobBuilder;
import net.i2p.router.Job; import net.i2p.router.Job;
@ -22,8 +21,8 @@ class TunnelCreateMessageHandler implements HandlerJobBuilder {
public TunnelCreateMessageHandler(RouterContext context) { public TunnelCreateMessageHandler(RouterContext context) {
_context = context; _context = context;
} }
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) { public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) {
return new HandleTunnelCreateMessageJob(_context, (TunnelCreateMessage)receivedMessage, from, fromHash, replyBlock); return new HandleTunnelCreateMessageJob(_context, (TunnelCreateMessage)receivedMessage, from, fromHash);
} }
} }