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];
|
byte compressed[] = new byte[compressedSize];
|
||||||
int read = DataHelper.read(in, compressed);
|
int read = DataHelper.read(in, compressed);
|
||||||
if (read != compressedSize)
|
if (read != compressedSize)
|
||||||
throw new I2NPMessageException("Invalid compressed data size");
|
throw new I2NPMessageException("Invalid compressed data size (expected "
|
||||||
|
+ compressedSize + " read " + read + ")");
|
||||||
ByteArrayInputStream bais = new ByteArrayInputStream(DataHelper.decompress(compressed));
|
ByteArrayInputStream bais = new ByteArrayInputStream(DataHelper.decompress(compressed));
|
||||||
_info.readBytes(bais);
|
_info.readBytes(bais);
|
||||||
} else {
|
} else {
|
||||||
|
@ -34,25 +34,8 @@ public class GarlicClove extends DataStructureImpl {
|
|||||||
private long _cloveId;
|
private long _cloveId;
|
||||||
private Date _expiration;
|
private Date _expiration;
|
||||||
private Certificate _certificate;
|
private Certificate _certificate;
|
||||||
private int _replyAction;
|
|
||||||
private SourceRouteBlock _sourceRouteBlock;
|
|
||||||
private I2NPMessageHandler _handler;
|
private I2NPMessageHandler _handler;
|
||||||
|
|
||||||
/** No action requested with the source route block */
|
|
||||||
public final static int ACTION_NONE = 0;
|
|
||||||
/**
|
|
||||||
* A DeliveryStatusMessage is requested with the source route block using
|
|
||||||
* the cloveId as the id received
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public final static int ACTION_STATUS = 1;
|
|
||||||
/**
|
|
||||||
* No DeliveryStatusMessage is requested, but the source route block is
|
|
||||||
* included for message specific replies
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public final static int ACTION_MESSAGE_SPECIFIC = 2;
|
|
||||||
|
|
||||||
public GarlicClove(RouterContext context) {
|
public GarlicClove(RouterContext context) {
|
||||||
_context = context;
|
_context = context;
|
||||||
_log = context.logManager().getLog(GarlicClove.class);
|
_log = context.logManager().getLog(GarlicClove.class);
|
||||||
@ -62,8 +45,6 @@ public class GarlicClove extends DataStructureImpl {
|
|||||||
setCloveId(-1);
|
setCloveId(-1);
|
||||||
setExpiration(null);
|
setExpiration(null);
|
||||||
setCertificate(null);
|
setCertificate(null);
|
||||||
setSourceRouteBlockAction(ACTION_NONE);
|
|
||||||
setSourceRouteBlock(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeliveryInstructions getInstructions() { return _instructions; }
|
public DeliveryInstructions getInstructions() { return _instructions; }
|
||||||
@ -76,10 +57,6 @@ public class GarlicClove extends DataStructureImpl {
|
|||||||
public void setExpiration(Date exp) { _expiration = exp; }
|
public void setExpiration(Date exp) { _expiration = exp; }
|
||||||
public Certificate getCertificate() { return _certificate; }
|
public Certificate getCertificate() { return _certificate; }
|
||||||
public void setCertificate(Certificate cert) { _certificate = cert; }
|
public void setCertificate(Certificate cert) { _certificate = cert; }
|
||||||
public int getSourceRouteBlockAction() { return _replyAction; }
|
|
||||||
public void setSourceRouteBlockAction(int action) { _replyAction = action; }
|
|
||||||
public SourceRouteBlock getSourceRouteBlock() { return _sourceRouteBlock; }
|
|
||||||
public void setSourceRouteBlock(SourceRouteBlock block) { _sourceRouteBlock = block; }
|
|
||||||
|
|
||||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||||
_instructions = new DeliveryInstructions();
|
_instructions = new DeliveryInstructions();
|
||||||
@ -99,12 +76,6 @@ public class GarlicClove extends DataStructureImpl {
|
|||||||
_certificate.readBytes(in);
|
_certificate.readBytes(in);
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Read cert: " + _certificate);
|
_log.debug("Read cert: " + _certificate);
|
||||||
int replyStyle = (int)DataHelper.readLong(in, 1);
|
|
||||||
setSourceRouteBlockAction(replyStyle);
|
|
||||||
if (replyStyle != ACTION_NONE) {
|
|
||||||
_sourceRouteBlock = new SourceRouteBlock();
|
|
||||||
_sourceRouteBlock.readBytes(in);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
||||||
@ -119,28 +90,23 @@ public class GarlicClove extends DataStructureImpl {
|
|||||||
error.append("Expiration is null ");
|
error.append("Expiration is null ");
|
||||||
if (_certificate == null)
|
if (_certificate == null)
|
||||||
error.append("Certificate is null ");
|
error.append("Certificate is null ");
|
||||||
if (_replyAction < 0)
|
|
||||||
error.append("Reply action is < 0 [").append(_replyAction).append("] ");;
|
|
||||||
if (error.length() > 0)
|
|
||||||
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))
|
if (error.length() > 0)
|
||||||
_log.debug("Wrote instructions: " + _instructions);
|
throw new DataFormatException(error.toString());
|
||||||
_msg.writeBytes(out);
|
|
||||||
DataHelper.writeLong(out, 4, _cloveId);
|
_instructions.writeBytes(out);
|
||||||
DataHelper.writeDate(out, _expiration);
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("CloveID written: " + _cloveId + " expiration written: "
|
_log.debug("Wrote instructions: " + _instructions);
|
||||||
+ _expiration);
|
_msg.writeBytes(out);
|
||||||
_certificate.writeBytes(out);
|
DataHelper.writeLong(out, 4, _cloveId);
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
DataHelper.writeDate(out, _expiration);
|
||||||
_log.debug("Written cert: " + _certificate);
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
DataHelper.writeLong(out, 1, _replyAction);
|
_log.debug("CloveID written: " + _cloveId + " expiration written: "
|
||||||
if ( (_replyAction != 0) && (_sourceRouteBlock != null) )
|
+ _expiration);
|
||||||
_sourceRouteBlock.writeBytes(out);
|
_certificate.writeBytes(out);
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Written cert: " + _certificate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
@ -148,22 +114,18 @@ public class GarlicClove extends DataStructureImpl {
|
|||||||
return false;
|
return false;
|
||||||
GarlicClove clove = (GarlicClove)obj;
|
GarlicClove clove = (GarlicClove)obj;
|
||||||
return DataHelper.eq(getCertificate(), clove.getCertificate()) &&
|
return DataHelper.eq(getCertificate(), clove.getCertificate()) &&
|
||||||
DataHelper.eq(getCloveId(), clove.getCloveId()) &&
|
DataHelper.eq(getCloveId(), clove.getCloveId()) &&
|
||||||
DataHelper.eq(getData(), clove.getData()) &&
|
DataHelper.eq(getData(), clove.getData()) &&
|
||||||
DataHelper.eq(getExpiration(), clove.getExpiration()) &&
|
DataHelper.eq(getExpiration(), clove.getExpiration()) &&
|
||||||
DataHelper.eq(getInstructions(), clove.getInstructions()) &&
|
DataHelper.eq(getInstructions(), clove.getInstructions());
|
||||||
DataHelper.eq(getSourceRouteBlock(), clove.getSourceRouteBlock()) &&
|
|
||||||
(getSourceRouteBlockAction() == clove.getSourceRouteBlockAction());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return DataHelper.hashCode(getCertificate()) +
|
return DataHelper.hashCode(getCertificate()) +
|
||||||
(int)getCloveId() +
|
(int)getCloveId() +
|
||||||
DataHelper.hashCode(getData()) +
|
DataHelper.hashCode(getData()) +
|
||||||
DataHelper.hashCode(getExpiration()) +
|
DataHelper.hashCode(getExpiration()) +
|
||||||
DataHelper.hashCode(getInstructions()) +
|
DataHelper.hashCode(getInstructions());
|
||||||
DataHelper.hashCode(getSourceRouteBlock()) +
|
|
||||||
getSourceRouteBlockAction();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
@ -173,8 +135,6 @@ public class GarlicClove extends DataStructureImpl {
|
|||||||
buf.append("\n\tCertificate: ").append(getCertificate());
|
buf.append("\n\tCertificate: ").append(getCertificate());
|
||||||
buf.append("\n\tClove ID: ").append(getCloveId());
|
buf.append("\n\tClove ID: ").append(getCloveId());
|
||||||
buf.append("\n\tExpiration: ").append(getExpiration());
|
buf.append("\n\tExpiration: ").append(getExpiration());
|
||||||
buf.append("\n\tSource route style: ").append(getSourceRouteBlockAction());
|
|
||||||
buf.append("\n\tSource route block: ").append(getSourceRouteBlock());
|
|
||||||
buf.append("\n\tData: ").append(getData());
|
buf.append("\n\tData: ").append(getData());
|
||||||
buf.append("]");
|
buf.append("]");
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
|
@ -73,8 +73,6 @@ public class I2NPMessageHandler {
|
|||||||
return new TunnelMessage(_context);
|
return new TunnelMessage(_context);
|
||||||
case DataMessage.MESSAGE_TYPE:
|
case DataMessage.MESSAGE_TYPE:
|
||||||
return new DataMessage(_context);
|
return new DataMessage(_context);
|
||||||
case SourceRouteReplyMessage.MESSAGE_TYPE:
|
|
||||||
return new SourceRouteReplyMessage(_context);
|
|
||||||
case TunnelCreateMessage.MESSAGE_TYPE:
|
case TunnelCreateMessage.MESSAGE_TYPE:
|
||||||
return new TunnelCreateMessage(_context);
|
return new TunnelCreateMessage(_context);
|
||||||
case TunnelCreateStatusMessage.MESSAGE_TYPE:
|
case TunnelCreateStatusMessage.MESSAGE_TYPE:
|
||||||
|
@ -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.DataHelper;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
import net.i2p.data.TunnelId;
|
import net.i2p.data.TunnelId;
|
||||||
|
import net.i2p.data.SessionKey;
|
||||||
|
import net.i2p.data.SessionTag;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,8 +32,9 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
|||||||
private final static Log _log = new Log(TunnelCreateMessage.class);
|
private final static Log _log = new Log(TunnelCreateMessage.class);
|
||||||
public final static int MESSAGE_TYPE = 6;
|
public final static int MESSAGE_TYPE = 6;
|
||||||
private int _participantType;
|
private int _participantType;
|
||||||
private Hash _nextRouter;
|
|
||||||
private TunnelId _tunnelId;
|
private TunnelId _tunnelId;
|
||||||
|
private Hash _nextRouter;
|
||||||
|
private TunnelId _nextTunnelId;
|
||||||
private long _tunnelDuration;
|
private long _tunnelDuration;
|
||||||
private TunnelConfigurationSessionKey _configKey;
|
private TunnelConfigurationSessionKey _configKey;
|
||||||
private long _maxPeakMessagesPerMin;
|
private long _maxPeakMessagesPerMin;
|
||||||
@ -44,7 +47,10 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
|||||||
private TunnelSigningPrivateKey _verificationPrivKey;
|
private TunnelSigningPrivateKey _verificationPrivKey;
|
||||||
private TunnelSessionKey _tunnelKey;
|
private TunnelSessionKey _tunnelKey;
|
||||||
private Certificate _certificate;
|
private Certificate _certificate;
|
||||||
private SourceRouteBlock _replyBlock;
|
private SessionTag _replyTag;
|
||||||
|
private SessionKey _replyKey;
|
||||||
|
private TunnelId _replyTunnel;
|
||||||
|
private Hash _replyPeer;
|
||||||
|
|
||||||
public static final int PARTICIPANT_TYPE_GATEWAY = 1;
|
public static final int PARTICIPANT_TYPE_GATEWAY = 1;
|
||||||
public static final int PARTICIPANT_TYPE_ENDPOINT = 2;
|
public static final int PARTICIPANT_TYPE_ENDPOINT = 2;
|
||||||
@ -57,6 +63,7 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
|||||||
super(context);
|
super(context);
|
||||||
setParticipantType(-1);
|
setParticipantType(-1);
|
||||||
setNextRouter(null);
|
setNextRouter(null);
|
||||||
|
setNextTunnelId(null);
|
||||||
setTunnelId(null);
|
setTunnelId(null);
|
||||||
setTunnelDurationSeconds(-1);
|
setTunnelDurationSeconds(-1);
|
||||||
setConfigurationKey(null);
|
setConfigurationKey(null);
|
||||||
@ -70,13 +77,18 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
|||||||
setVerificationPrivateKey(null);
|
setVerificationPrivateKey(null);
|
||||||
setTunnelKey(null);
|
setTunnelKey(null);
|
||||||
setCertificate(null);
|
setCertificate(null);
|
||||||
setReplyBlock(null);
|
setReplyTag(null);
|
||||||
|
setReplyKey(null);
|
||||||
|
setReplyTunnel(null);
|
||||||
|
setReplyPeer(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParticipantType(int type) { _participantType = type; }
|
public void setParticipantType(int type) { _participantType = type; }
|
||||||
public int getParticipantType() { return _participantType; }
|
public int getParticipantType() { return _participantType; }
|
||||||
public void setNextRouter(Hash routerIdentityHash) { _nextRouter = routerIdentityHash; }
|
public void setNextRouter(Hash routerIdentityHash) { _nextRouter = routerIdentityHash; }
|
||||||
public Hash getNextRouter() { return _nextRouter; }
|
public Hash getNextRouter() { return _nextRouter; }
|
||||||
|
public void setNextTunnelId(TunnelId id) { _nextTunnelId = id; }
|
||||||
|
public TunnelId getNextTunnelId() { return _nextTunnelId; }
|
||||||
public void setTunnelId(TunnelId id) { _tunnelId = id; }
|
public void setTunnelId(TunnelId id) { _tunnelId = id; }
|
||||||
public TunnelId getTunnelId() { return _tunnelId; }
|
public TunnelId getTunnelId() { return _tunnelId; }
|
||||||
public void setTunnelDurationSeconds(long durationSeconds) { _tunnelDuration = durationSeconds; }
|
public void setTunnelDurationSeconds(long durationSeconds) { _tunnelDuration = durationSeconds; }
|
||||||
@ -103,8 +115,14 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
|||||||
public TunnelSessionKey getTunnelKey() { return _tunnelKey; }
|
public TunnelSessionKey getTunnelKey() { return _tunnelKey; }
|
||||||
public void setCertificate(Certificate cert) { _certificate = cert; }
|
public void setCertificate(Certificate cert) { _certificate = cert; }
|
||||||
public Certificate getCertificate() { return _certificate; }
|
public Certificate getCertificate() { return _certificate; }
|
||||||
public void setReplyBlock(SourceRouteBlock block) { _replyBlock = block; }
|
public void setReplyTag(SessionTag tag) { _replyTag = tag; }
|
||||||
public SourceRouteBlock getReplyBlock() { return _replyBlock; }
|
public SessionTag getReplyTag() { return _replyTag; }
|
||||||
|
public void setReplyKey(SessionKey key) { _replyKey = key; }
|
||||||
|
public SessionKey getReplyKey() { return _replyKey; }
|
||||||
|
public void setReplyTunnel(TunnelId id) { _replyTunnel = id; }
|
||||||
|
public TunnelId getReplyTunnel() { return _replyTunnel; }
|
||||||
|
public void setReplyPeer(Hash peer) { _replyPeer = peer; }
|
||||||
|
public Hash getReplyPeer() { return _replyPeer; }
|
||||||
|
|
||||||
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
|
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
|
||||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||||
@ -113,6 +131,8 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
|||||||
if (_participantType != PARTICIPANT_TYPE_ENDPOINT) {
|
if (_participantType != PARTICIPANT_TYPE_ENDPOINT) {
|
||||||
_nextRouter = new Hash();
|
_nextRouter = new Hash();
|
||||||
_nextRouter.readBytes(in);
|
_nextRouter.readBytes(in);
|
||||||
|
_nextTunnelId = new TunnelId();
|
||||||
|
_nextTunnelId.readBytes(in);
|
||||||
}
|
}
|
||||||
_tunnelId = new TunnelId();
|
_tunnelId = new TunnelId();
|
||||||
_tunnelId.readBytes(in);
|
_tunnelId.readBytes(in);
|
||||||
@ -140,8 +160,14 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
|||||||
}
|
}
|
||||||
_certificate = new Certificate();
|
_certificate = new Certificate();
|
||||||
_certificate.readBytes(in);
|
_certificate.readBytes(in);
|
||||||
_replyBlock = new SourceRouteBlock();
|
_replyTag = new SessionTag();
|
||||||
_replyBlock.readBytes(in);
|
_replyTag.readBytes(in);
|
||||||
|
_replyKey = new SessionKey();
|
||||||
|
_replyKey.readBytes(in);
|
||||||
|
_replyTunnel = new TunnelId();
|
||||||
|
_replyTunnel.readBytes(in);
|
||||||
|
_replyPeer = new Hash();
|
||||||
|
_replyPeer.readBytes(in);
|
||||||
} catch (DataFormatException dfe) {
|
} catch (DataFormatException dfe) {
|
||||||
throw new I2NPMessageException("Unable to load the message data", dfe);
|
throw new I2NPMessageException("Unable to load the message data", dfe);
|
||||||
}
|
}
|
||||||
@ -153,6 +179,7 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
|||||||
DataHelper.writeLong(os, 1, _participantType);
|
DataHelper.writeLong(os, 1, _participantType);
|
||||||
if (_participantType != PARTICIPANT_TYPE_ENDPOINT) {
|
if (_participantType != PARTICIPANT_TYPE_ENDPOINT) {
|
||||||
_nextRouter.writeBytes(os);
|
_nextRouter.writeBytes(os);
|
||||||
|
_nextTunnelId.writeBytes(os);
|
||||||
}
|
}
|
||||||
_tunnelId.writeBytes(os);
|
_tunnelId.writeBytes(os);
|
||||||
DataHelper.writeLong(os, 4, _tunnelDuration);
|
DataHelper.writeLong(os, 4, _tunnelDuration);
|
||||||
@ -174,7 +201,10 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
|||||||
_tunnelKey.writeBytes(os);
|
_tunnelKey.writeBytes(os);
|
||||||
}
|
}
|
||||||
_certificate.writeBytes(os);
|
_certificate.writeBytes(os);
|
||||||
_replyBlock.writeBytes(os);
|
_replyTag.writeBytes(os);
|
||||||
|
_replyKey.writeBytes(os);
|
||||||
|
_replyTunnel.writeBytes(os);
|
||||||
|
_replyPeer.writeBytes(os);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
throw new I2NPMessageException("Error writing out the message data", t);
|
throw new I2NPMessageException("Error writing out the message data", t);
|
||||||
}
|
}
|
||||||
@ -201,21 +231,23 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
|||||||
|
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return (int)(DataHelper.hashCode(getCertificate()) +
|
return (int)(DataHelper.hashCode(getCertificate()) +
|
||||||
DataHelper.hashCode(getConfigurationKey()) +
|
DataHelper.hashCode(getConfigurationKey()) +
|
||||||
DataHelper.hashCode(getNextRouter()) +
|
DataHelper.hashCode(getNextRouter()) +
|
||||||
DataHelper.hashCode(getReplyBlock()) +
|
DataHelper.hashCode(getNextTunnelId()) +
|
||||||
DataHelper.hashCode(getTunnelId()) +
|
DataHelper.hashCode(getReplyPeer()) +
|
||||||
DataHelper.hashCode(getTunnelKey()) +
|
DataHelper.hashCode(getReplyTunnel()) +
|
||||||
DataHelper.hashCode(getVerificationPrivateKey()) +
|
DataHelper.hashCode(getTunnelId()) +
|
||||||
DataHelper.hashCode(getVerificationPublicKey()) +
|
DataHelper.hashCode(getTunnelKey()) +
|
||||||
(getIncludeDummyTraffic() ? 1 : 0) +
|
DataHelper.hashCode(getVerificationPrivateKey()) +
|
||||||
getMaxAvgBytesPerMin() +
|
DataHelper.hashCode(getVerificationPublicKey()) +
|
||||||
getMaxAvgMessagesPerMin() +
|
(getIncludeDummyTraffic() ? 1 : 0) +
|
||||||
getMaxPeakBytesPerMin() +
|
getMaxAvgBytesPerMin() +
|
||||||
getMaxPeakMessagesPerMin() +
|
getMaxAvgMessagesPerMin() +
|
||||||
getParticipantType() +
|
getMaxPeakBytesPerMin() +
|
||||||
(getReorderMessages() ? 1 : 0) +
|
getMaxPeakMessagesPerMin() +
|
||||||
getTunnelDurationSeconds());
|
getParticipantType() +
|
||||||
|
(getReorderMessages() ? 1 : 0) +
|
||||||
|
getTunnelDurationSeconds());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean equals(Object object) {
|
public boolean equals(Object object) {
|
||||||
@ -224,7 +256,11 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
|||||||
return DataHelper.eq(getCertificate(), msg.getCertificate()) &&
|
return DataHelper.eq(getCertificate(), msg.getCertificate()) &&
|
||||||
DataHelper.eq(getConfigurationKey(), msg.getConfigurationKey()) &&
|
DataHelper.eq(getConfigurationKey(), msg.getConfigurationKey()) &&
|
||||||
DataHelper.eq(getNextRouter(), msg.getNextRouter()) &&
|
DataHelper.eq(getNextRouter(), msg.getNextRouter()) &&
|
||||||
DataHelper.eq(getReplyBlock(), msg.getReplyBlock()) &&
|
DataHelper.eq(getNextTunnelId(), msg.getNextTunnelId()) &&
|
||||||
|
DataHelper.eq(getReplyTag(), msg.getReplyTag()) &&
|
||||||
|
DataHelper.eq(getReplyKey(), msg.getReplyKey()) &&
|
||||||
|
DataHelper.eq(getReplyTunnel(), msg.getReplyTunnel()) &&
|
||||||
|
DataHelper.eq(getReplyPeer(), msg.getReplyPeer()) &&
|
||||||
DataHelper.eq(getTunnelId(), msg.getTunnelId()) &&
|
DataHelper.eq(getTunnelId(), msg.getTunnelId()) &&
|
||||||
DataHelper.eq(getTunnelKey(), msg.getTunnelKey()) &&
|
DataHelper.eq(getTunnelKey(), msg.getTunnelKey()) &&
|
||||||
DataHelper.eq(getVerificationPrivateKey(), msg.getVerificationPrivateKey()) &&
|
DataHelper.eq(getVerificationPrivateKey(), msg.getVerificationPrivateKey()) &&
|
||||||
@ -249,7 +285,11 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
|
|||||||
buf.append("\n\tCertificate: ").append(getCertificate());
|
buf.append("\n\tCertificate: ").append(getCertificate());
|
||||||
buf.append("\n\tConfiguration Key: ").append(getConfigurationKey());
|
buf.append("\n\tConfiguration Key: ").append(getConfigurationKey());
|
||||||
buf.append("\n\tNext Router: ").append(getNextRouter());
|
buf.append("\n\tNext Router: ").append(getNextRouter());
|
||||||
buf.append("\n\tReply Block: ").append(getReplyBlock());
|
buf.append("\n\tNext Tunnel: ").append(getNextTunnelId());
|
||||||
|
buf.append("\n\tReply Tag: ").append(getReplyTag());
|
||||||
|
buf.append("\n\tReply Key: ").append(getReplyKey());
|
||||||
|
buf.append("\n\tReply Tunnel: ").append(getReplyTunnel());
|
||||||
|
buf.append("\n\tReply Peer: ").append(getReplyPeer());
|
||||||
buf.append("\n\tTunnel ID: ").append(getTunnelId());
|
buf.append("\n\tTunnel ID: ").append(getTunnelId());
|
||||||
buf.append("\n\tTunnel Key: ").append(getTunnelKey());
|
buf.append("\n\tTunnel Key: ").append(getTunnelKey());
|
||||||
buf.append("\n\tVerification Private Key: ").append(getVerificationPrivateKey());
|
buf.append("\n\tVerification Private Key: ").append(getVerificationPrivateKey());
|
||||||
|
@ -11,7 +11,6 @@ package net.i2p.router;
|
|||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
import net.i2p.data.RouterIdentity;
|
import net.i2p.data.RouterIdentity;
|
||||||
import net.i2p.data.i2np.I2NPMessage;
|
import net.i2p.data.i2np.I2NPMessage;
|
||||||
import net.i2p.data.i2np.SourceRouteBlock;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines a class that builds jobs to handle a particular message - these
|
* Defines a class that builds jobs to handle a particular message - these
|
||||||
@ -27,9 +26,8 @@ public interface HandlerJobBuilder {
|
|||||||
* @param receivedMessage I2NP message received
|
* @param receivedMessage I2NP message received
|
||||||
* @param from router that sent the message (if available)
|
* @param from router that sent the message (if available)
|
||||||
* @param fromHash hash of the routerIdentity of the router that sent the message (if available)
|
* @param fromHash hash of the routerIdentity of the router that sent the message (if available)
|
||||||
* @param replyBlock block with which a reply could be sent (if available)
|
|
||||||
* @return a job or null if no particular job is appropriate (in which case,
|
* @return a job or null if no particular job is appropriate (in which case,
|
||||||
* the message should go into the inbound message pool)
|
* the message should go into the inbound message pool)
|
||||||
*/
|
*/
|
||||||
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock);
|
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash);
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ package net.i2p.router;
|
|||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
import net.i2p.data.RouterIdentity;
|
import net.i2p.data.RouterIdentity;
|
||||||
import net.i2p.data.i2np.I2NPMessage;
|
import net.i2p.data.i2np.I2NPMessage;
|
||||||
import net.i2p.data.i2np.SourceRouteBlock;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrap an I2NP message received from the network prior to handling and processing.
|
* Wrap an I2NP message received from the network prior to handling and processing.
|
||||||
@ -22,7 +21,6 @@ public class InNetMessage {
|
|||||||
private I2NPMessage _message;
|
private I2NPMessage _message;
|
||||||
private RouterIdentity _fromRouter;
|
private RouterIdentity _fromRouter;
|
||||||
private Hash _fromRouterHash;
|
private Hash _fromRouterHash;
|
||||||
private SourceRouteBlock _replyBlock;
|
|
||||||
private long _created;
|
private long _created;
|
||||||
|
|
||||||
public InNetMessage(RouterContext context) {
|
public InNetMessage(RouterContext context) {
|
||||||
@ -30,7 +28,6 @@ public class InNetMessage {
|
|||||||
setMessage(null);
|
setMessage(null);
|
||||||
setFromRouter(null);
|
setFromRouter(null);
|
||||||
setFromRouterHash(null);
|
setFromRouterHash(null);
|
||||||
setReplyBlock(null);
|
|
||||||
context.messageStateMonitor().inboundMessageAdded();
|
context.messageStateMonitor().inboundMessageAdded();
|
||||||
_created = context.clock().now();
|
_created = context.clock().now();
|
||||||
_context.statManager().createRateStat("inNetMessage.timeToDiscard",
|
_context.statManager().createRateStat("inNetMessage.timeToDiscard",
|
||||||
@ -59,15 +56,6 @@ public class InNetMessage {
|
|||||||
public RouterIdentity getFromRouter() { return _fromRouter; }
|
public RouterIdentity getFromRouter() { return _fromRouter; }
|
||||||
public void setFromRouter(RouterIdentity router) { _fromRouter = router; }
|
public void setFromRouter(RouterIdentity router) { _fromRouter = router; }
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve any source route block supplied with this message for replies
|
|
||||||
*
|
|
||||||
* @return source route block, or null if it was not supplied /or/ if it was already
|
|
||||||
* used in an ack
|
|
||||||
*/
|
|
||||||
public SourceRouteBlock getReplyBlock() { return _replyBlock; }
|
|
||||||
public void setReplyBlock(SourceRouteBlock block) { _replyBlock = block; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this after we're done dealing with this message (when we no
|
* Call this after we're done dealing with this message (when we no
|
||||||
* longer need its data)
|
* longer need its data)
|
||||||
|
@ -88,7 +88,7 @@ public class InNetMessagePool {
|
|||||||
|
|
||||||
if (builder != null) {
|
if (builder != null) {
|
||||||
Job job = builder.createJob(messageBody, msg.getFromRouter(),
|
Job job = builder.createJob(messageBody, msg.getFromRouter(),
|
||||||
msg.getFromRouterHash(), msg.getReplyBlock());
|
msg.getFromRouterHash());
|
||||||
if (job != null) {
|
if (job != null) {
|
||||||
_context.jobQueue().addJob(job);
|
_context.jobQueue().addJob(job);
|
||||||
synchronized (_messages) {
|
synchronized (_messages) {
|
||||||
|
@ -19,7 +19,6 @@ import java.util.Iterator;
|
|||||||
import java.util.SortedMap;
|
import java.util.SortedMap;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import net.i2p.router.message.HandleSourceRouteReplyMessageJob;
|
|
||||||
import net.i2p.router.networkdb.HandleDatabaseLookupMessageJob;
|
import net.i2p.router.networkdb.HandleDatabaseLookupMessageJob;
|
||||||
import net.i2p.router.tunnelmanager.HandleTunnelCreateMessageJob;
|
import net.i2p.router.tunnelmanager.HandleTunnelCreateMessageJob;
|
||||||
import net.i2p.router.tunnelmanager.RequestTunnelJob;
|
import net.i2p.router.tunnelmanager.RequestTunnelJob;
|
||||||
@ -221,14 +220,6 @@ public class JobQueue {
|
|||||||
if (!_allowParallelOperation) return false; // dont drop during startup [duh]
|
if (!_allowParallelOperation) return false; // dont drop during startup [duh]
|
||||||
Class cls = job.getClass();
|
Class cls = job.getClass();
|
||||||
if (numReady > _maxWaitingJobs) {
|
if (numReady > _maxWaitingJobs) {
|
||||||
|
|
||||||
// heavy cpu load, plus we're allowed to be unreliable with these two
|
|
||||||
// [but garlics can contain our payloads, so lets not drop them]
|
|
||||||
//if (cls == HandleGarlicMessageJob.class)
|
|
||||||
// return true;
|
|
||||||
if (cls == HandleSourceRouteReplyMessageJob.class)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// lets not try to drop too many tunnel messages...
|
// lets not try to drop too many tunnel messages...
|
||||||
//if (cls == HandleTunnelMessageJob.class)
|
//if (cls == HandleTunnelMessageJob.class)
|
||||||
// return true;
|
// return true;
|
||||||
|
@ -128,11 +128,10 @@ public class MessageHistory {
|
|||||||
* @param outTunnel tunnel we are sending this request out
|
* @param outTunnel tunnel we are sending this request out
|
||||||
* @param peerRequested peer asked to participate in the tunnel
|
* @param peerRequested peer asked to participate in the tunnel
|
||||||
* @param nextPeer who peerRequested should forward messages to (or null if it is the endpoint)
|
* @param nextPeer who peerRequested should forward messages to (or null if it is the endpoint)
|
||||||
* @param sourceRoutePeer to whom peerRequested should forward its TunnelCreateStatusMessage through
|
|
||||||
* @param replyTunnel the tunnel sourceRoutePeer should forward the source routed message to
|
* @param replyTunnel the tunnel sourceRoutePeer should forward the source routed message to
|
||||||
* @param replyThrough the gateway of the tunnel that the sourceRoutePeer will be sending to
|
* @param replyThrough the gateway of the tunnel that the sourceRoutePeer will be sending to
|
||||||
*/
|
*/
|
||||||
public void requestTunnelCreate(TunnelId createTunnel, TunnelId outTunnel, Hash peerRequested, Hash nextPeer, Hash sourceRoutePeer, TunnelId replyTunnel, Hash replyThrough) {
|
public void requestTunnelCreate(TunnelId createTunnel, TunnelId outTunnel, Hash peerRequested, Hash nextPeer, TunnelId replyTunnel, Hash replyThrough) {
|
||||||
if (!_doLog) return;
|
if (!_doLog) return;
|
||||||
StringBuffer buf = new StringBuffer(128);
|
StringBuffer buf = new StringBuffer(128);
|
||||||
buf.append(getPrefix());
|
buf.append(getPrefix());
|
||||||
@ -142,8 +141,6 @@ public class MessageHistory {
|
|||||||
buf.append("(next [").append(getName(nextPeer)).append("]) ");
|
buf.append("(next [").append(getName(nextPeer)).append("]) ");
|
||||||
if (outTunnel != null)
|
if (outTunnel != null)
|
||||||
buf.append("via [").append(outTunnel.getTunnelId()).append("] ");
|
buf.append("via [").append(outTunnel.getTunnelId()).append("] ");
|
||||||
if (sourceRoutePeer != null)
|
|
||||||
buf.append("with replies routed through [").append(getName(sourceRoutePeer)).append("] ");
|
|
||||||
if ( (replyTunnel != null) && (replyThrough != null) )
|
if ( (replyTunnel != null) && (replyThrough != null) )
|
||||||
buf.append("who forwards it through [").append(replyTunnel.getTunnelId()).append("] on [").append(getName(replyThrough)).append("]");
|
buf.append("who forwards it through [").append(replyTunnel.getTunnelId()).append("] on [").append(getName(replyThrough)).append("]");
|
||||||
addEntry(buf.toString());
|
addEntry(buf.toString());
|
||||||
@ -258,15 +255,13 @@ public class MessageHistory {
|
|||||||
* of a timeout or an explicit refusal).
|
* of a timeout or an explicit refusal).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public void tunnelRequestTimedOut(Hash peer, TunnelId tunnel, Hash replyThrough) {
|
public void tunnelRequestTimedOut(Hash peer, TunnelId tunnel) {
|
||||||
if (!_doLog) return;
|
if (!_doLog) return;
|
||||||
if ( (tunnel == null) || (peer == null) ) return;
|
if ( (tunnel == null) || (peer == null) ) return;
|
||||||
StringBuffer buf = new StringBuffer(128);
|
StringBuffer buf = new StringBuffer(128);
|
||||||
buf.append(getPrefix());
|
buf.append(getPrefix());
|
||||||
buf.append("tunnel [").append(tunnel.getTunnelId()).append("] timed out on [");
|
buf.append("tunnel [").append(tunnel.getTunnelId()).append("] timed out on [");
|
||||||
buf.append(getName(peer)).append("]");
|
buf.append(getName(peer)).append("]");
|
||||||
if (replyThrough != null)
|
|
||||||
buf.append(" with their reply intended to come through [").append(getName(replyThrough)).append("]");
|
|
||||||
addEntry(buf.toString());
|
addEntry(buf.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,10 +28,8 @@ import net.i2p.data.DataFormatException;
|
|||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.data.RouterInfo;
|
import net.i2p.data.RouterInfo;
|
||||||
import net.i2p.data.i2np.GarlicMessage;
|
import net.i2p.data.i2np.GarlicMessage;
|
||||||
import net.i2p.data.i2np.SourceRouteReplyMessage;
|
|
||||||
import net.i2p.data.i2np.TunnelMessage;
|
import net.i2p.data.i2np.TunnelMessage;
|
||||||
import net.i2p.router.message.GarlicMessageHandler;
|
import net.i2p.router.message.GarlicMessageHandler;
|
||||||
import net.i2p.router.message.SourceRouteReplyMessageHandler;
|
|
||||||
import net.i2p.router.message.TunnelMessageHandler;
|
import net.i2p.router.message.TunnelMessageHandler;
|
||||||
import net.i2p.router.startup.StartupJob;
|
import net.i2p.router.startup.StartupJob;
|
||||||
import net.i2p.stat.Rate;
|
import net.i2p.stat.Rate;
|
||||||
@ -218,7 +216,6 @@ public class Router {
|
|||||||
private void setupHandlers() {
|
private void setupHandlers() {
|
||||||
_context.inNetMessagePool().registerHandlerJobBuilder(GarlicMessage.MESSAGE_TYPE, new GarlicMessageHandler(_context));
|
_context.inNetMessagePool().registerHandlerJobBuilder(GarlicMessage.MESSAGE_TYPE, new GarlicMessageHandler(_context));
|
||||||
_context.inNetMessagePool().registerHandlerJobBuilder(TunnelMessage.MESSAGE_TYPE, new TunnelMessageHandler(_context));
|
_context.inNetMessagePool().registerHandlerJobBuilder(TunnelMessage.MESSAGE_TYPE, new TunnelMessageHandler(_context));
|
||||||
_context.inNetMessagePool().registerHandlerJobBuilder(SourceRouteReplyMessage.MESSAGE_TYPE, new SourceRouteReplyMessageHandler(_context));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void renderStatusHTML(OutputStream out) throws IOException {
|
public void renderStatusHTML(OutputStream out) throws IOException {
|
||||||
|
@ -22,7 +22,6 @@ import net.i2p.data.SessionTag;
|
|||||||
import net.i2p.data.i2np.GarlicClove;
|
import net.i2p.data.i2np.GarlicClove;
|
||||||
import net.i2p.data.i2np.GarlicMessage;
|
import net.i2p.data.i2np.GarlicMessage;
|
||||||
import net.i2p.data.i2np.I2NPMessage;
|
import net.i2p.data.i2np.I2NPMessage;
|
||||||
import net.i2p.data.i2np.SourceRouteBlock;
|
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
@ -36,9 +35,6 @@ public class GarlicMessageBuilder {
|
|||||||
}
|
}
|
||||||
public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags) {
|
public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags) {
|
||||||
Log log = ctx.logManager().getLog(GarlicMessageBuilder.class);
|
Log log = ctx.logManager().getLog(GarlicMessageBuilder.class);
|
||||||
if (config == null)
|
|
||||||
throw new IllegalArgumentException("Null config specified");
|
|
||||||
|
|
||||||
PublicKey key = config.getRecipientPublicKey();
|
PublicKey key = config.getRecipientPublicKey();
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
if (config.getRecipient() == null) {
|
if (config.getRecipient() == null) {
|
||||||
@ -50,19 +46,15 @@ public class GarlicMessageBuilder {
|
|||||||
} else
|
} else
|
||||||
key = config.getRecipient().getIdentity().getPublicKey();
|
key = config.getRecipient().getIdentity().getPublicKey();
|
||||||
}
|
}
|
||||||
GarlicMessage msg = new GarlicMessage(ctx);
|
|
||||||
|
|
||||||
noteWrap(ctx, msg, config);
|
|
||||||
|
|
||||||
if (log.shouldLog(Log.INFO))
|
if (log.shouldLog(Log.INFO))
|
||||||
log.info("Encrypted with public key " + key + " to expire on " + new Date(config.getExpiration()));
|
log.info("Encrypted with public key " + key + " to expire on " + new Date(config.getExpiration()));
|
||||||
|
|
||||||
byte cloveSet[] = buildCloveSet(ctx, config);
|
|
||||||
|
|
||||||
SessionKey curKey = ctx.sessionKeyManager().getCurrentKey(key);
|
SessionKey curKey = ctx.sessionKeyManager().getCurrentKey(key);
|
||||||
if (curKey == null)
|
if (curKey == null)
|
||||||
curKey = ctx.sessionKeyManager().createSession(key);
|
curKey = ctx.sessionKeyManager().createSession(key);
|
||||||
wrappedKey.setData(curKey.getData());
|
|
||||||
|
SessionTag curTag = ctx.sessionKeyManager().consumeNextAvailableTag(key, curKey);
|
||||||
|
|
||||||
int availTags = ctx.sessionKeyManager().getAvailableTags(key, curKey);
|
int availTags = ctx.sessionKeyManager().getAvailableTags(key, curKey);
|
||||||
if (log.shouldLog(Log.DEBUG))
|
if (log.shouldLog(Log.DEBUG))
|
||||||
@ -83,8 +75,34 @@ public class GarlicMessageBuilder {
|
|||||||
// always tack on at least one more - not necessary.
|
// always tack on at least one more - not necessary.
|
||||||
//wrappedTags.add(new SessionTag(true));
|
//wrappedTags.add(new SessionTag(true));
|
||||||
}
|
}
|
||||||
SessionTag curTag = ctx.sessionKeyManager().consumeNextAvailableTag(key, curKey);
|
|
||||||
byte encData[] = ctx.elGamalAESEngine().encrypt(cloveSet, key, curKey, wrappedTags, curTag, 256);
|
wrappedKey.setData(curKey.getData());
|
||||||
|
|
||||||
|
return buildMessage(ctx, config, wrappedKey, wrappedTags, key, curKey, curTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ctx scope
|
||||||
|
* @param config how/what to wrap
|
||||||
|
* @param wrappedKey output parameter that will be filled with the sessionKey used
|
||||||
|
* @param wrappedTags output parameter that will be filled with the sessionTags used
|
||||||
|
* @param target public key of the location being garlic routed to (may be null if we
|
||||||
|
* know the encryptKey and encryptTag)
|
||||||
|
* @param encryptKey sessionKey used to encrypt the current message
|
||||||
|
* @param encryptTag sessionTag used to encrypt the current message
|
||||||
|
*/
|
||||||
|
public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags, PublicKey target, SessionKey encryptKey, SessionTag encryptTag) {
|
||||||
|
Log log = ctx.logManager().getLog(GarlicMessageBuilder.class);
|
||||||
|
if (config == null)
|
||||||
|
throw new IllegalArgumentException("Null config specified");
|
||||||
|
|
||||||
|
GarlicMessage msg = new GarlicMessage(ctx);
|
||||||
|
|
||||||
|
noteWrap(ctx, msg, config);
|
||||||
|
|
||||||
|
byte cloveSet[] = buildCloveSet(ctx, config);
|
||||||
|
|
||||||
|
byte encData[] = ctx.elGamalAESEngine().encrypt(cloveSet, target, encryptKey, wrappedTags, encryptTag, 128);
|
||||||
msg.setData(encData);
|
msg.setData(encData);
|
||||||
Date exp = new Date(config.getExpiration());
|
Date exp = new Date(config.getExpiration());
|
||||||
msg.setMessageExpiration(exp);
|
msg.setMessageExpiration(exp);
|
||||||
@ -168,46 +186,8 @@ public class GarlicMessageBuilder {
|
|||||||
clove.setCloveId(config.getId());
|
clove.setCloveId(config.getId());
|
||||||
clove.setExpiration(new Date(config.getExpiration()));
|
clove.setExpiration(new Date(config.getExpiration()));
|
||||||
clove.setInstructions(config.getDeliveryInstructions());
|
clove.setInstructions(config.getDeliveryInstructions());
|
||||||
specifySourceRouteBlock(ctx, clove, config);
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
|
||||||
clove.writeBytes(baos);
|
clove.writeBytes(baos);
|
||||||
return baos.toByteArray();
|
return baos.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void specifySourceRouteBlock(RouterContext ctx, GarlicClove clove, GarlicConfig config) throws DataFormatException {
|
|
||||||
Log log = ctx.logManager().getLog(GarlicMessageBuilder.class);
|
|
||||||
boolean includeBlock = false;
|
|
||||||
if (config.getRequestAck()) {
|
|
||||||
clove.setSourceRouteBlockAction(GarlicClove.ACTION_STATUS);
|
|
||||||
includeBlock = true;
|
|
||||||
} else if (config.getReplyInstructions() != null) {
|
|
||||||
clove.setSourceRouteBlockAction(GarlicClove.ACTION_MESSAGE_SPECIFIC);
|
|
||||||
includeBlock = true;
|
|
||||||
} else {
|
|
||||||
clove.setSourceRouteBlockAction(GarlicClove.ACTION_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (includeBlock) {
|
|
||||||
log.debug("Specifying source route block");
|
|
||||||
|
|
||||||
SessionKey replySessionKey = ctx.keyGenerator().generateSessionKey();
|
|
||||||
SessionTag tag = new SessionTag(true);
|
|
||||||
|
|
||||||
// make it so we'll read the session tag correctly and use the right session key
|
|
||||||
HashSet tags = new HashSet(1);
|
|
||||||
tags.add(tag);
|
|
||||||
ctx.sessionKeyManager().tagsReceived(replySessionKey, tags);
|
|
||||||
|
|
||||||
SourceRouteBlock block = new SourceRouteBlock();
|
|
||||||
PublicKey pk = config.getReplyThroughRouter().getIdentity().getPublicKey();
|
|
||||||
block.setData(ctx, config.getReplyInstructions(), config.getReplyBlockMessageId(),
|
|
||||||
config.getReplyBlockCertificate(), config.getReplyBlockExpiration(), pk);
|
|
||||||
block.setRouter(config.getReplyThroughRouter().getIdentity().getHash());
|
|
||||||
block.setKey(replySessionKey);
|
|
||||||
block.setTag(tag);
|
|
||||||
clove.setSourceRouteBlock(block);
|
|
||||||
} else {
|
|
||||||
clove.setSourceRouteBlock(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ import net.i2p.data.Hash;
|
|||||||
import net.i2p.data.RouterIdentity;
|
import net.i2p.data.RouterIdentity;
|
||||||
import net.i2p.data.i2np.GarlicMessage;
|
import net.i2p.data.i2np.GarlicMessage;
|
||||||
import net.i2p.data.i2np.I2NPMessage;
|
import net.i2p.data.i2np.I2NPMessage;
|
||||||
import net.i2p.data.i2np.SourceRouteBlock;
|
|
||||||
import net.i2p.router.HandlerJobBuilder;
|
import net.i2p.router.HandlerJobBuilder;
|
||||||
import net.i2p.router.Job;
|
import net.i2p.router.Job;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
@ -28,8 +27,7 @@ public class GarlicMessageHandler implements HandlerJobBuilder {
|
|||||||
_context = context;
|
_context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) {
|
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) {
|
||||||
// ignore the reply block for the moment
|
|
||||||
HandleGarlicMessageJob job = new HandleGarlicMessageJob(_context, (GarlicMessage)receivedMessage, from, fromHash);
|
HandleGarlicMessageJob job = new HandleGarlicMessageJob(_context, (GarlicMessage)receivedMessage, from, fromHash);
|
||||||
return job;
|
return job;
|
||||||
}
|
}
|
||||||
|
@ -165,10 +165,8 @@ public class HandleGarlicMessageJob extends JobImpl {
|
|||||||
_log.warn("Invalid clove " + clove);
|
_log.warn("Invalid clove " + clove);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean requestAck = (clove.getSourceRouteBlockAction() == GarlicClove.ACTION_STATUS);
|
|
||||||
long sendExpiration = clove.getExpiration().getTime();
|
long sendExpiration = clove.getExpiration().getTime();
|
||||||
_handler.handleMessage(clove.getInstructions(), clove.getData(),
|
_handler.handleMessage(clove.getInstructions(), clove.getData(),
|
||||||
requestAck, clove.getSourceRouteBlock(),
|
|
||||||
clove.getCloveId(), _from, _fromHash,
|
clove.getCloveId(), _from, _fromHash,
|
||||||
sendExpiration, FORWARD_PRIORITY);
|
sendExpiration, FORWARD_PRIORITY);
|
||||||
}
|
}
|
||||||
|
@ -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.DataMessage;
|
||||||
import net.i2p.data.i2np.DeliveryInstructions;
|
import net.i2p.data.i2np.DeliveryInstructions;
|
||||||
import net.i2p.data.i2np.I2NPMessage;
|
import net.i2p.data.i2np.I2NPMessage;
|
||||||
import net.i2p.data.i2np.SourceRouteBlock;
|
|
||||||
import net.i2p.data.i2np.TunnelMessage;
|
import net.i2p.data.i2np.TunnelMessage;
|
||||||
import net.i2p.router.ClientMessage;
|
import net.i2p.router.ClientMessage;
|
||||||
import net.i2p.router.InNetMessage;
|
import net.i2p.router.InNetMessage;
|
||||||
@ -41,7 +40,6 @@ class MessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void handleMessage(DeliveryInstructions instructions, I2NPMessage message,
|
public void handleMessage(DeliveryInstructions instructions, I2NPMessage message,
|
||||||
boolean requestAck, SourceRouteBlock replyBlock,
|
|
||||||
long replyId, RouterIdentity from, Hash fromHash,
|
long replyId, RouterIdentity from, Hash fromHash,
|
||||||
long expiration, int priority) {
|
long expiration, int priority) {
|
||||||
switch (instructions.getDeliveryMode()) {
|
switch (instructions.getDeliveryMode()) {
|
||||||
@ -50,7 +48,7 @@ class MessageHandler {
|
|||||||
if (message.getType() == DataMessage.MESSAGE_TYPE) {
|
if (message.getType() == DataMessage.MESSAGE_TYPE) {
|
||||||
handleLocalDestination(instructions, message, fromHash);
|
handleLocalDestination(instructions, message, fromHash);
|
||||||
} else {
|
} else {
|
||||||
handleLocalRouter(message, from, fromHash, replyBlock, requestAck);
|
handleLocalRouter(message, from, fromHash);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DeliveryInstructions.DELIVERY_MODE_ROUTER:
|
case DeliveryInstructions.DELIVERY_MODE_ROUTER:
|
||||||
@ -58,7 +56,7 @@ class MessageHandler {
|
|||||||
_log.debug("Instructions for ROUTER DELIVERY to "
|
_log.debug("Instructions for ROUTER DELIVERY to "
|
||||||
+ instructions.getRouter().toBase64());
|
+ instructions.getRouter().toBase64());
|
||||||
if (_context.routerHash().equals(instructions.getRouter())) {
|
if (_context.routerHash().equals(instructions.getRouter())) {
|
||||||
handleLocalRouter(message, from, fromHash, replyBlock, requestAck);
|
handleLocalRouter(message, from, fromHash);
|
||||||
} else {
|
} else {
|
||||||
handleRemoteRouter(message, instructions, expiration, priority);
|
handleRemoteRouter(message, instructions, expiration, priority);
|
||||||
}
|
}
|
||||||
@ -84,28 +82,14 @@ class MessageHandler {
|
|||||||
_log.error("Message has instructions that are not yet implemented: mode = " + instructions.getDeliveryMode());
|
_log.error("Message has instructions that are not yet implemented: mode = " + instructions.getDeliveryMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requestAck) {
|
|
||||||
_log.debug("SEND ACK REQUESTED");
|
|
||||||
sendAck(replyBlock, replyId);
|
|
||||||
} else {
|
|
||||||
_log.debug("No ack requested");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendAck(SourceRouteBlock replyBlock, long replyId) {
|
private void handleLocalRouter(I2NPMessage message, RouterIdentity from, Hash fromHash) {
|
||||||
_log.info("Queueing up ack job via reply block " + replyBlock);
|
|
||||||
Job ackJob = new SendMessageAckJob(_context, replyBlock, replyId);
|
|
||||||
_context.jobQueue().addJob(ackJob);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleLocalRouter(I2NPMessage message, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock, boolean ackUsed) {
|
|
||||||
_log.info("Handle " + message.getClass().getName() + " to a local router - toss it on the inbound network pool");
|
_log.info("Handle " + message.getClass().getName() + " to a local router - toss it on the inbound network pool");
|
||||||
InNetMessage msg = new InNetMessage(_context);
|
InNetMessage msg = new InNetMessage(_context);
|
||||||
msg.setFromRouter(from);
|
msg.setFromRouter(from);
|
||||||
msg.setFromRouterHash(fromHash);
|
msg.setFromRouterHash(fromHash);
|
||||||
msg.setMessage(message);
|
msg.setMessage(message);
|
||||||
if (!ackUsed)
|
|
||||||
msg.setReplyBlock(replyBlock);
|
|
||||||
_context.inNetMessagePool().add(msg);
|
_context.inNetMessagePool().add(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.setFromRouter(ident);
|
||||||
msg.setFromRouterHash(ident.getHash());
|
msg.setFromRouterHash(ident.getHash());
|
||||||
msg.setMessage(_message);
|
msg.setMessage(_message);
|
||||||
msg.setReplyBlock(null);
|
|
||||||
getContext().inNetMessagePool().add(msg);
|
getContext().inNetMessagePool().add(msg);
|
||||||
} else {
|
} else {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
@ -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.Hash;
|
||||||
import net.i2p.data.RouterIdentity;
|
import net.i2p.data.RouterIdentity;
|
||||||
import net.i2p.data.i2np.I2NPMessage;
|
import net.i2p.data.i2np.I2NPMessage;
|
||||||
import net.i2p.data.i2np.SourceRouteBlock;
|
|
||||||
import net.i2p.data.i2np.TunnelMessage;
|
import net.i2p.data.i2np.TunnelMessage;
|
||||||
import net.i2p.router.HandlerJobBuilder;
|
import net.i2p.router.HandlerJobBuilder;
|
||||||
import net.i2p.router.Job;
|
import net.i2p.router.Job;
|
||||||
@ -27,8 +26,7 @@ public class TunnelMessageHandler implements HandlerJobBuilder {
|
|||||||
public TunnelMessageHandler(RouterContext context) {
|
public TunnelMessageHandler(RouterContext context) {
|
||||||
_context = context;
|
_context = context;
|
||||||
}
|
}
|
||||||
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) {
|
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) {
|
||||||
// ignore the replyBlock for now
|
|
||||||
HandleTunnelMessageJob job = new HandleTunnelMessageJob(_context, (TunnelMessage)receivedMessage, from, fromHash);
|
HandleTunnelMessageJob job = new HandleTunnelMessageJob(_context, (TunnelMessage)receivedMessage, from, fromHash);
|
||||||
return job;
|
return job;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ import net.i2p.data.Hash;
|
|||||||
import net.i2p.data.RouterIdentity;
|
import net.i2p.data.RouterIdentity;
|
||||||
import net.i2p.data.i2np.DatabaseLookupMessage;
|
import net.i2p.data.i2np.DatabaseLookupMessage;
|
||||||
import net.i2p.data.i2np.I2NPMessage;
|
import net.i2p.data.i2np.I2NPMessage;
|
||||||
import net.i2p.data.i2np.SourceRouteBlock;
|
|
||||||
import net.i2p.router.HandlerJobBuilder;
|
import net.i2p.router.HandlerJobBuilder;
|
||||||
import net.i2p.router.Job;
|
import net.i2p.router.Job;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
@ -32,7 +31,7 @@ public class DatabaseLookupMessageHandler implements HandlerJobBuilder {
|
|||||||
_context.statManager().createRateStat("netDb.lookupsDropped", "How many netDb lookups did we drop due to throttling?", "Network Database", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
|
_context.statManager().createRateStat("netDb.lookupsDropped", "How many netDb lookups did we drop due to throttling?", "Network Database", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||||
}
|
}
|
||||||
|
|
||||||
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) {
|
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) {
|
||||||
_context.statManager().addRateData("netDb.lookupsReceived", 1, 0);
|
_context.statManager().addRateData("netDb.lookupsReceived", 1, 0);
|
||||||
|
|
||||||
if (_context.throttle().acceptNetDbLookupRequest(((DatabaseLookupMessage)receivedMessage).getSearchKey())) {
|
if (_context.throttle().acceptNetDbLookupRequest(((DatabaseLookupMessage)receivedMessage).getSearchKey())) {
|
||||||
|
@ -12,7 +12,6 @@ import net.i2p.data.Hash;
|
|||||||
import net.i2p.data.RouterIdentity;
|
import net.i2p.data.RouterIdentity;
|
||||||
import net.i2p.data.i2np.DatabaseStoreMessage;
|
import net.i2p.data.i2np.DatabaseStoreMessage;
|
||||||
import net.i2p.data.i2np.I2NPMessage;
|
import net.i2p.data.i2np.I2NPMessage;
|
||||||
import net.i2p.data.i2np.SourceRouteBlock;
|
|
||||||
import net.i2p.router.HandlerJobBuilder;
|
import net.i2p.router.HandlerJobBuilder;
|
||||||
import net.i2p.router.Job;
|
import net.i2p.router.Job;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
@ -26,8 +25,7 @@ public class DatabaseStoreMessageHandler implements HandlerJobBuilder {
|
|||||||
public DatabaseStoreMessageHandler(RouterContext context) {
|
public DatabaseStoreMessageHandler(RouterContext context) {
|
||||||
_context = context;
|
_context = context;
|
||||||
}
|
}
|
||||||
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) {
|
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) {
|
||||||
// ignore the reply block for the moment
|
|
||||||
return new HandleDatabaseStoreMessageJob(_context, (DatabaseStoreMessage)receivedMessage, from, fromHash);
|
return new HandleDatabaseStoreMessageJob(_context, (DatabaseStoreMessage)receivedMessage, from, fromHash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,20 +9,32 @@ package net.i2p.router.tunnelmanager;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.i2p.data.Certificate;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
import net.i2p.data.RouterIdentity;
|
import net.i2p.data.RouterIdentity;
|
||||||
import net.i2p.data.RouterInfo;
|
import net.i2p.data.RouterInfo;
|
||||||
import net.i2p.data.TunnelId;
|
import net.i2p.data.TunnelId;
|
||||||
import net.i2p.data.i2np.SourceRouteBlock;
|
|
||||||
import net.i2p.data.i2np.TunnelCreateMessage;
|
import net.i2p.data.i2np.TunnelCreateMessage;
|
||||||
|
import net.i2p.data.i2np.DeliveryInstructions;
|
||||||
|
import net.i2p.data.i2np.GarlicClove;
|
||||||
|
import net.i2p.data.i2np.GarlicMessage;
|
||||||
|
import net.i2p.data.i2np.I2NPMessage;
|
||||||
import net.i2p.data.i2np.TunnelCreateStatusMessage;
|
import net.i2p.data.i2np.TunnelCreateStatusMessage;
|
||||||
|
import net.i2p.router.Job;
|
||||||
import net.i2p.router.JobImpl;
|
import net.i2p.router.JobImpl;
|
||||||
|
import net.i2p.router.MessageSelector;
|
||||||
|
import net.i2p.router.ReplyJob;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
import net.i2p.router.TunnelInfo;
|
import net.i2p.router.TunnelInfo;
|
||||||
import net.i2p.router.TunnelSettings;
|
import net.i2p.router.TunnelSettings;
|
||||||
|
import net.i2p.router.TunnelSelectionCriteria;
|
||||||
import net.i2p.router.message.BuildTestMessageJob;
|
import net.i2p.router.message.BuildTestMessageJob;
|
||||||
import net.i2p.router.message.SendReplyMessageJob;
|
import net.i2p.router.message.GarlicConfig;
|
||||||
|
import net.i2p.router.message.GarlicMessageBuilder;
|
||||||
|
import net.i2p.router.message.PayloadGarlicConfig;
|
||||||
|
import net.i2p.router.message.SendTunnelMessageJob;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
public class HandleTunnelCreateMessageJob extends JobImpl {
|
public class HandleTunnelCreateMessageJob extends JobImpl {
|
||||||
@ -30,20 +42,18 @@ public class HandleTunnelCreateMessageJob extends JobImpl {
|
|||||||
private TunnelCreateMessage _message;
|
private TunnelCreateMessage _message;
|
||||||
private RouterIdentity _from;
|
private RouterIdentity _from;
|
||||||
private Hash _fromHash;
|
private Hash _fromHash;
|
||||||
private SourceRouteBlock _replyBlock;
|
|
||||||
|
|
||||||
private final static long TIMEOUT = 30*1000; // 30 secs to contact a peer that will be our next hop
|
private final static long TIMEOUT = 30*1000; // 30 secs to contact a peer that will be our next hop
|
||||||
private final static int PRIORITY = 123;
|
private final static int PRIORITY = 123;
|
||||||
|
|
||||||
HandleTunnelCreateMessageJob(RouterContext ctx, TunnelCreateMessage receivedMessage,
|
HandleTunnelCreateMessageJob(RouterContext ctx, TunnelCreateMessage receivedMessage,
|
||||||
RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) {
|
RouterIdentity from, Hash fromHash) {
|
||||||
super(ctx);
|
super(ctx);
|
||||||
_log = ctx.logManager().getLog(HandleTunnelCreateMessageJob.class);
|
_log = ctx.logManager().getLog(HandleTunnelCreateMessageJob.class);
|
||||||
ctx.statManager().createRateStat("tunnel.rejectOverloaded", "How many tunnels did we deny due to throttling?", "Tunnels", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
|
ctx.statManager().createRateStat("tunnel.rejectOverloaded", "How many tunnels did we deny due to throttling?", "Tunnels", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||||
_message = receivedMessage;
|
_message = receivedMessage;
|
||||||
_from = from;
|
_from = from;
|
||||||
_fromHash = fromHash;
|
_fromHash = fromHash;
|
||||||
_replyBlock = replyBlock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void runJob() {
|
public void runJob() {
|
||||||
@ -119,15 +129,16 @@ public class HandleTunnelCreateMessageJob extends JobImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final long REPLY_TIMEOUT = 10*1000;
|
||||||
private void sendReply(boolean ok) {
|
private void sendReply(boolean ok) {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Sending reply to a tunnel create of id " + _message.getTunnelId()
|
_log.debug("Sending reply to a tunnel create of id " + _message.getTunnelId()
|
||||||
+ " with ok (" + ok + ") to router " + _message.getReplyBlock().getRouter().toBase64());
|
+ " with ok (" + ok + ") to tunnel " + _message.getReplyTunnel()
|
||||||
|
+ " on router " + _message.getReplyPeer());
|
||||||
|
|
||||||
getContext().messageHistory().receiveTunnelCreate(_message.getTunnelId(), _message.getNextRouter(),
|
getContext().messageHistory().receiveTunnelCreate(_message.getTunnelId(), _message.getNextRouter(),
|
||||||
new Date(getContext().clock().now() + 1000*_message.getTunnelDurationSeconds()),
|
new Date(getContext().clock().now() + 1000*_message.getTunnelDurationSeconds()),
|
||||||
ok, _message.getReplyBlock().getRouter());
|
ok, _message.getReplyPeer());
|
||||||
|
|
||||||
TunnelCreateStatusMessage msg = new TunnelCreateStatusMessage(getContext());
|
TunnelCreateStatusMessage msg = new TunnelCreateStatusMessage(getContext());
|
||||||
msg.setFromHash(getContext().routerHash());
|
msg.setFromHash(getContext().routerHash());
|
||||||
@ -139,10 +150,88 @@ public class HandleTunnelCreateMessageJob extends JobImpl {
|
|||||||
msg.setStatus(TunnelCreateStatusMessage.STATUS_FAILED_OVERLOADED);
|
msg.setStatus(TunnelCreateStatusMessage.STATUS_FAILED_OVERLOADED);
|
||||||
}
|
}
|
||||||
msg.setMessageExpiration(new Date(getContext().clock().now()+60*1000));
|
msg.setMessageExpiration(new Date(getContext().clock().now()+60*1000));
|
||||||
SendReplyMessageJob job = new SendReplyMessageJob(getContext(), _message.getReplyBlock(), msg, PRIORITY);
|
|
||||||
|
// put that message into a garlic
|
||||||
|
GarlicMessage reply = createReply(msg);
|
||||||
|
|
||||||
|
TunnelId outTunnelId = selectReplyTunnel();
|
||||||
|
|
||||||
|
SendTunnelMessageJob job = new SendTunnelMessageJob(getContext(), reply, outTunnelId,
|
||||||
|
_message.getReplyPeer(),
|
||||||
|
_message.getReplyTunnel(),
|
||||||
|
(Job)null, (ReplyJob)null,
|
||||||
|
(Job)null, (MessageSelector)null,
|
||||||
|
REPLY_TIMEOUT, PRIORITY);
|
||||||
getContext().jobQueue().addJob(job);
|
getContext().jobQueue().addJob(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private GarlicMessage createReply(TunnelCreateStatusMessage body) {
|
||||||
|
GarlicConfig cfg = createReplyConfig(body);
|
||||||
|
return GarlicMessageBuilder.buildMessage(getContext(), cfg, null, null, null,
|
||||||
|
_message.getReplyKey(), _message.getReplyTag());
|
||||||
|
}
|
||||||
|
|
||||||
|
private GarlicConfig createReplyConfig(TunnelCreateStatusMessage body) {
|
||||||
|
GarlicConfig config = new GarlicConfig();
|
||||||
|
|
||||||
|
PayloadGarlicConfig replyClove = buildReplyClove(body);
|
||||||
|
config.addClove(replyClove);
|
||||||
|
|
||||||
|
DeliveryInstructions instructions = new DeliveryInstructions();
|
||||||
|
instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_LOCAL);
|
||||||
|
instructions.setDelayRequested(false);
|
||||||
|
instructions.setDelaySeconds(0);
|
||||||
|
instructions.setEncrypted(false);
|
||||||
|
instructions.setEncryptionKey(null);
|
||||||
|
instructions.setRouter(null);
|
||||||
|
instructions.setTunnelId(null);
|
||||||
|
|
||||||
|
config.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
|
||||||
|
config.setDeliveryInstructions(instructions);
|
||||||
|
config.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||||
|
config.setExpiration(REPLY_TIMEOUT+getContext().clock().now());
|
||||||
|
config.setRecipient(null);
|
||||||
|
config.setRequestAck(false);
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a clove that sends the tunnel create reply
|
||||||
|
*/
|
||||||
|
private PayloadGarlicConfig buildReplyClove(TunnelCreateStatusMessage body) {
|
||||||
|
PayloadGarlicConfig replyClove = new PayloadGarlicConfig();
|
||||||
|
|
||||||
|
DeliveryInstructions instructions = new DeliveryInstructions();
|
||||||
|
instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_LOCAL);
|
||||||
|
instructions.setRouter(null);
|
||||||
|
instructions.setDelayRequested(false);
|
||||||
|
instructions.setDelaySeconds(0);
|
||||||
|
instructions.setEncrypted(false);
|
||||||
|
|
||||||
|
replyClove.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
|
||||||
|
replyClove.setDeliveryInstructions(instructions);
|
||||||
|
replyClove.setExpiration(REPLY_TIMEOUT+getContext().clock().now());
|
||||||
|
replyClove.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||||
|
replyClove.setPayload(body);
|
||||||
|
replyClove.setRecipient(null);
|
||||||
|
replyClove.setRequestAck(false);
|
||||||
|
|
||||||
|
return replyClove;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private TunnelId selectReplyTunnel() {
|
||||||
|
TunnelSelectionCriteria crit = new TunnelSelectionCriteria();
|
||||||
|
crit.setMinimumTunnelsRequired(1);
|
||||||
|
crit.setMaximumTunnelsRequired(1);
|
||||||
|
List ids = getContext().tunnelManager().selectOutboundTunnelIds(crit);
|
||||||
|
if ( (ids != null) && (ids.size() > 0) )
|
||||||
|
return (TunnelId)ids.get(0);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public String getName() { return "Handle Tunnel Create Message"; }
|
public String getName() { return "Handle Tunnel Create Message"; }
|
||||||
|
|
||||||
private class JoinJob extends JobImpl {
|
private class JoinJob extends JobImpl {
|
||||||
|
@ -28,7 +28,6 @@ import net.i2p.data.i2np.DeliveryInstructions;
|
|||||||
import net.i2p.data.i2np.DeliveryStatusMessage;
|
import net.i2p.data.i2np.DeliveryStatusMessage;
|
||||||
import net.i2p.data.i2np.GarlicMessage;
|
import net.i2p.data.i2np.GarlicMessage;
|
||||||
import net.i2p.data.i2np.I2NPMessage;
|
import net.i2p.data.i2np.I2NPMessage;
|
||||||
import net.i2p.data.i2np.SourceRouteBlock;
|
|
||||||
import net.i2p.data.i2np.TunnelCreateMessage;
|
import net.i2p.data.i2np.TunnelCreateMessage;
|
||||||
import net.i2p.data.i2np.TunnelCreateStatusMessage;
|
import net.i2p.data.i2np.TunnelCreateStatusMessage;
|
||||||
import net.i2p.router.Job;
|
import net.i2p.router.Job;
|
||||||
@ -163,18 +162,8 @@ public class RequestTunnelJob extends JobImpl {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// select reply peer [peer to which SourceRouteReply should be sent, and
|
|
||||||
// from which the reply will be forwarded to an inbound tunnel]
|
|
||||||
RouterInfo replyPeer = selectReplyPeer(participant);
|
|
||||||
if (replyPeer == null) {
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("No reply peers available! unable to request a new tunnel!");
|
|
||||||
fail();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// select inbound tunnel gateway
|
// select inbound tunnel gateway
|
||||||
TunnelGateway inboundGateway = selectInboundGateway(participant, replyPeer);
|
TunnelGateway inboundGateway = selectInboundGateway(participant);
|
||||||
if (inboundGateway == null) {
|
if (inboundGateway == null) {
|
||||||
if (_log.shouldLog(Log.ERROR))
|
if (_log.shouldLog(Log.ERROR))
|
||||||
_log.error("Unable to find an inbound gateway");
|
_log.error("Unable to find an inbound gateway");
|
||||||
@ -187,7 +176,7 @@ public class RequestTunnelJob extends JobImpl {
|
|||||||
PublicKey wrappedTo = new PublicKey();
|
PublicKey wrappedTo = new PublicKey();
|
||||||
|
|
||||||
RequestState state = new RequestState(wrappedKey, wrappedTags, wrappedTo,
|
RequestState state = new RequestState(wrappedKey, wrappedTags, wrappedTo,
|
||||||
participant, inboundGateway, replyPeer,
|
participant, inboundGateway,
|
||||||
outboundTunnel, target);
|
outboundTunnel, target);
|
||||||
Request r = new Request(state);
|
Request r = new Request(state);
|
||||||
getContext().jobQueue().addJob(r);
|
getContext().jobQueue().addJob(r);
|
||||||
@ -217,7 +206,6 @@ public class RequestTunnelJob extends JobImpl {
|
|||||||
_state.getOutboundTunnel(),
|
_state.getOutboundTunnel(),
|
||||||
_state.getParticipant().getThisHop(),
|
_state.getParticipant().getThisHop(),
|
||||||
_state.getParticipant().getNextHop(),
|
_state.getParticipant().getNextHop(),
|
||||||
_state.getReplyPeer().getIdentity().getHash(),
|
|
||||||
_state.getInboundGateway().getTunnelId(),
|
_state.getInboundGateway().getTunnelId(),
|
||||||
_state.getInboundGateway().getGateway());
|
_state.getInboundGateway().getGateway());
|
||||||
}
|
}
|
||||||
@ -239,54 +227,45 @@ public class RequestTunnelJob extends JobImpl {
|
|||||||
private Set _wrappedTags;
|
private Set _wrappedTags;
|
||||||
private PublicKey _wrappedTo;
|
private PublicKey _wrappedTo;
|
||||||
private TunnelCreateMessage _createMsg;
|
private TunnelCreateMessage _createMsg;
|
||||||
private DeliveryStatusMessage _statusMsg;
|
|
||||||
private GarlicMessage _garlicMessage;
|
private GarlicMessage _garlicMessage;
|
||||||
private TunnelInfo _participant;
|
private TunnelInfo _participant;
|
||||||
private TunnelGateway _inboundGateway;
|
private TunnelGateway _inboundGateway;
|
||||||
private RouterInfo _replyPeer;
|
|
||||||
private TunnelId _outboundTunnel;
|
private TunnelId _outboundTunnel;
|
||||||
private RouterInfo _target;
|
private RouterInfo _target;
|
||||||
|
|
||||||
public RequestState(SessionKey wrappedKey, Set wrappedTags, PublicKey wrappedTo,
|
public RequestState(SessionKey wrappedKey, Set wrappedTags, PublicKey wrappedTo,
|
||||||
TunnelInfo participant, TunnelGateway inboundGateway,
|
TunnelInfo participant, TunnelGateway inboundGateway,
|
||||||
RouterInfo replyPeer, TunnelId outboundTunnel, RouterInfo target) {
|
TunnelId outboundTunnel, RouterInfo target) {
|
||||||
_wrappedKey = wrappedKey;
|
_wrappedKey = wrappedKey;
|
||||||
_wrappedTags = wrappedTags;
|
_wrappedTags = wrappedTags;
|
||||||
_wrappedTo = wrappedTo;
|
_wrappedTo = wrappedTo;
|
||||||
_participant = participant;
|
_participant = participant;
|
||||||
_inboundGateway = inboundGateway;
|
_inboundGateway = inboundGateway;
|
||||||
_replyPeer = replyPeer;
|
|
||||||
_outboundTunnel = outboundTunnel;
|
_outboundTunnel = outboundTunnel;
|
||||||
_target = target;
|
_target = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TunnelId getOutboundTunnel() { return _outboundTunnel; }
|
public TunnelId getOutboundTunnel() { return _outboundTunnel; }
|
||||||
public TunnelInfo getParticipant() { return _participant; }
|
public TunnelInfo getParticipant() { return _participant; }
|
||||||
public RouterInfo getReplyPeer() { return _replyPeer; }
|
|
||||||
public TunnelGateway getInboundGateway() { return _inboundGateway; }
|
public TunnelGateway getInboundGateway() { return _inboundGateway; }
|
||||||
|
|
||||||
public boolean doNext() {
|
public boolean doNext() {
|
||||||
if (_createMsg == null) {
|
if (_createMsg == null) {
|
||||||
_createMsg = buildTunnelCreate(_participant, _inboundGateway, _replyPeer);
|
_createMsg = buildTunnelCreate(_participant, _inboundGateway);
|
||||||
return true;
|
|
||||||
} else if (_statusMsg == null) {
|
|
||||||
_statusMsg = buildDeliveryStatusMessage();
|
|
||||||
return true;
|
return true;
|
||||||
} else if (_garlicMessage == null) {
|
} else if (_garlicMessage == null) {
|
||||||
_garlicMessage = buildGarlicMessage(_createMsg, _statusMsg, _replyPeer,
|
_garlicMessage = buildGarlicMessage(_createMsg, _inboundGateway, _target,
|
||||||
_inboundGateway, _target, _wrappedKey,
|
_wrappedKey, _wrappedTags, _wrappedTo);
|
||||||
_wrappedTags, _wrappedTo);
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// send the GarlicMessage
|
// send the GarlicMessage
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
_log.info("Sending tunnel create to " + _target.getIdentity().getHash().toBase64() +
|
_log.info("Sending tunnel create to " + _target.getIdentity().getHash().toBase64() +
|
||||||
" with replies through " + _replyPeer.getIdentity().getHash().toBase64() +
|
|
||||||
" to inbound gateway " + _inboundGateway.getGateway().toBase64() +
|
" to inbound gateway " + _inboundGateway.getGateway().toBase64() +
|
||||||
" : " + _inboundGateway.getTunnelId().getTunnelId());
|
" : " + _inboundGateway.getTunnelId().getTunnelId());
|
||||||
ReplyJob onReply = new Success(_participant, _wrappedKey, _wrappedTags, _wrappedTo);
|
ReplyJob onReply = new Success(_participant, _wrappedKey, _wrappedTags, _wrappedTo);
|
||||||
Job onFail = new Failure(_participant, _replyPeer.getIdentity().getHash());
|
Job onFail = new Failure(_participant);
|
||||||
MessageSelector selector = new Selector(_participant, _statusMsg.getMessageId());
|
MessageSelector selector = new Selector(_participant);
|
||||||
SendTunnelMessageJob j = new SendTunnelMessageJob(getContext(), _garlicMessage,
|
SendTunnelMessageJob j = new SendTunnelMessageJob(getContext(), _garlicMessage,
|
||||||
_outboundTunnel, _target.getIdentity().getHash(),
|
_outboundTunnel, _target.getIdentity().getHash(),
|
||||||
null, null, onReply, onFail,
|
null, null, onReply, onFail,
|
||||||
@ -330,47 +309,10 @@ public class RequestTunnelJob extends JobImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select a peer to which the tunnelParticipant will send the SourceRouteReplyMessage
|
* Select an inbound tunnel to receive replies and acks from the participant
|
||||||
* containing a garlic wrapped TunnelCreateStatusMessage destined for the local router.
|
|
||||||
*
|
|
||||||
* Currently just a random peer
|
|
||||||
*/
|
|
||||||
private RouterInfo selectReplyPeer(TunnelInfo tunnelParticipant) {
|
|
||||||
PeerSelectionCriteria criteria = new PeerSelectionCriteria();
|
|
||||||
criteria.setMaximumRequired(1);
|
|
||||||
criteria.setMinimumRequired(1);
|
|
||||||
criteria.setPurpose(PeerSelectionCriteria.PURPOSE_SOURCE_ROUTE);
|
|
||||||
List peerHashes = getContext().peerManager().selectPeers(criteria);
|
|
||||||
|
|
||||||
RouterInfo peerInfo = null;
|
|
||||||
for (int i = 0; (i < peerHashes.size()) && (peerInfo == null); i++) {
|
|
||||||
Hash peerHash = (Hash)peerHashes.get(i);
|
|
||||||
peerInfo = getContext().netDb().lookupRouterInfoLocally(peerHash);
|
|
||||||
if (peerInfo == null) {
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Selected a peer [" + peerHash + "] we don't have info on locally... trying another");
|
|
||||||
} else {
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Peer [" + peerHash.toBase64() + "] is known locally, keep it in the list of replyPeers");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (peerInfo == null) {
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("No peers know for a reply (out of " + peerHashes.size() + ") - using ourself");
|
|
||||||
return getContext().router().getRouterInfo();
|
|
||||||
} else {
|
|
||||||
return peerInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Select an inbound tunnel to receive replies and acks from the participant by means of the
|
|
||||||
* replyPeer
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private TunnelGateway selectInboundGateway(TunnelInfo participant, RouterInfo replyPeer) {
|
private TunnelGateway selectInboundGateway(TunnelInfo participant) {
|
||||||
TunnelSelectionCriteria criteria = new TunnelSelectionCriteria();
|
TunnelSelectionCriteria criteria = new TunnelSelectionCriteria();
|
||||||
criteria.setAnonymityPriority(66);
|
criteria.setAnonymityPriority(66);
|
||||||
criteria.setReliabilityPriority(66);
|
criteria.setReliabilityPriority(66);
|
||||||
@ -408,7 +350,7 @@ public class RequestTunnelJob extends JobImpl {
|
|||||||
/**
|
/**
|
||||||
* Build a TunnelCreateMessage to the participant
|
* Build a TunnelCreateMessage to the participant
|
||||||
*/
|
*/
|
||||||
private TunnelCreateMessage buildTunnelCreate(TunnelInfo participant, TunnelGateway replyGateway, RouterInfo replyPeer) {
|
private TunnelCreateMessage buildTunnelCreate(TunnelInfo participant, TunnelGateway replyGateway) {
|
||||||
TunnelCreateMessage msg = new TunnelCreateMessage(getContext());
|
TunnelCreateMessage msg = new TunnelCreateMessage(getContext());
|
||||||
msg.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
|
msg.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
|
||||||
msg.setConfigurationKey(participant.getConfigurationKey());
|
msg.setConfigurationKey(participant.getConfigurationKey());
|
||||||
@ -418,6 +360,9 @@ public class RequestTunnelJob extends JobImpl {
|
|||||||
msg.setMaxPeakBytesPerMin(participant.getSettings().getBytesPerMinutePeak());
|
msg.setMaxPeakBytesPerMin(participant.getSettings().getBytesPerMinutePeak());
|
||||||
msg.setMaxPeakMessagesPerMin(participant.getSettings().getMessagesPerMinutePeak());
|
msg.setMaxPeakMessagesPerMin(participant.getSettings().getMessagesPerMinutePeak());
|
||||||
msg.setNextRouter(participant.getNextHop());
|
msg.setNextRouter(participant.getNextHop());
|
||||||
|
// TODO: update the TunnelInfo structure so we can have the tunnel contain
|
||||||
|
// different tunnelIds per hop
|
||||||
|
msg.setNextTunnelId(participant.getTunnelId());
|
||||||
if (participant.getNextHop() == null)
|
if (participant.getNextHop() == null)
|
||||||
msg.setParticipantType(TunnelCreateMessage.PARTICIPANT_TYPE_ENDPOINT);
|
msg.setParticipantType(TunnelCreateMessage.PARTICIPANT_TYPE_ENDPOINT);
|
||||||
else if (participant.getSigningKey() != null)
|
else if (participant.getSigningKey() != null)
|
||||||
@ -426,11 +371,19 @@ public class RequestTunnelJob extends JobImpl {
|
|||||||
msg.setParticipantType(TunnelCreateMessage.PARTICIPANT_TYPE_OTHER);
|
msg.setParticipantType(TunnelCreateMessage.PARTICIPANT_TYPE_OTHER);
|
||||||
msg.setReorderMessages(participant.getSettings().getReorder());
|
msg.setReorderMessages(participant.getSettings().getReorder());
|
||||||
|
|
||||||
SourceRouteBlock replyBlock = buildReplyBlock(replyGateway, replyPeer);
|
|
||||||
if (replyBlock == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
msg.setReplyBlock(replyBlock);
|
SessionKey replySessionKey = getContext().keyGenerator().generateSessionKey();
|
||||||
|
SessionTag tag = new SessionTag(true);
|
||||||
|
Set tags = new HashSet();
|
||||||
|
tags.add(tag);
|
||||||
|
// make it so we'll read the session tag correctly and use the right session key
|
||||||
|
getContext().sessionKeyManager().tagsReceived(replySessionKey, tags);
|
||||||
|
|
||||||
|
msg.setReplyPeer(replyGateway.getGateway());
|
||||||
|
msg.setReplyTunnel(replyGateway.getTunnelId());
|
||||||
|
msg.setReplyKey(replySessionKey);
|
||||||
|
msg.setReplyTag(tag);
|
||||||
|
|
||||||
long duration = participant.getSettings().getExpiration() - getContext().clock().now();
|
long duration = participant.getSettings().getExpiration() - getContext().clock().now();
|
||||||
if (duration == 0) duration = 1;
|
if (duration == 0) duration = 1;
|
||||||
msg.setTunnelDurationSeconds(duration/1000);
|
msg.setTunnelDurationSeconds(duration/1000);
|
||||||
@ -442,97 +395,16 @@ public class RequestTunnelJob extends JobImpl {
|
|||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a source route block directing the reply through the gateway by means of the
|
|
||||||
* replyPeer
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
private SourceRouteBlock buildReplyBlock(TunnelGateway gateway, RouterInfo replyPeer) {
|
|
||||||
if (replyPeer == null) {
|
|
||||||
if (_log.shouldLog(Log.ERROR))
|
|
||||||
_log.error("No peer specified for reply!");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
SessionKey replySessionKey = getContext().keyGenerator().generateSessionKey();
|
|
||||||
SessionTag tag = new SessionTag(true);
|
|
||||||
Set tags = new HashSet();
|
|
||||||
tags.add(tag);
|
|
||||||
// make it so we'll read the session tag correctly and use the right session key
|
|
||||||
getContext().sessionKeyManager().tagsReceived(replySessionKey, tags);
|
|
||||||
|
|
||||||
PublicKey pk = replyPeer.getIdentity().getPublicKey();
|
|
||||||
|
|
||||||
DeliveryInstructions instructions = new DeliveryInstructions();
|
|
||||||
instructions.setDelayRequested(false);
|
|
||||||
instructions.setDelaySeconds(0);
|
|
||||||
instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_TUNNEL);
|
|
||||||
instructions.setDestination(null);
|
|
||||||
instructions.setEncrypted(false);
|
|
||||||
instructions.setEncryptionKey(null);
|
|
||||||
instructions.setRouter(gateway.getGateway());
|
|
||||||
instructions.setTunnelId(gateway.getTunnelId());
|
|
||||||
|
|
||||||
long replyId = getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE);
|
|
||||||
|
|
||||||
Certificate replyCert = new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null);
|
|
||||||
|
|
||||||
long expiration = getContext().clock().now() + _timeoutMs; // _expiration;
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Setting the expiration on the reply block to " + (new Date(expiration)));
|
|
||||||
SourceRouteBlock block = new SourceRouteBlock();
|
|
||||||
try {
|
|
||||||
long begin = getContext().clock().now();
|
|
||||||
block.setData(getContext(), instructions, replyId, replyCert, expiration, pk);
|
|
||||||
long end = getContext().clock().now();
|
|
||||||
if ( (end - begin) > 1000) {
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Took too long (" + (end-begin) + "ms) to build source route block");
|
|
||||||
} else {
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("did NOT take long (" + (end-begin) + "ms) to build source route block!");
|
|
||||||
}
|
|
||||||
} catch (DataFormatException dfe) {
|
|
||||||
if (_log.shouldLog(Log.ERROR))
|
|
||||||
_log.error("Error building the reply block", dfe);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
block.setRouter(replyPeer.getIdentity().getHash());
|
|
||||||
block.setKey(replySessionKey);
|
|
||||||
block.setTag(tag);
|
|
||||||
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a message containing a random id to check for after garlic routing
|
|
||||||
* it out so that we know the other message in the garlic has been received
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
private DeliveryStatusMessage buildDeliveryStatusMessage() {
|
|
||||||
DeliveryStatusMessage msg = new DeliveryStatusMessage(getContext());
|
|
||||||
msg.setArrival(new Date(getContext().clock().now()));
|
|
||||||
msg.setMessageId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
|
||||||
Date exp = new Date(getContext().clock().now() + _timeoutMs); // _expiration);
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Setting the expiration on the delivery status message to " + exp);
|
|
||||||
msg.setMessageExpiration(exp);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a garlic message wrapping the data and status as cloves with both to be routed
|
* Build a garlic message wrapping the data and status as cloves with both to be routed
|
||||||
* through the target, where the data is destined. The status however is to continue on
|
* through the target, where the data is destined.
|
||||||
* to the replyPeer, where it is then sent down the replyTunnel to the local router.
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private GarlicMessage buildGarlicMessage(I2NPMessage data, I2NPMessage status,
|
private GarlicMessage buildGarlicMessage(I2NPMessage data,
|
||||||
RouterInfo replyPeer, TunnelGateway replyTunnel,
|
TunnelGateway replyTunnel,
|
||||||
RouterInfo target, SessionKey wrappedKey,
|
RouterInfo target, SessionKey wrappedKey,
|
||||||
Set wrappedTags, PublicKey wrappedTo) {
|
Set wrappedTags, PublicKey wrappedTo) {
|
||||||
GarlicConfig config = buildGarlicConfig(data, status, replyPeer, replyTunnel, target);
|
GarlicConfig config = buildGarlicConfig(data, replyTunnel, target);
|
||||||
|
|
||||||
PublicKey rcptKey = config.getRecipientPublicKey();
|
PublicKey rcptKey = config.getRecipientPublicKey();
|
||||||
if (rcptKey == null) {
|
if (rcptKey == null) {
|
||||||
@ -562,16 +434,13 @@ public class RequestTunnelJob extends JobImpl {
|
|||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
private GarlicConfig buildGarlicConfig(I2NPMessage data, I2NPMessage status,
|
private GarlicConfig buildGarlicConfig(I2NPMessage data,
|
||||||
RouterInfo replyPeer, TunnelGateway replyTunnel,
|
TunnelGateway replyTunnel, RouterInfo target) {
|
||||||
RouterInfo target) {
|
|
||||||
GarlicConfig config = new GarlicConfig();
|
GarlicConfig config = new GarlicConfig();
|
||||||
|
|
||||||
long garlicExpiration = getContext().clock().now() + _timeoutMs;
|
long garlicExpiration = getContext().clock().now() + _timeoutMs;
|
||||||
PayloadGarlicConfig dataClove = buildDataClove(data, target, garlicExpiration);
|
PayloadGarlicConfig dataClove = buildDataClove(data, target, garlicExpiration);
|
||||||
config.addClove(dataClove);
|
config.addClove(dataClove);
|
||||||
PayloadGarlicConfig ackClove = buildAckClove(status, replyPeer, replyTunnel, garlicExpiration);
|
|
||||||
config.addClove(ackClove);
|
|
||||||
|
|
||||||
DeliveryInstructions instructions = new DeliveryInstructions();
|
DeliveryInstructions instructions = new DeliveryInstructions();
|
||||||
instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_ROUTER);
|
instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_ROUTER);
|
||||||
@ -590,40 +459,10 @@ public class RequestTunnelJob extends JobImpl {
|
|||||||
config.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
config.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||||
config.setExpiration(garlicExpiration);
|
config.setExpiration(garlicExpiration);
|
||||||
config.setRecipientPublicKey(target.getIdentity().getPublicKey());
|
config.setRecipientPublicKey(target.getIdentity().getPublicKey());
|
||||||
config.setRequestAck(false);
|
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a clove that sends a DeliveryStatusMessage to us
|
|
||||||
*/
|
|
||||||
private PayloadGarlicConfig buildAckClove(I2NPMessage ackMsg, RouterInfo replyPeer,
|
|
||||||
TunnelGateway replyTunnel, long expiration) {
|
|
||||||
PayloadGarlicConfig ackClove = new PayloadGarlicConfig();
|
|
||||||
|
|
||||||
Hash replyToTunnelRouter = replyTunnel.getGateway(); // inbound tunnel gateway
|
|
||||||
TunnelId replyToTunnelId = replyTunnel.getTunnelId(); // tunnel id on that gateway
|
|
||||||
|
|
||||||
DeliveryInstructions ackInstructions = new DeliveryInstructions();
|
|
||||||
ackInstructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_TUNNEL);
|
|
||||||
ackInstructions.setRouter(replyToTunnelRouter);
|
|
||||||
ackInstructions.setTunnelId(replyToTunnelId);
|
|
||||||
ackInstructions.setDelayRequested(false);
|
|
||||||
ackInstructions.setDelaySeconds(0);
|
|
||||||
ackInstructions.setEncrypted(false);
|
|
||||||
|
|
||||||
ackClove.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
|
|
||||||
ackClove.setDeliveryInstructions(ackInstructions);
|
|
||||||
ackClove.setExpiration(expiration);
|
|
||||||
ackClove.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
|
||||||
ackClove.setPayload(ackMsg);
|
|
||||||
ackClove.setRecipient(replyPeer);
|
|
||||||
ackClove.setRequestAck(false);
|
|
||||||
|
|
||||||
return ackClove;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a clove that sends the data to the target (which is local)
|
* Build a clove that sends the data to the target (which is local)
|
||||||
*/
|
*/
|
||||||
@ -808,12 +647,10 @@ public class RequestTunnelJob extends JobImpl {
|
|||||||
|
|
||||||
private class Failure extends JobImpl {
|
private class Failure extends JobImpl {
|
||||||
private TunnelInfo _tunnel;
|
private TunnelInfo _tunnel;
|
||||||
private Hash _replyThrough;
|
|
||||||
private long _started;
|
private long _started;
|
||||||
public Failure(TunnelInfo tunnel, Hash replyThrough) {
|
public Failure(TunnelInfo tunnel) {
|
||||||
super(RequestTunnelJob.this.getContext());
|
super(RequestTunnelJob.this.getContext());
|
||||||
_tunnel = tunnel;
|
_tunnel = tunnel;
|
||||||
_replyThrough = replyThrough;
|
|
||||||
_started = getContext().clock().now();
|
_started = getContext().clock().now();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -829,9 +666,8 @@ public class RequestTunnelJob extends JobImpl {
|
|||||||
}
|
}
|
||||||
synchronized (_failedTunnelParticipants) {
|
synchronized (_failedTunnelParticipants) {
|
||||||
_failedTunnelParticipants.add(_tunnel.getThisHop());
|
_failedTunnelParticipants.add(_tunnel.getThisHop());
|
||||||
_failedTunnelParticipants.add(_replyThrough);
|
|
||||||
}
|
}
|
||||||
Failure.this.getContext().messageHistory().tunnelRequestTimedOut(_tunnel.getThisHop(), _tunnel.getTunnelId(), _replyThrough);
|
Failure.this.getContext().messageHistory().tunnelRequestTimedOut(_tunnel.getThisHop(), _tunnel.getTunnelId());
|
||||||
long responseTime = getContext().clock().now() - _started;
|
long responseTime = getContext().clock().now() - _started;
|
||||||
// perhaps not an explicit reject, but an implicit one (due to dropped messages, tunnel failure, etc)
|
// perhaps not an explicit reject, but an implicit one (due to dropped messages, tunnel failure, etc)
|
||||||
getContext().profileManager().tunnelRejected(_tunnel.getThisHop(), responseTime, false);
|
getContext().profileManager().tunnelRejected(_tunnel.getThisHop(), responseTime, false);
|
||||||
@ -843,26 +679,20 @@ public class RequestTunnelJob extends JobImpl {
|
|||||||
|
|
||||||
private class Selector implements MessageSelector {
|
private class Selector implements MessageSelector {
|
||||||
private TunnelInfo _tunnel;
|
private TunnelInfo _tunnel;
|
||||||
private long _ackId;
|
|
||||||
private boolean _statusFound;
|
private boolean _statusFound;
|
||||||
private boolean _ackFound;
|
|
||||||
private long _attemptExpiration;
|
private long _attemptExpiration;
|
||||||
|
|
||||||
public Selector(TunnelInfo tunnel, long ackId) {
|
public Selector(TunnelInfo tunnel) {
|
||||||
_tunnel = tunnel;
|
_tunnel = tunnel;
|
||||||
_ackId = ackId;
|
|
||||||
_statusFound = false;
|
_statusFound = false;
|
||||||
_ackFound = false;
|
|
||||||
_attemptExpiration = getContext().clock().now() + _timeoutMs;
|
_attemptExpiration = getContext().clock().now() + _timeoutMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean continueMatching() {
|
public boolean continueMatching() {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("ContinueMatching looking for tunnel " + _tunnel.getTunnelId().getTunnelId()
|
_log.debug("ContinueMatching looking for tunnel " + _tunnel.getTunnelId().getTunnelId()
|
||||||
+ " from " + _tunnel.getThisHop().toBase64() + ": found? " + _statusFound
|
+ " from " + _tunnel.getThisHop().toBase64() + ": found? " + _statusFound);
|
||||||
+ " ackFound? " + _ackFound);
|
return !_statusFound;
|
||||||
return !_statusFound || !_ackFound;
|
|
||||||
//return !_statusFound; // who cares about the ack if we get the status OK?
|
|
||||||
}
|
}
|
||||||
public long getExpiration() { return _attemptExpiration; }
|
public long getExpiration() { return _attemptExpiration; }
|
||||||
public boolean isMatch(I2NPMessage message) {
|
public boolean isMatch(I2NPMessage message) {
|
||||||
@ -890,17 +720,6 @@ public class RequestTunnelJob extends JobImpl {
|
|||||||
+ _tunnel.getThisHop().toBase64() + "]");
|
+ _tunnel.getThisHop().toBase64() + "]");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (message.getType() == DeliveryStatusMessage.MESSAGE_TYPE) {
|
|
||||||
if (((DeliveryStatusMessage)message).getMessageId() == _ackId) {
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Matches the ping message tied to the tunnel create status message");
|
|
||||||
_ackFound = true;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Message is a delivery status message, but with the wrong id");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
//_log.debug("Message " + message.getClass().getName()
|
//_log.debug("Message " + message.getClass().getName()
|
||||||
// + " is not a delivery status or tunnel create status message [waiting for ok for tunnel "
|
// + " is not a delivery status or tunnel create status message [waiting for ok for tunnel "
|
||||||
@ -911,8 +730,8 @@ public class RequestTunnelJob extends JobImpl {
|
|||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Build Tunnel Job Selector for tunnel " + _tunnel.getTunnelId().getTunnelId()
|
return "Build Tunnel Job Selector for tunnel " + _tunnel.getTunnelId().getTunnelId()
|
||||||
+ " at " + _tunnel.getThisHop().toBase64() + " [found=" + _statusFound + ", ack="
|
+ " at " + _tunnel.getThisHop().toBase64() + " [found=" + _statusFound + "] (@"
|
||||||
+ _ackFound + "] (@" + (new Date(getExpiration())) + ")";
|
+ (new Date(getExpiration())) + ")";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ package net.i2p.router.tunnelmanager;
|
|||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
import net.i2p.data.RouterIdentity;
|
import net.i2p.data.RouterIdentity;
|
||||||
import net.i2p.data.i2np.I2NPMessage;
|
import net.i2p.data.i2np.I2NPMessage;
|
||||||
import net.i2p.data.i2np.SourceRouteBlock;
|
|
||||||
import net.i2p.data.i2np.TunnelCreateMessage;
|
import net.i2p.data.i2np.TunnelCreateMessage;
|
||||||
import net.i2p.router.HandlerJobBuilder;
|
import net.i2p.router.HandlerJobBuilder;
|
||||||
import net.i2p.router.Job;
|
import net.i2p.router.Job;
|
||||||
@ -22,8 +21,8 @@ class TunnelCreateMessageHandler implements HandlerJobBuilder {
|
|||||||
public TunnelCreateMessageHandler(RouterContext context) {
|
public TunnelCreateMessageHandler(RouterContext context) {
|
||||||
_context = context;
|
_context = context;
|
||||||
}
|
}
|
||||||
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash, SourceRouteBlock replyBlock) {
|
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) {
|
||||||
return new HandleTunnelCreateMessageJob(_context, (TunnelCreateMessage)receivedMessage, from, fromHash, replyBlock);
|
return new HandleTunnelCreateMessageJob(_context, (TunnelCreateMessage)receivedMessage, from, fromHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user