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:
@ -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 {
|
||||
|
@ -34,25 +34,8 @@ public class GarlicClove extends DataStructureImpl {
|
||||
private long _cloveId;
|
||||
private Date _expiration;
|
||||
private Certificate _certificate;
|
||||
private int _replyAction;
|
||||
private SourceRouteBlock _sourceRouteBlock;
|
||||
private I2NPMessageHandler _handler;
|
||||
|
||||
/** No action requested with the source route block */
|
||||
public final static int ACTION_NONE = 0;
|
||||
/**
|
||||
* A DeliveryStatusMessage is requested with the source route block using
|
||||
* the cloveId as the id received
|
||||
*
|
||||
*/
|
||||
public final static int ACTION_STATUS = 1;
|
||||
/**
|
||||
* No DeliveryStatusMessage is requested, but the source route block is
|
||||
* included for message specific replies
|
||||
*
|
||||
*/
|
||||
public final static int ACTION_MESSAGE_SPECIFIC = 2;
|
||||
|
||||
public GarlicClove(RouterContext context) {
|
||||
_context = context;
|
||||
_log = context.logManager().getLog(GarlicClove.class);
|
||||
@ -62,8 +45,6 @@ public class GarlicClove extends DataStructureImpl {
|
||||
setCloveId(-1);
|
||||
setExpiration(null);
|
||||
setCertificate(null);
|
||||
setSourceRouteBlockAction(ACTION_NONE);
|
||||
setSourceRouteBlock(null);
|
||||
}
|
||||
|
||||
public DeliveryInstructions getInstructions() { return _instructions; }
|
||||
@ -76,10 +57,6 @@ public class GarlicClove extends DataStructureImpl {
|
||||
public void setExpiration(Date exp) { _expiration = exp; }
|
||||
public Certificate getCertificate() { return _certificate; }
|
||||
public void setCertificate(Certificate cert) { _certificate = cert; }
|
||||
public int getSourceRouteBlockAction() { return _replyAction; }
|
||||
public void setSourceRouteBlockAction(int action) { _replyAction = action; }
|
||||
public SourceRouteBlock getSourceRouteBlock() { return _sourceRouteBlock; }
|
||||
public void setSourceRouteBlock(SourceRouteBlock block) { _sourceRouteBlock = block; }
|
||||
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
_instructions = new DeliveryInstructions();
|
||||
@ -99,12 +76,6 @@ public class GarlicClove extends DataStructureImpl {
|
||||
_certificate.readBytes(in);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Read cert: " + _certificate);
|
||||
int replyStyle = (int)DataHelper.readLong(in, 1);
|
||||
setSourceRouteBlockAction(replyStyle);
|
||||
if (replyStyle != ACTION_NONE) {
|
||||
_sourceRouteBlock = new SourceRouteBlock();
|
||||
_sourceRouteBlock.readBytes(in);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
||||
@ -119,28 +90,23 @@ public class GarlicClove extends DataStructureImpl {
|
||||
error.append("Expiration is null ");
|
||||
if (_certificate == null)
|
||||
error.append("Certificate is null ");
|
||||
if (_replyAction < 0)
|
||||
error.append("Reply action is < 0 [").append(_replyAction).append("] ");;
|
||||
if (error.length() > 0)
|
||||
throw new DataFormatException(error.toString());
|
||||
if ( (_replyAction != 0) && (_sourceRouteBlock == null) )
|
||||
throw new DataFormatException("Source route block must be specified for non-null action");
|
||||
_instructions.writeBytes(out);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Wrote instructions: " + _instructions);
|
||||
_msg.writeBytes(out);
|
||||
DataHelper.writeLong(out, 4, _cloveId);
|
||||
DataHelper.writeDate(out, _expiration);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("CloveID written: " + _cloveId + " expiration written: "
|
||||
+ _expiration);
|
||||
_certificate.writeBytes(out);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Written cert: " + _certificate);
|
||||
DataHelper.writeLong(out, 1, _replyAction);
|
||||
if ( (_replyAction != 0) && (_sourceRouteBlock != null) )
|
||||
_sourceRouteBlock.writeBytes(out);
|
||||
if (error.length() > 0)
|
||||
throw new DataFormatException(error.toString());
|
||||
|
||||
_instructions.writeBytes(out);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Wrote instructions: " + _instructions);
|
||||
_msg.writeBytes(out);
|
||||
DataHelper.writeLong(out, 4, _cloveId);
|
||||
DataHelper.writeDate(out, _expiration);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("CloveID written: " + _cloveId + " expiration written: "
|
||||
+ _expiration);
|
||||
_certificate.writeBytes(out);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Written cert: " + _certificate);
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
@ -148,22 +114,18 @@ public class GarlicClove extends DataStructureImpl {
|
||||
return false;
|
||||
GarlicClove clove = (GarlicClove)obj;
|
||||
return DataHelper.eq(getCertificate(), clove.getCertificate()) &&
|
||||
DataHelper.eq(getCloveId(), clove.getCloveId()) &&
|
||||
DataHelper.eq(getData(), clove.getData()) &&
|
||||
DataHelper.eq(getExpiration(), clove.getExpiration()) &&
|
||||
DataHelper.eq(getInstructions(), clove.getInstructions()) &&
|
||||
DataHelper.eq(getSourceRouteBlock(), clove.getSourceRouteBlock()) &&
|
||||
(getSourceRouteBlockAction() == clove.getSourceRouteBlockAction());
|
||||
DataHelper.eq(getCloveId(), clove.getCloveId()) &&
|
||||
DataHelper.eq(getData(), clove.getData()) &&
|
||||
DataHelper.eq(getExpiration(), clove.getExpiration()) &&
|
||||
DataHelper.eq(getInstructions(), clove.getInstructions());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return DataHelper.hashCode(getCertificate()) +
|
||||
(int)getCloveId() +
|
||||
DataHelper.hashCode(getData()) +
|
||||
DataHelper.hashCode(getExpiration()) +
|
||||
DataHelper.hashCode(getInstructions()) +
|
||||
DataHelper.hashCode(getSourceRouteBlock()) +
|
||||
getSourceRouteBlockAction();
|
||||
(int)getCloveId() +
|
||||
DataHelper.hashCode(getData()) +
|
||||
DataHelper.hashCode(getExpiration()) +
|
||||
DataHelper.hashCode(getInstructions());
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
@ -173,8 +135,6 @@ public class GarlicClove extends DataStructureImpl {
|
||||
buf.append("\n\tCertificate: ").append(getCertificate());
|
||||
buf.append("\n\tClove ID: ").append(getCloveId());
|
||||
buf.append("\n\tExpiration: ").append(getExpiration());
|
||||
buf.append("\n\tSource route style: ").append(getSourceRouteBlockAction());
|
||||
buf.append("\n\tSource route block: ").append(getSourceRouteBlock());
|
||||
buf.append("\n\tData: ").append(getData());
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
|
@ -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:
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -18,6 +18,8 @@ import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.TunnelId;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.SessionTag;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
@ -30,8 +32,9 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
||||
private final static Log _log = new Log(TunnelCreateMessage.class);
|
||||
public final static int MESSAGE_TYPE = 6;
|
||||
private int _participantType;
|
||||
private Hash _nextRouter;
|
||||
private TunnelId _tunnelId;
|
||||
private Hash _nextRouter;
|
||||
private TunnelId _nextTunnelId;
|
||||
private long _tunnelDuration;
|
||||
private TunnelConfigurationSessionKey _configKey;
|
||||
private long _maxPeakMessagesPerMin;
|
||||
@ -44,7 +47,10 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
||||
private TunnelSigningPrivateKey _verificationPrivKey;
|
||||
private TunnelSessionKey _tunnelKey;
|
||||
private Certificate _certificate;
|
||||
private SourceRouteBlock _replyBlock;
|
||||
private SessionTag _replyTag;
|
||||
private SessionKey _replyKey;
|
||||
private TunnelId _replyTunnel;
|
||||
private Hash _replyPeer;
|
||||
|
||||
public static final int PARTICIPANT_TYPE_GATEWAY = 1;
|
||||
public static final int PARTICIPANT_TYPE_ENDPOINT = 2;
|
||||
@ -57,6 +63,7 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
||||
super(context);
|
||||
setParticipantType(-1);
|
||||
setNextRouter(null);
|
||||
setNextTunnelId(null);
|
||||
setTunnelId(null);
|
||||
setTunnelDurationSeconds(-1);
|
||||
setConfigurationKey(null);
|
||||
@ -70,13 +77,18 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
||||
setVerificationPrivateKey(null);
|
||||
setTunnelKey(null);
|
||||
setCertificate(null);
|
||||
setReplyBlock(null);
|
||||
setReplyTag(null);
|
||||
setReplyKey(null);
|
||||
setReplyTunnel(null);
|
||||
setReplyPeer(null);
|
||||
}
|
||||
|
||||
public void setParticipantType(int type) { _participantType = type; }
|
||||
public int getParticipantType() { return _participantType; }
|
||||
public void setNextRouter(Hash routerIdentityHash) { _nextRouter = routerIdentityHash; }
|
||||
public Hash getNextRouter() { return _nextRouter; }
|
||||
public void setNextTunnelId(TunnelId id) { _nextTunnelId = id; }
|
||||
public TunnelId getNextTunnelId() { return _nextTunnelId; }
|
||||
public void setTunnelId(TunnelId id) { _tunnelId = id; }
|
||||
public TunnelId getTunnelId() { return _tunnelId; }
|
||||
public void setTunnelDurationSeconds(long durationSeconds) { _tunnelDuration = durationSeconds; }
|
||||
@ -103,8 +115,14 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
||||
public TunnelSessionKey getTunnelKey() { return _tunnelKey; }
|
||||
public void setCertificate(Certificate cert) { _certificate = cert; }
|
||||
public Certificate getCertificate() { return _certificate; }
|
||||
public void setReplyBlock(SourceRouteBlock block) { _replyBlock = block; }
|
||||
public SourceRouteBlock getReplyBlock() { return _replyBlock; }
|
||||
public void setReplyTag(SessionTag tag) { _replyTag = tag; }
|
||||
public SessionTag getReplyTag() { return _replyTag; }
|
||||
public void setReplyKey(SessionKey key) { _replyKey = key; }
|
||||
public SessionKey getReplyKey() { return _replyKey; }
|
||||
public void setReplyTunnel(TunnelId id) { _replyTunnel = id; }
|
||||
public TunnelId getReplyTunnel() { return _replyTunnel; }
|
||||
public void setReplyPeer(Hash peer) { _replyPeer = peer; }
|
||||
public Hash getReplyPeer() { return _replyPeer; }
|
||||
|
||||
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
@ -113,6 +131,8 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
||||
if (_participantType != PARTICIPANT_TYPE_ENDPOINT) {
|
||||
_nextRouter = new Hash();
|
||||
_nextRouter.readBytes(in);
|
||||
_nextTunnelId = new TunnelId();
|
||||
_nextTunnelId.readBytes(in);
|
||||
}
|
||||
_tunnelId = new TunnelId();
|
||||
_tunnelId.readBytes(in);
|
||||
@ -140,8 +160,14 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
||||
}
|
||||
_certificate = new Certificate();
|
||||
_certificate.readBytes(in);
|
||||
_replyBlock = new SourceRouteBlock();
|
||||
_replyBlock.readBytes(in);
|
||||
_replyTag = new SessionTag();
|
||||
_replyTag.readBytes(in);
|
||||
_replyKey = new SessionKey();
|
||||
_replyKey.readBytes(in);
|
||||
_replyTunnel = new TunnelId();
|
||||
_replyTunnel.readBytes(in);
|
||||
_replyPeer = new Hash();
|
||||
_replyPeer.readBytes(in);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Unable to load the message data", dfe);
|
||||
}
|
||||
@ -153,6 +179,7 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
||||
DataHelper.writeLong(os, 1, _participantType);
|
||||
if (_participantType != PARTICIPANT_TYPE_ENDPOINT) {
|
||||
_nextRouter.writeBytes(os);
|
||||
_nextTunnelId.writeBytes(os);
|
||||
}
|
||||
_tunnelId.writeBytes(os);
|
||||
DataHelper.writeLong(os, 4, _tunnelDuration);
|
||||
@ -174,7 +201,10 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
||||
_tunnelKey.writeBytes(os);
|
||||
}
|
||||
_certificate.writeBytes(os);
|
||||
_replyBlock.writeBytes(os);
|
||||
_replyTag.writeBytes(os);
|
||||
_replyKey.writeBytes(os);
|
||||
_replyTunnel.writeBytes(os);
|
||||
_replyPeer.writeBytes(os);
|
||||
} catch (Throwable t) {
|
||||
throw new I2NPMessageException("Error writing out the message data", t);
|
||||
}
|
||||
@ -201,21 +231,23 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
||||
|
||||
public int hashCode() {
|
||||
return (int)(DataHelper.hashCode(getCertificate()) +
|
||||
DataHelper.hashCode(getConfigurationKey()) +
|
||||
DataHelper.hashCode(getNextRouter()) +
|
||||
DataHelper.hashCode(getReplyBlock()) +
|
||||
DataHelper.hashCode(getTunnelId()) +
|
||||
DataHelper.hashCode(getTunnelKey()) +
|
||||
DataHelper.hashCode(getVerificationPrivateKey()) +
|
||||
DataHelper.hashCode(getVerificationPublicKey()) +
|
||||
(getIncludeDummyTraffic() ? 1 : 0) +
|
||||
getMaxAvgBytesPerMin() +
|
||||
getMaxAvgMessagesPerMin() +
|
||||
getMaxPeakBytesPerMin() +
|
||||
getMaxPeakMessagesPerMin() +
|
||||
getParticipantType() +
|
||||
(getReorderMessages() ? 1 : 0) +
|
||||
getTunnelDurationSeconds());
|
||||
DataHelper.hashCode(getConfigurationKey()) +
|
||||
DataHelper.hashCode(getNextRouter()) +
|
||||
DataHelper.hashCode(getNextTunnelId()) +
|
||||
DataHelper.hashCode(getReplyPeer()) +
|
||||
DataHelper.hashCode(getReplyTunnel()) +
|
||||
DataHelper.hashCode(getTunnelId()) +
|
||||
DataHelper.hashCode(getTunnelKey()) +
|
||||
DataHelper.hashCode(getVerificationPrivateKey()) +
|
||||
DataHelper.hashCode(getVerificationPublicKey()) +
|
||||
(getIncludeDummyTraffic() ? 1 : 0) +
|
||||
getMaxAvgBytesPerMin() +
|
||||
getMaxAvgMessagesPerMin() +
|
||||
getMaxPeakBytesPerMin() +
|
||||
getMaxPeakMessagesPerMin() +
|
||||
getParticipantType() +
|
||||
(getReorderMessages() ? 1 : 0) +
|
||||
getTunnelDurationSeconds());
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
@ -224,7 +256,11 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
||||
return DataHelper.eq(getCertificate(), msg.getCertificate()) &&
|
||||
DataHelper.eq(getConfigurationKey(), msg.getConfigurationKey()) &&
|
||||
DataHelper.eq(getNextRouter(), msg.getNextRouter()) &&
|
||||
DataHelper.eq(getReplyBlock(), msg.getReplyBlock()) &&
|
||||
DataHelper.eq(getNextTunnelId(), msg.getNextTunnelId()) &&
|
||||
DataHelper.eq(getReplyTag(), msg.getReplyTag()) &&
|
||||
DataHelper.eq(getReplyKey(), msg.getReplyKey()) &&
|
||||
DataHelper.eq(getReplyTunnel(), msg.getReplyTunnel()) &&
|
||||
DataHelper.eq(getReplyPeer(), msg.getReplyPeer()) &&
|
||||
DataHelper.eq(getTunnelId(), msg.getTunnelId()) &&
|
||||
DataHelper.eq(getTunnelKey(), msg.getTunnelKey()) &&
|
||||
DataHelper.eq(getVerificationPrivateKey(), msg.getVerificationPrivateKey()) &&
|
||||
@ -249,7 +285,11 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
||||
buf.append("\n\tCertificate: ").append(getCertificate());
|
||||
buf.append("\n\tConfiguration Key: ").append(getConfigurationKey());
|
||||
buf.append("\n\tNext Router: ").append(getNextRouter());
|
||||
buf.append("\n\tReply Block: ").append(getReplyBlock());
|
||||
buf.append("\n\tNext Tunnel: ").append(getNextTunnelId());
|
||||
buf.append("\n\tReply Tag: ").append(getReplyTag());
|
||||
buf.append("\n\tReply Key: ").append(getReplyKey());
|
||||
buf.append("\n\tReply Tunnel: ").append(getReplyTunnel());
|
||||
buf.append("\n\tReply Peer: ").append(getReplyPeer());
|
||||
buf.append("\n\tTunnel ID: ").append(getTunnelId());
|
||||
buf.append("\n\tTunnel Key: ").append(getTunnelKey());
|
||||
buf.append("\n\tVerification Private Key: ").append(getVerificationPrivateKey());
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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"; }
|
||||
}
|
@ -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"; }
|
||||
}
|
@ -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))
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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())) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -9,20 +9,32 @@ package net.i2p.router.tunnelmanager;
|
||||
*/
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.data.Certificate;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.RouterIdentity;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.TunnelId;
|
||||
import net.i2p.data.i2np.SourceRouteBlock;
|
||||
import net.i2p.data.i2np.TunnelCreateMessage;
|
||||
import net.i2p.data.i2np.DeliveryInstructions;
|
||||
import net.i2p.data.i2np.GarlicClove;
|
||||
import net.i2p.data.i2np.GarlicMessage;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
import net.i2p.data.i2np.TunnelCreateStatusMessage;
|
||||
import net.i2p.router.Job;
|
||||
import net.i2p.router.JobImpl;
|
||||
import net.i2p.router.MessageSelector;
|
||||
import net.i2p.router.ReplyJob;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.TunnelInfo;
|
||||
import net.i2p.router.TunnelSettings;
|
||||
import net.i2p.router.TunnelSelectionCriteria;
|
||||
import net.i2p.router.message.BuildTestMessageJob;
|
||||
import net.i2p.router.message.SendReplyMessageJob;
|
||||
import net.i2p.router.message.GarlicConfig;
|
||||
import net.i2p.router.message.GarlicMessageBuilder;
|
||||
import net.i2p.router.message.PayloadGarlicConfig;
|
||||
import net.i2p.router.message.SendTunnelMessageJob;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public class HandleTunnelCreateMessageJob extends JobImpl {
|
||||
@ -30,20 +42,18 @@ public class HandleTunnelCreateMessageJob extends JobImpl {
|
||||
private TunnelCreateMessage _message;
|
||||
private RouterIdentity _from;
|
||||
private Hash _fromHash;
|
||||
private SourceRouteBlock _replyBlock;
|
||||
|
||||
private final static long TIMEOUT = 30*1000; // 30 secs to contact a peer that will be our next hop
|
||||
private final static int PRIORITY = 123;
|
||||
|
||||
HandleTunnelCreateMessageJob(RouterContext ctx, TunnelCreateMessage receivedMessage,
|
||||
RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) {
|
||||
RouterIdentity from, Hash fromHash) {
|
||||
super(ctx);
|
||||
_log = ctx.logManager().getLog(HandleTunnelCreateMessageJob.class);
|
||||
ctx.statManager().createRateStat("tunnel.rejectOverloaded", "How many tunnels did we deny due to throttling?", "Tunnels", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||
_message = receivedMessage;
|
||||
_from = from;
|
||||
_fromHash = fromHash;
|
||||
_replyBlock = replyBlock;
|
||||
}
|
||||
|
||||
public void runJob() {
|
||||
@ -119,15 +129,16 @@ public class HandleTunnelCreateMessageJob extends JobImpl {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final long REPLY_TIMEOUT = 10*1000;
|
||||
private void sendReply(boolean ok) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Sending reply to a tunnel create of id " + _message.getTunnelId()
|
||||
+ " with ok (" + ok + ") to router " + _message.getReplyBlock().getRouter().toBase64());
|
||||
+ " with ok (" + ok + ") to tunnel " + _message.getReplyTunnel()
|
||||
+ " on router " + _message.getReplyPeer());
|
||||
|
||||
getContext().messageHistory().receiveTunnelCreate(_message.getTunnelId(), _message.getNextRouter(),
|
||||
new Date(getContext().clock().now() + 1000*_message.getTunnelDurationSeconds()),
|
||||
ok, _message.getReplyBlock().getRouter());
|
||||
new Date(getContext().clock().now() + 1000*_message.getTunnelDurationSeconds()),
|
||||
ok, _message.getReplyPeer());
|
||||
|
||||
TunnelCreateStatusMessage msg = new TunnelCreateStatusMessage(getContext());
|
||||
msg.setFromHash(getContext().routerHash());
|
||||
@ -139,10 +150,88 @@ public class HandleTunnelCreateMessageJob extends JobImpl {
|
||||
msg.setStatus(TunnelCreateStatusMessage.STATUS_FAILED_OVERLOADED);
|
||||
}
|
||||
msg.setMessageExpiration(new Date(getContext().clock().now()+60*1000));
|
||||
SendReplyMessageJob job = new SendReplyMessageJob(getContext(), _message.getReplyBlock(), msg, PRIORITY);
|
||||
|
||||
// put that message into a garlic
|
||||
GarlicMessage reply = createReply(msg);
|
||||
|
||||
TunnelId outTunnelId = selectReplyTunnel();
|
||||
|
||||
SendTunnelMessageJob job = new SendTunnelMessageJob(getContext(), reply, outTunnelId,
|
||||
_message.getReplyPeer(),
|
||||
_message.getReplyTunnel(),
|
||||
(Job)null, (ReplyJob)null,
|
||||
(Job)null, (MessageSelector)null,
|
||||
REPLY_TIMEOUT, PRIORITY);
|
||||
getContext().jobQueue().addJob(job);
|
||||
}
|
||||
|
||||
private GarlicMessage createReply(TunnelCreateStatusMessage body) {
|
||||
GarlicConfig cfg = createReplyConfig(body);
|
||||
return GarlicMessageBuilder.buildMessage(getContext(), cfg, null, null, null,
|
||||
_message.getReplyKey(), _message.getReplyTag());
|
||||
}
|
||||
|
||||
private GarlicConfig createReplyConfig(TunnelCreateStatusMessage body) {
|
||||
GarlicConfig config = new GarlicConfig();
|
||||
|
||||
PayloadGarlicConfig replyClove = buildReplyClove(body);
|
||||
config.addClove(replyClove);
|
||||
|
||||
DeliveryInstructions instructions = new DeliveryInstructions();
|
||||
instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_LOCAL);
|
||||
instructions.setDelayRequested(false);
|
||||
instructions.setDelaySeconds(0);
|
||||
instructions.setEncrypted(false);
|
||||
instructions.setEncryptionKey(null);
|
||||
instructions.setRouter(null);
|
||||
instructions.setTunnelId(null);
|
||||
|
||||
config.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
|
||||
config.setDeliveryInstructions(instructions);
|
||||
config.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||
config.setExpiration(REPLY_TIMEOUT+getContext().clock().now());
|
||||
config.setRecipient(null);
|
||||
config.setRequestAck(false);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a clove that sends the tunnel create reply
|
||||
*/
|
||||
private PayloadGarlicConfig buildReplyClove(TunnelCreateStatusMessage body) {
|
||||
PayloadGarlicConfig replyClove = new PayloadGarlicConfig();
|
||||
|
||||
DeliveryInstructions instructions = new DeliveryInstructions();
|
||||
instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_LOCAL);
|
||||
instructions.setRouter(null);
|
||||
instructions.setDelayRequested(false);
|
||||
instructions.setDelaySeconds(0);
|
||||
instructions.setEncrypted(false);
|
||||
|
||||
replyClove.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
|
||||
replyClove.setDeliveryInstructions(instructions);
|
||||
replyClove.setExpiration(REPLY_TIMEOUT+getContext().clock().now());
|
||||
replyClove.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||
replyClove.setPayload(body);
|
||||
replyClove.setRecipient(null);
|
||||
replyClove.setRequestAck(false);
|
||||
|
||||
return replyClove;
|
||||
}
|
||||
|
||||
|
||||
private TunnelId selectReplyTunnel() {
|
||||
TunnelSelectionCriteria crit = new TunnelSelectionCriteria();
|
||||
crit.setMinimumTunnelsRequired(1);
|
||||
crit.setMaximumTunnelsRequired(1);
|
||||
List ids = getContext().tunnelManager().selectOutboundTunnelIds(crit);
|
||||
if ( (ids != null) && (ids.size() > 0) )
|
||||
return (TunnelId)ids.get(0);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getName() { return "Handle Tunnel Create Message"; }
|
||||
|
||||
private class JoinJob extends JobImpl {
|
||||
|
@ -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())) + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user