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];
int read = DataHelper.read(in, compressed);
if (read != compressedSize)
throw new I2NPMessageException("Invalid compressed data size");
throw new I2NPMessageException("Invalid compressed data size (expected "
+ compressedSize + " read " + read + ")");
ByteArrayInputStream bais = new ByteArrayInputStream(DataHelper.decompress(compressed));
_info.readBytes(bais);
} else {

View File

@ -34,25 +34,8 @@ public class GarlicClove extends DataStructureImpl {
private long _cloveId;
private Date _expiration;
private Certificate _certificate;
private int _replyAction;
private SourceRouteBlock _sourceRouteBlock;
private I2NPMessageHandler _handler;
/** No action requested with the source route block */
public final static int ACTION_NONE = 0;
/**
* A DeliveryStatusMessage is requested with the source route block using
* the cloveId as the id received
*
*/
public final static int ACTION_STATUS = 1;
/**
* No DeliveryStatusMessage is requested, but the source route block is
* included for message specific replies
*
*/
public final static int ACTION_MESSAGE_SPECIFIC = 2;
public GarlicClove(RouterContext context) {
_context = context;
_log = context.logManager().getLog(GarlicClove.class);
@ -62,8 +45,6 @@ public class GarlicClove extends DataStructureImpl {
setCloveId(-1);
setExpiration(null);
setCertificate(null);
setSourceRouteBlockAction(ACTION_NONE);
setSourceRouteBlock(null);
}
public DeliveryInstructions getInstructions() { return _instructions; }
@ -76,10 +57,6 @@ public class GarlicClove extends DataStructureImpl {
public void setExpiration(Date exp) { _expiration = exp; }
public Certificate getCertificate() { return _certificate; }
public void setCertificate(Certificate cert) { _certificate = cert; }
public int getSourceRouteBlockAction() { return _replyAction; }
public void setSourceRouteBlockAction(int action) { _replyAction = action; }
public SourceRouteBlock getSourceRouteBlock() { return _sourceRouteBlock; }
public void setSourceRouteBlock(SourceRouteBlock block) { _sourceRouteBlock = block; }
public void readBytes(InputStream in) throws DataFormatException, IOException {
_instructions = new DeliveryInstructions();
@ -99,12 +76,6 @@ public class GarlicClove extends DataStructureImpl {
_certificate.readBytes(in);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read cert: " + _certificate);
int replyStyle = (int)DataHelper.readLong(in, 1);
setSourceRouteBlockAction(replyStyle);
if (replyStyle != ACTION_NONE) {
_sourceRouteBlock = new SourceRouteBlock();
_sourceRouteBlock.readBytes(in);
}
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
@ -119,12 +90,10 @@ public class GarlicClove extends DataStructureImpl {
error.append("Expiration is null ");
if (_certificate == null)
error.append("Certificate is null ");
if (_replyAction < 0)
error.append("Reply action is < 0 [").append(_replyAction).append("] ");;
if (error.length() > 0)
throw new DataFormatException(error.toString());
if ( (_replyAction != 0) && (_sourceRouteBlock == null) )
throw new DataFormatException("Source route block must be specified for non-null action");
_instructions.writeBytes(out);
if (_log.shouldLog(Log.DEBUG))
@ -138,9 +107,6 @@ public class GarlicClove extends DataStructureImpl {
_certificate.writeBytes(out);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Written cert: " + _certificate);
DataHelper.writeLong(out, 1, _replyAction);
if ( (_replyAction != 0) && (_sourceRouteBlock != null) )
_sourceRouteBlock.writeBytes(out);
}
public boolean equals(Object obj) {
@ -151,9 +117,7 @@ public class GarlicClove extends DataStructureImpl {
DataHelper.eq(getCloveId(), clove.getCloveId()) &&
DataHelper.eq(getData(), clove.getData()) &&
DataHelper.eq(getExpiration(), clove.getExpiration()) &&
DataHelper.eq(getInstructions(), clove.getInstructions()) &&
DataHelper.eq(getSourceRouteBlock(), clove.getSourceRouteBlock()) &&
(getSourceRouteBlockAction() == clove.getSourceRouteBlockAction());
DataHelper.eq(getInstructions(), clove.getInstructions());
}
public int hashCode() {
@ -161,9 +125,7 @@ public class GarlicClove extends DataStructureImpl {
(int)getCloveId() +
DataHelper.hashCode(getData()) +
DataHelper.hashCode(getExpiration()) +
DataHelper.hashCode(getInstructions()) +
DataHelper.hashCode(getSourceRouteBlock()) +
getSourceRouteBlockAction();
DataHelper.hashCode(getInstructions());
}
public String toString() {
@ -173,8 +135,6 @@ public class GarlicClove extends DataStructureImpl {
buf.append("\n\tCertificate: ").append(getCertificate());
buf.append("\n\tClove ID: ").append(getCloveId());
buf.append("\n\tExpiration: ").append(getExpiration());
buf.append("\n\tSource route style: ").append(getSourceRouteBlockAction());
buf.append("\n\tSource route block: ").append(getSourceRouteBlock());
buf.append("\n\tData: ").append(getData());
buf.append("]");
return buf.toString();

View File

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

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

View File

@ -11,7 +11,6 @@ package net.i2p.router;
import net.i2p.data.Hash;
import net.i2p.data.RouterIdentity;
import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.SourceRouteBlock;
/**
* Defines a class that builds jobs to handle a particular message - these
@ -27,9 +26,8 @@ public interface HandlerJobBuilder {
* @param receivedMessage I2NP message received
* @param from router that sent the message (if available)
* @param fromHash hash of the routerIdentity of the router that sent the message (if available)
* @param replyBlock block with which a reply could be sent (if available)
* @return a job or null if no particular job is appropriate (in which case,
* the message should go into the inbound message pool)
*/
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock);
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash);
}

View File

@ -11,7 +11,6 @@ package net.i2p.router;
import net.i2p.data.Hash;
import net.i2p.data.RouterIdentity;
import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.SourceRouteBlock;
/**
* Wrap an I2NP message received from the network prior to handling and processing.
@ -22,7 +21,6 @@ public class InNetMessage {
private I2NPMessage _message;
private RouterIdentity _fromRouter;
private Hash _fromRouterHash;
private SourceRouteBlock _replyBlock;
private long _created;
public InNetMessage(RouterContext context) {
@ -30,7 +28,6 @@ public class InNetMessage {
setMessage(null);
setFromRouter(null);
setFromRouterHash(null);
setReplyBlock(null);
context.messageStateMonitor().inboundMessageAdded();
_created = context.clock().now();
_context.statManager().createRateStat("inNetMessage.timeToDiscard",
@ -59,15 +56,6 @@ public class InNetMessage {
public RouterIdentity getFromRouter() { return _fromRouter; }
public void setFromRouter(RouterIdentity router) { _fromRouter = router; }
/**
* Retrieve any source route block supplied with this message for replies
*
* @return source route block, or null if it was not supplied /or/ if it was already
* used in an ack
*/
public SourceRouteBlock getReplyBlock() { return _replyBlock; }
public void setReplyBlock(SourceRouteBlock block) { _replyBlock = block; }
/**
* Call this after we're done dealing with this message (when we no
* longer need its data)

View File

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

View File

@ -19,7 +19,6 @@ import java.util.Iterator;
import java.util.SortedMap;
import java.util.TreeMap;
import net.i2p.router.message.HandleSourceRouteReplyMessageJob;
import net.i2p.router.networkdb.HandleDatabaseLookupMessageJob;
import net.i2p.router.tunnelmanager.HandleTunnelCreateMessageJob;
import net.i2p.router.tunnelmanager.RequestTunnelJob;
@ -221,14 +220,6 @@ public class JobQueue {
if (!_allowParallelOperation) return false; // dont drop during startup [duh]
Class cls = job.getClass();
if (numReady > _maxWaitingJobs) {
// heavy cpu load, plus we're allowed to be unreliable with these two
// [but garlics can contain our payloads, so lets not drop them]
//if (cls == HandleGarlicMessageJob.class)
// return true;
if (cls == HandleSourceRouteReplyMessageJob.class)
return true;
// lets not try to drop too many tunnel messages...
//if (cls == HandleTunnelMessageJob.class)
// return true;

View File

@ -128,11 +128,10 @@ public class MessageHistory {
* @param outTunnel tunnel we are sending this request out
* @param peerRequested peer asked to participate in the tunnel
* @param nextPeer who peerRequested should forward messages to (or null if it is the endpoint)
* @param sourceRoutePeer to whom peerRequested should forward its TunnelCreateStatusMessage through
* @param replyTunnel the tunnel sourceRoutePeer should forward the source routed message to
* @param replyThrough the gateway of the tunnel that the sourceRoutePeer will be sending to
*/
public void requestTunnelCreate(TunnelId createTunnel, TunnelId outTunnel, Hash peerRequested, Hash nextPeer, Hash sourceRoutePeer, TunnelId replyTunnel, Hash replyThrough) {
public void requestTunnelCreate(TunnelId createTunnel, TunnelId outTunnel, Hash peerRequested, Hash nextPeer, TunnelId replyTunnel, Hash replyThrough) {
if (!_doLog) return;
StringBuffer buf = new StringBuffer(128);
buf.append(getPrefix());
@ -142,8 +141,6 @@ public class MessageHistory {
buf.append("(next [").append(getName(nextPeer)).append("]) ");
if (outTunnel != null)
buf.append("via [").append(outTunnel.getTunnelId()).append("] ");
if (sourceRoutePeer != null)
buf.append("with replies routed through [").append(getName(sourceRoutePeer)).append("] ");
if ( (replyTunnel != null) && (replyThrough != null) )
buf.append("who forwards it through [").append(replyTunnel.getTunnelId()).append("] on [").append(getName(replyThrough)).append("]");
addEntry(buf.toString());
@ -258,15 +255,13 @@ public class MessageHistory {
* of a timeout or an explicit refusal).
*
*/
public void tunnelRequestTimedOut(Hash peer, TunnelId tunnel, Hash replyThrough) {
public void tunnelRequestTimedOut(Hash peer, TunnelId tunnel) {
if (!_doLog) return;
if ( (tunnel == null) || (peer == null) ) return;
StringBuffer buf = new StringBuffer(128);
buf.append(getPrefix());
buf.append("tunnel [").append(tunnel.getTunnelId()).append("] timed out on [");
buf.append(getName(peer)).append("]");
if (replyThrough != null)
buf.append(" with their reply intended to come through [").append(getName(replyThrough)).append("]");
addEntry(buf.toString());
}

View File

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

View File

@ -22,7 +22,6 @@ import net.i2p.data.SessionTag;
import net.i2p.data.i2np.GarlicClove;
import net.i2p.data.i2np.GarlicMessage;
import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.SourceRouteBlock;
import net.i2p.router.RouterContext;
import net.i2p.util.Log;
@ -36,9 +35,6 @@ public class GarlicMessageBuilder {
}
public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags) {
Log log = ctx.logManager().getLog(GarlicMessageBuilder.class);
if (config == null)
throw new IllegalArgumentException("Null config specified");
PublicKey key = config.getRecipientPublicKey();
if (key == null) {
if (config.getRecipient() == null) {
@ -50,19 +46,15 @@ public class GarlicMessageBuilder {
} else
key = config.getRecipient().getIdentity().getPublicKey();
}
GarlicMessage msg = new GarlicMessage(ctx);
noteWrap(ctx, msg, config);
if (log.shouldLog(Log.INFO))
log.info("Encrypted with public key " + key + " to expire on " + new Date(config.getExpiration()));
byte cloveSet[] = buildCloveSet(ctx, config);
SessionKey curKey = ctx.sessionKeyManager().getCurrentKey(key);
if (curKey == null)
curKey = ctx.sessionKeyManager().createSession(key);
wrappedKey.setData(curKey.getData());
SessionTag curTag = ctx.sessionKeyManager().consumeNextAvailableTag(key, curKey);
int availTags = ctx.sessionKeyManager().getAvailableTags(key, curKey);
if (log.shouldLog(Log.DEBUG))
@ -83,8 +75,34 @@ public class GarlicMessageBuilder {
// always tack on at least one more - not necessary.
//wrappedTags.add(new SessionTag(true));
}
SessionTag curTag = ctx.sessionKeyManager().consumeNextAvailableTag(key, curKey);
byte encData[] = ctx.elGamalAESEngine().encrypt(cloveSet, key, curKey, wrappedTags, curTag, 256);
wrappedKey.setData(curKey.getData());
return buildMessage(ctx, config, wrappedKey, wrappedTags, key, curKey, curTag);
}
/**
* @param ctx scope
* @param config how/what to wrap
* @param wrappedKey output parameter that will be filled with the sessionKey used
* @param wrappedTags output parameter that will be filled with the sessionTags used
* @param target public key of the location being garlic routed to (may be null if we
* know the encryptKey and encryptTag)
* @param encryptKey sessionKey used to encrypt the current message
* @param encryptTag sessionTag used to encrypt the current message
*/
public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags, PublicKey target, SessionKey encryptKey, SessionTag encryptTag) {
Log log = ctx.logManager().getLog(GarlicMessageBuilder.class);
if (config == null)
throw new IllegalArgumentException("Null config specified");
GarlicMessage msg = new GarlicMessage(ctx);
noteWrap(ctx, msg, config);
byte cloveSet[] = buildCloveSet(ctx, config);
byte encData[] = ctx.elGamalAESEngine().encrypt(cloveSet, target, encryptKey, wrappedTags, encryptTag, 128);
msg.setData(encData);
Date exp = new Date(config.getExpiration());
msg.setMessageExpiration(exp);
@ -168,46 +186,8 @@ public class GarlicMessageBuilder {
clove.setCloveId(config.getId());
clove.setExpiration(new Date(config.getExpiration()));
clove.setInstructions(config.getDeliveryInstructions());
specifySourceRouteBlock(ctx, clove, config);
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
clove.writeBytes(baos);
return baos.toByteArray();
}
private static void specifySourceRouteBlock(RouterContext ctx, GarlicClove clove, GarlicConfig config) throws DataFormatException {
Log log = ctx.logManager().getLog(GarlicMessageBuilder.class);
boolean includeBlock = false;
if (config.getRequestAck()) {
clove.setSourceRouteBlockAction(GarlicClove.ACTION_STATUS);
includeBlock = true;
} else if (config.getReplyInstructions() != null) {
clove.setSourceRouteBlockAction(GarlicClove.ACTION_MESSAGE_SPECIFIC);
includeBlock = true;
} else {
clove.setSourceRouteBlockAction(GarlicClove.ACTION_NONE);
}
if (includeBlock) {
log.debug("Specifying source route block");
SessionKey replySessionKey = ctx.keyGenerator().generateSessionKey();
SessionTag tag = new SessionTag(true);
// make it so we'll read the session tag correctly and use the right session key
HashSet tags = new HashSet(1);
tags.add(tag);
ctx.sessionKeyManager().tagsReceived(replySessionKey, tags);
SourceRouteBlock block = new SourceRouteBlock();
PublicKey pk = config.getReplyThroughRouter().getIdentity().getPublicKey();
block.setData(ctx, config.getReplyInstructions(), config.getReplyBlockMessageId(),
config.getReplyBlockCertificate(), config.getReplyBlockExpiration(), pk);
block.setRouter(config.getReplyThroughRouter().getIdentity().getHash());
block.setKey(replySessionKey);
block.setTag(tag);
clove.setSourceRouteBlock(block);
} else {
clove.setSourceRouteBlock(null);
}
}
}

View File

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

View File

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

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

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.setFromRouterHash(ident.getHash());
msg.setMessage(_message);
msg.setReplyBlock(null);
getContext().inNetMessagePool().add(msg);
} else {
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.RouterIdentity;
import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.SourceRouteBlock;
import net.i2p.data.i2np.TunnelMessage;
import net.i2p.router.HandlerJobBuilder;
import net.i2p.router.Job;
@ -27,8 +26,7 @@ public class TunnelMessageHandler implements HandlerJobBuilder {
public TunnelMessageHandler(RouterContext context) {
_context = context;
}
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) {
// ignore the replyBlock for now
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) {
HandleTunnelMessageJob job = new HandleTunnelMessageJob(_context, (TunnelMessage)receivedMessage, from, fromHash);
return job;
}

View File

@ -12,7 +12,6 @@ import net.i2p.data.Hash;
import net.i2p.data.RouterIdentity;
import net.i2p.data.i2np.DatabaseLookupMessage;
import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.SourceRouteBlock;
import net.i2p.router.HandlerJobBuilder;
import net.i2p.router.Job;
import net.i2p.router.RouterContext;
@ -32,7 +31,7 @@ public class DatabaseLookupMessageHandler implements HandlerJobBuilder {
_context.statManager().createRateStat("netDb.lookupsDropped", "How many netDb lookups did we drop due to throttling?", "Network Database", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
}
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) {
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) {
_context.statManager().addRateData("netDb.lookupsReceived", 1, 0);
if (_context.throttle().acceptNetDbLookupRequest(((DatabaseLookupMessage)receivedMessage).getSearchKey())) {

View File

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

View File

@ -9,20 +9,32 @@ package net.i2p.router.tunnelmanager;
*/
import java.util.Date;
import java.util.List;
import net.i2p.data.Certificate;
import net.i2p.data.Hash;
import net.i2p.data.RouterIdentity;
import net.i2p.data.RouterInfo;
import net.i2p.data.TunnelId;
import net.i2p.data.i2np.SourceRouteBlock;
import net.i2p.data.i2np.TunnelCreateMessage;
import net.i2p.data.i2np.DeliveryInstructions;
import net.i2p.data.i2np.GarlicClove;
import net.i2p.data.i2np.GarlicMessage;
import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.TunnelCreateStatusMessage;
import net.i2p.router.Job;
import net.i2p.router.JobImpl;
import net.i2p.router.MessageSelector;
import net.i2p.router.ReplyJob;
import net.i2p.router.RouterContext;
import net.i2p.router.TunnelInfo;
import net.i2p.router.TunnelSettings;
import net.i2p.router.TunnelSelectionCriteria;
import net.i2p.router.message.BuildTestMessageJob;
import net.i2p.router.message.SendReplyMessageJob;
import net.i2p.router.message.GarlicConfig;
import net.i2p.router.message.GarlicMessageBuilder;
import net.i2p.router.message.PayloadGarlicConfig;
import net.i2p.router.message.SendTunnelMessageJob;
import net.i2p.util.Log;
public class HandleTunnelCreateMessageJob extends JobImpl {
@ -30,20 +42,18 @@ public class HandleTunnelCreateMessageJob extends JobImpl {
private TunnelCreateMessage _message;
private RouterIdentity _from;
private Hash _fromHash;
private SourceRouteBlock _replyBlock;
private final static long TIMEOUT = 30*1000; // 30 secs to contact a peer that will be our next hop
private final static int PRIORITY = 123;
HandleTunnelCreateMessageJob(RouterContext ctx, TunnelCreateMessage receivedMessage,
RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) {
RouterIdentity from, Hash fromHash) {
super(ctx);
_log = ctx.logManager().getLog(HandleTunnelCreateMessageJob.class);
ctx.statManager().createRateStat("tunnel.rejectOverloaded", "How many tunnels did we deny due to throttling?", "Tunnels", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
_message = receivedMessage;
_from = from;
_fromHash = fromHash;
_replyBlock = replyBlock;
}
public void runJob() {
@ -119,15 +129,16 @@ public class HandleTunnelCreateMessageJob extends JobImpl {
}
}
private static final long REPLY_TIMEOUT = 10*1000;
private void sendReply(boolean ok) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending reply to a tunnel create of id " + _message.getTunnelId()
+ " with ok (" + ok + ") to router " + _message.getReplyBlock().getRouter().toBase64());
+ " with ok (" + ok + ") to tunnel " + _message.getReplyTunnel()
+ " on router " + _message.getReplyPeer());
getContext().messageHistory().receiveTunnelCreate(_message.getTunnelId(), _message.getNextRouter(),
new Date(getContext().clock().now() + 1000*_message.getTunnelDurationSeconds()),
ok, _message.getReplyBlock().getRouter());
ok, _message.getReplyPeer());
TunnelCreateStatusMessage msg = new TunnelCreateStatusMessage(getContext());
msg.setFromHash(getContext().routerHash());
@ -139,10 +150,88 @@ public class HandleTunnelCreateMessageJob extends JobImpl {
msg.setStatus(TunnelCreateStatusMessage.STATUS_FAILED_OVERLOADED);
}
msg.setMessageExpiration(new Date(getContext().clock().now()+60*1000));
SendReplyMessageJob job = new SendReplyMessageJob(getContext(), _message.getReplyBlock(), msg, PRIORITY);
// put that message into a garlic
GarlicMessage reply = createReply(msg);
TunnelId outTunnelId = selectReplyTunnel();
SendTunnelMessageJob job = new SendTunnelMessageJob(getContext(), reply, outTunnelId,
_message.getReplyPeer(),
_message.getReplyTunnel(),
(Job)null, (ReplyJob)null,
(Job)null, (MessageSelector)null,
REPLY_TIMEOUT, PRIORITY);
getContext().jobQueue().addJob(job);
}
private GarlicMessage createReply(TunnelCreateStatusMessage body) {
GarlicConfig cfg = createReplyConfig(body);
return GarlicMessageBuilder.buildMessage(getContext(), cfg, null, null, null,
_message.getReplyKey(), _message.getReplyTag());
}
private GarlicConfig createReplyConfig(TunnelCreateStatusMessage body) {
GarlicConfig config = new GarlicConfig();
PayloadGarlicConfig replyClove = buildReplyClove(body);
config.addClove(replyClove);
DeliveryInstructions instructions = new DeliveryInstructions();
instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_LOCAL);
instructions.setDelayRequested(false);
instructions.setDelaySeconds(0);
instructions.setEncrypted(false);
instructions.setEncryptionKey(null);
instructions.setRouter(null);
instructions.setTunnelId(null);
config.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
config.setDeliveryInstructions(instructions);
config.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE));
config.setExpiration(REPLY_TIMEOUT+getContext().clock().now());
config.setRecipient(null);
config.setRequestAck(false);
return config;
}
/**
* Build a clove that sends the tunnel create reply
*/
private PayloadGarlicConfig buildReplyClove(TunnelCreateStatusMessage body) {
PayloadGarlicConfig replyClove = new PayloadGarlicConfig();
DeliveryInstructions instructions = new DeliveryInstructions();
instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_LOCAL);
instructions.setRouter(null);
instructions.setDelayRequested(false);
instructions.setDelaySeconds(0);
instructions.setEncrypted(false);
replyClove.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
replyClove.setDeliveryInstructions(instructions);
replyClove.setExpiration(REPLY_TIMEOUT+getContext().clock().now());
replyClove.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE));
replyClove.setPayload(body);
replyClove.setRecipient(null);
replyClove.setRequestAck(false);
return replyClove;
}
private TunnelId selectReplyTunnel() {
TunnelSelectionCriteria crit = new TunnelSelectionCriteria();
crit.setMinimumTunnelsRequired(1);
crit.setMaximumTunnelsRequired(1);
List ids = getContext().tunnelManager().selectOutboundTunnelIds(crit);
if ( (ids != null) && (ids.size() > 0) )
return (TunnelId)ids.get(0);
else
return null;
}
public String getName() { return "Handle Tunnel Create Message"; }
private class JoinJob extends JobImpl {

View File

@ -28,7 +28,6 @@ import net.i2p.data.i2np.DeliveryInstructions;
import net.i2p.data.i2np.DeliveryStatusMessage;
import net.i2p.data.i2np.GarlicMessage;
import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.SourceRouteBlock;
import net.i2p.data.i2np.TunnelCreateMessage;
import net.i2p.data.i2np.TunnelCreateStatusMessage;
import net.i2p.router.Job;
@ -163,18 +162,8 @@ public class RequestTunnelJob extends JobImpl {
return;
}
// select reply peer [peer to which SourceRouteReply should be sent, and
// from which the reply will be forwarded to an inbound tunnel]
RouterInfo replyPeer = selectReplyPeer(participant);
if (replyPeer == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("No reply peers available! unable to request a new tunnel!");
fail();
return;
}
// select inbound tunnel gateway
TunnelGateway inboundGateway = selectInboundGateway(participant, replyPeer);
TunnelGateway inboundGateway = selectInboundGateway(participant);
if (inboundGateway == null) {
if (_log.shouldLog(Log.ERROR))
_log.error("Unable to find an inbound gateway");
@ -187,7 +176,7 @@ public class RequestTunnelJob extends JobImpl {
PublicKey wrappedTo = new PublicKey();
RequestState state = new RequestState(wrappedKey, wrappedTags, wrappedTo,
participant, inboundGateway, replyPeer,
participant, inboundGateway,
outboundTunnel, target);
Request r = new Request(state);
getContext().jobQueue().addJob(r);
@ -217,7 +206,6 @@ public class RequestTunnelJob extends JobImpl {
_state.getOutboundTunnel(),
_state.getParticipant().getThisHop(),
_state.getParticipant().getNextHop(),
_state.getReplyPeer().getIdentity().getHash(),
_state.getInboundGateway().getTunnelId(),
_state.getInboundGateway().getGateway());
}
@ -239,54 +227,45 @@ public class RequestTunnelJob extends JobImpl {
private Set _wrappedTags;
private PublicKey _wrappedTo;
private TunnelCreateMessage _createMsg;
private DeliveryStatusMessage _statusMsg;
private GarlicMessage _garlicMessage;
private TunnelInfo _participant;
private TunnelGateway _inboundGateway;
private RouterInfo _replyPeer;
private TunnelId _outboundTunnel;
private RouterInfo _target;
public RequestState(SessionKey wrappedKey, Set wrappedTags, PublicKey wrappedTo,
TunnelInfo participant, TunnelGateway inboundGateway,
RouterInfo replyPeer, TunnelId outboundTunnel, RouterInfo target) {
TunnelId outboundTunnel, RouterInfo target) {
_wrappedKey = wrappedKey;
_wrappedTags = wrappedTags;
_wrappedTo = wrappedTo;
_participant = participant;
_inboundGateway = inboundGateway;
_replyPeer = replyPeer;
_outboundTunnel = outboundTunnel;
_target = target;
}
public TunnelId getOutboundTunnel() { return _outboundTunnel; }
public TunnelInfo getParticipant() { return _participant; }
public RouterInfo getReplyPeer() { return _replyPeer; }
public TunnelGateway getInboundGateway() { return _inboundGateway; }
public boolean doNext() {
if (_createMsg == null) {
_createMsg = buildTunnelCreate(_participant, _inboundGateway, _replyPeer);
return true;
} else if (_statusMsg == null) {
_statusMsg = buildDeliveryStatusMessage();
_createMsg = buildTunnelCreate(_participant, _inboundGateway);
return true;
} else if (_garlicMessage == null) {
_garlicMessage = buildGarlicMessage(_createMsg, _statusMsg, _replyPeer,
_inboundGateway, _target, _wrappedKey,
_wrappedTags, _wrappedTo);
_garlicMessage = buildGarlicMessage(_createMsg, _inboundGateway, _target,
_wrappedKey, _wrappedTags, _wrappedTo);
return true;
} else {
// send the GarlicMessage
if (_log.shouldLog(Log.INFO))
_log.info("Sending tunnel create to " + _target.getIdentity().getHash().toBase64() +
" with replies through " + _replyPeer.getIdentity().getHash().toBase64() +
" to inbound gateway " + _inboundGateway.getGateway().toBase64() +
" : " + _inboundGateway.getTunnelId().getTunnelId());
ReplyJob onReply = new Success(_participant, _wrappedKey, _wrappedTags, _wrappedTo);
Job onFail = new Failure(_participant, _replyPeer.getIdentity().getHash());
MessageSelector selector = new Selector(_participant, _statusMsg.getMessageId());
Job onFail = new Failure(_participant);
MessageSelector selector = new Selector(_participant);
SendTunnelMessageJob j = new SendTunnelMessageJob(getContext(), _garlicMessage,
_outboundTunnel, _target.getIdentity().getHash(),
null, null, onReply, onFail,
@ -330,47 +309,10 @@ public class RequestTunnelJob extends JobImpl {
}
/**
* Select a peer to which the tunnelParticipant will send the SourceRouteReplyMessage
* containing a garlic wrapped TunnelCreateStatusMessage destined for the local router.
*
* Currently just a random peer
*/
private RouterInfo selectReplyPeer(TunnelInfo tunnelParticipant) {
PeerSelectionCriteria criteria = new PeerSelectionCriteria();
criteria.setMaximumRequired(1);
criteria.setMinimumRequired(1);
criteria.setPurpose(PeerSelectionCriteria.PURPOSE_SOURCE_ROUTE);
List peerHashes = getContext().peerManager().selectPeers(criteria);
RouterInfo peerInfo = null;
for (int i = 0; (i < peerHashes.size()) && (peerInfo == null); i++) {
Hash peerHash = (Hash)peerHashes.get(i);
peerInfo = getContext().netDb().lookupRouterInfoLocally(peerHash);
if (peerInfo == null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Selected a peer [" + peerHash + "] we don't have info on locally... trying another");
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Peer [" + peerHash.toBase64() + "] is known locally, keep it in the list of replyPeers");
break;
}
}
if (peerInfo == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("No peers know for a reply (out of " + peerHashes.size() + ") - using ourself");
return getContext().router().getRouterInfo();
} else {
return peerInfo;
}
}
/**
* Select an inbound tunnel to receive replies and acks from the participant by means of the
* replyPeer
* Select an inbound tunnel to receive replies and acks from the participant
*
*/
private TunnelGateway selectInboundGateway(TunnelInfo participant, RouterInfo replyPeer) {
private TunnelGateway selectInboundGateway(TunnelInfo participant) {
TunnelSelectionCriteria criteria = new TunnelSelectionCriteria();
criteria.setAnonymityPriority(66);
criteria.setReliabilityPriority(66);
@ -408,7 +350,7 @@ public class RequestTunnelJob extends JobImpl {
/**
* Build a TunnelCreateMessage to the participant
*/
private TunnelCreateMessage buildTunnelCreate(TunnelInfo participant, TunnelGateway replyGateway, RouterInfo replyPeer) {
private TunnelCreateMessage buildTunnelCreate(TunnelInfo participant, TunnelGateway replyGateway) {
TunnelCreateMessage msg = new TunnelCreateMessage(getContext());
msg.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
msg.setConfigurationKey(participant.getConfigurationKey());
@ -418,6 +360,9 @@ public class RequestTunnelJob extends JobImpl {
msg.setMaxPeakBytesPerMin(participant.getSettings().getBytesPerMinutePeak());
msg.setMaxPeakMessagesPerMin(participant.getSettings().getMessagesPerMinutePeak());
msg.setNextRouter(participant.getNextHop());
// TODO: update the TunnelInfo structure so we can have the tunnel contain
// different tunnelIds per hop
msg.setNextTunnelId(participant.getTunnelId());
if (participant.getNextHop() == null)
msg.setParticipantType(TunnelCreateMessage.PARTICIPANT_TYPE_ENDPOINT);
else if (participant.getSigningKey() != null)
@ -426,11 +371,19 @@ public class RequestTunnelJob extends JobImpl {
msg.setParticipantType(TunnelCreateMessage.PARTICIPANT_TYPE_OTHER);
msg.setReorderMessages(participant.getSettings().getReorder());
SourceRouteBlock replyBlock = buildReplyBlock(replyGateway, replyPeer);
if (replyBlock == null)
return null;
msg.setReplyBlock(replyBlock);
SessionKey replySessionKey = getContext().keyGenerator().generateSessionKey();
SessionTag tag = new SessionTag(true);
Set tags = new HashSet();
tags.add(tag);
// make it so we'll read the session tag correctly and use the right session key
getContext().sessionKeyManager().tagsReceived(replySessionKey, tags);
msg.setReplyPeer(replyGateway.getGateway());
msg.setReplyTunnel(replyGateway.getTunnelId());
msg.setReplyKey(replySessionKey);
msg.setReplyTag(tag);
long duration = participant.getSettings().getExpiration() - getContext().clock().now();
if (duration == 0) duration = 1;
msg.setTunnelDurationSeconds(duration/1000);
@ -442,97 +395,16 @@ public class RequestTunnelJob extends JobImpl {
return msg;
}
/**
* Build a source route block directing the reply through the gateway by means of the
* replyPeer
*
*/
private SourceRouteBlock buildReplyBlock(TunnelGateway gateway, RouterInfo replyPeer) {
if (replyPeer == null) {
if (_log.shouldLog(Log.ERROR))
_log.error("No peer specified for reply!");
return null;
}
SessionKey replySessionKey = getContext().keyGenerator().generateSessionKey();
SessionTag tag = new SessionTag(true);
Set tags = new HashSet();
tags.add(tag);
// make it so we'll read the session tag correctly and use the right session key
getContext().sessionKeyManager().tagsReceived(replySessionKey, tags);
PublicKey pk = replyPeer.getIdentity().getPublicKey();
DeliveryInstructions instructions = new DeliveryInstructions();
instructions.setDelayRequested(false);
instructions.setDelaySeconds(0);
instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_TUNNEL);
instructions.setDestination(null);
instructions.setEncrypted(false);
instructions.setEncryptionKey(null);
instructions.setRouter(gateway.getGateway());
instructions.setTunnelId(gateway.getTunnelId());
long replyId = getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE);
Certificate replyCert = new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null);
long expiration = getContext().clock().now() + _timeoutMs; // _expiration;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Setting the expiration on the reply block to " + (new Date(expiration)));
SourceRouteBlock block = new SourceRouteBlock();
try {
long begin = getContext().clock().now();
block.setData(getContext(), instructions, replyId, replyCert, expiration, pk);
long end = getContext().clock().now();
if ( (end - begin) > 1000) {
if (_log.shouldLog(Log.WARN))
_log.warn("Took too long (" + (end-begin) + "ms) to build source route block");
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("did NOT take long (" + (end-begin) + "ms) to build source route block!");
}
} catch (DataFormatException dfe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error building the reply block", dfe);
return null;
}
block.setRouter(replyPeer.getIdentity().getHash());
block.setKey(replySessionKey);
block.setTag(tag);
return block;
}
/**
* Create a message containing a random id to check for after garlic routing
* it out so that we know the other message in the garlic has been received
*
*/
private DeliveryStatusMessage buildDeliveryStatusMessage() {
DeliveryStatusMessage msg = new DeliveryStatusMessage(getContext());
msg.setArrival(new Date(getContext().clock().now()));
msg.setMessageId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE));
Date exp = new Date(getContext().clock().now() + _timeoutMs); // _expiration);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Setting the expiration on the delivery status message to " + exp);
msg.setMessageExpiration(exp);
return msg;
}
/**
* Build a garlic message wrapping the data and status as cloves with both to be routed
* through the target, where the data is destined. The status however is to continue on
* to the replyPeer, where it is then sent down the replyTunnel to the local router.
* through the target, where the data is destined.
*
*/
private GarlicMessage buildGarlicMessage(I2NPMessage data, I2NPMessage status,
RouterInfo replyPeer, TunnelGateway replyTunnel,
private GarlicMessage buildGarlicMessage(I2NPMessage data,
TunnelGateway replyTunnel,
RouterInfo target, SessionKey wrappedKey,
Set wrappedTags, PublicKey wrappedTo) {
GarlicConfig config = buildGarlicConfig(data, status, replyPeer, replyTunnel, target);
GarlicConfig config = buildGarlicConfig(data, replyTunnel, target);
PublicKey rcptKey = config.getRecipientPublicKey();
if (rcptKey == null) {
@ -562,16 +434,13 @@ public class RequestTunnelJob extends JobImpl {
return message;
}
private GarlicConfig buildGarlicConfig(I2NPMessage data, I2NPMessage status,
RouterInfo replyPeer, TunnelGateway replyTunnel,
RouterInfo target) {
private GarlicConfig buildGarlicConfig(I2NPMessage data,
TunnelGateway replyTunnel, RouterInfo target) {
GarlicConfig config = new GarlicConfig();
long garlicExpiration = getContext().clock().now() + _timeoutMs;
PayloadGarlicConfig dataClove = buildDataClove(data, target, garlicExpiration);
config.addClove(dataClove);
PayloadGarlicConfig ackClove = buildAckClove(status, replyPeer, replyTunnel, garlicExpiration);
config.addClove(ackClove);
DeliveryInstructions instructions = new DeliveryInstructions();
instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_ROUTER);
@ -590,40 +459,10 @@ public class RequestTunnelJob extends JobImpl {
config.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE));
config.setExpiration(garlicExpiration);
config.setRecipientPublicKey(target.getIdentity().getPublicKey());
config.setRequestAck(false);
return config;
}
/**
* Build a clove that sends a DeliveryStatusMessage to us
*/
private PayloadGarlicConfig buildAckClove(I2NPMessage ackMsg, RouterInfo replyPeer,
TunnelGateway replyTunnel, long expiration) {
PayloadGarlicConfig ackClove = new PayloadGarlicConfig();
Hash replyToTunnelRouter = replyTunnel.getGateway(); // inbound tunnel gateway
TunnelId replyToTunnelId = replyTunnel.getTunnelId(); // tunnel id on that gateway
DeliveryInstructions ackInstructions = new DeliveryInstructions();
ackInstructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_TUNNEL);
ackInstructions.setRouter(replyToTunnelRouter);
ackInstructions.setTunnelId(replyToTunnelId);
ackInstructions.setDelayRequested(false);
ackInstructions.setDelaySeconds(0);
ackInstructions.setEncrypted(false);
ackClove.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
ackClove.setDeliveryInstructions(ackInstructions);
ackClove.setExpiration(expiration);
ackClove.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE));
ackClove.setPayload(ackMsg);
ackClove.setRecipient(replyPeer);
ackClove.setRequestAck(false);
return ackClove;
}
/**
* Build a clove that sends the data to the target (which is local)
*/
@ -808,12 +647,10 @@ public class RequestTunnelJob extends JobImpl {
private class Failure extends JobImpl {
private TunnelInfo _tunnel;
private Hash _replyThrough;
private long _started;
public Failure(TunnelInfo tunnel, Hash replyThrough) {
public Failure(TunnelInfo tunnel) {
super(RequestTunnelJob.this.getContext());
_tunnel = tunnel;
_replyThrough = replyThrough;
_started = getContext().clock().now();
}
@ -829,9 +666,8 @@ public class RequestTunnelJob extends JobImpl {
}
synchronized (_failedTunnelParticipants) {
_failedTunnelParticipants.add(_tunnel.getThisHop());
_failedTunnelParticipants.add(_replyThrough);
}
Failure.this.getContext().messageHistory().tunnelRequestTimedOut(_tunnel.getThisHop(), _tunnel.getTunnelId(), _replyThrough);
Failure.this.getContext().messageHistory().tunnelRequestTimedOut(_tunnel.getThisHop(), _tunnel.getTunnelId());
long responseTime = getContext().clock().now() - _started;
// perhaps not an explicit reject, but an implicit one (due to dropped messages, tunnel failure, etc)
getContext().profileManager().tunnelRejected(_tunnel.getThisHop(), responseTime, false);
@ -843,26 +679,20 @@ public class RequestTunnelJob extends JobImpl {
private class Selector implements MessageSelector {
private TunnelInfo _tunnel;
private long _ackId;
private boolean _statusFound;
private boolean _ackFound;
private long _attemptExpiration;
public Selector(TunnelInfo tunnel, long ackId) {
public Selector(TunnelInfo tunnel) {
_tunnel = tunnel;
_ackId = ackId;
_statusFound = false;
_ackFound = false;
_attemptExpiration = getContext().clock().now() + _timeoutMs;
}
public boolean continueMatching() {
if (_log.shouldLog(Log.DEBUG))
_log.debug("ContinueMatching looking for tunnel " + _tunnel.getTunnelId().getTunnelId()
+ " from " + _tunnel.getThisHop().toBase64() + ": found? " + _statusFound
+ " ackFound? " + _ackFound);
return !_statusFound || !_ackFound;
//return !_statusFound; // who cares about the ack if we get the status OK?
+ " from " + _tunnel.getThisHop().toBase64() + ": found? " + _statusFound);
return !_statusFound;
}
public long getExpiration() { return _attemptExpiration; }
public boolean isMatch(I2NPMessage message) {
@ -890,17 +720,6 @@ public class RequestTunnelJob extends JobImpl {
+ _tunnel.getThisHop().toBase64() + "]");
return false;
}
} else if (message.getType() == DeliveryStatusMessage.MESSAGE_TYPE) {
if (((DeliveryStatusMessage)message).getMessageId() == _ackId) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Matches the ping message tied to the tunnel create status message");
_ackFound = true;
return true;
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Message is a delivery status message, but with the wrong id");
return false;
}
} else {
//_log.debug("Message " + message.getClass().getName()
// + " is not a delivery status or tunnel create status message [waiting for ok for tunnel "
@ -911,8 +730,8 @@ public class RequestTunnelJob extends JobImpl {
public String toString() {
return "Build Tunnel Job Selector for tunnel " + _tunnel.getTunnelId().getTunnelId()
+ " at " + _tunnel.getThisHop().toBase64() + " [found=" + _statusFound + ", ack="
+ _ackFound + "] (@" + (new Date(getExpiration())) + ")";
+ " at " + _tunnel.getThisHop().toBase64() + " [found=" + _statusFound + "] (@"
+ (new Date(getExpiration())) + ")";
}
}
}

View File

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