TunnelCreateMessage.java 11.14 KiB
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.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.data.Certificate;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag;
import net.i2p.data.TunnelId;
import net.i2p.util.Log;
/**
* Defines the message sent to a router to request that it participate in a
* tunnel using the included configuration settings.
*
*/
public class TunnelCreateMessage extends I2NPMessageImpl {
private Log _log;
public final static int MESSAGE_TYPE = 6;
private Hash _nextRouter;
private TunnelId _nextTunnelId;
private int _durationSeconds;
private SessionKey _layerKey;
private SessionKey _ivKey;
private Properties _options;
private Hash _replyGateway;
private TunnelId _replyTunnel;
private SessionTag _replyTag;
private SessionKey _replyKey;
private boolean _isGateway;
private long _nonce;
private Certificate _certificate;
private byte[] _optionsCache;
private byte[] _certificateCache;
public static final long MAX_NONCE_VALUE = ((1l << 32l) - 1l);
private static final Hash INVALID_HASH = new Hash(new byte[Hash.HASH_LENGTH]); // all 0s
public TunnelCreateMessage(I2PAppContext context) {
super(context);
_log = context.logManager().getLog(TunnelCreateMessage.class);
}
public void setNextRouter(Hash routerIdentityHash) { _nextRouter = routerIdentityHash; }
public Hash getNextRouter() { return _nextRouter; }
public void setNextTunnelId(TunnelId id) { _nextTunnelId = id; }
public TunnelId getNextTunnelId() { return _nextTunnelId; }
public void setDurationSeconds(int seconds) { _durationSeconds = seconds; }
public int getDurationSeconds() { return _durationSeconds; }
public void setLayerKey(SessionKey key) { _layerKey = key; }
public SessionKey getLayerKey() { return _layerKey; }
public void setIVKey(SessionKey key) { _ivKey = key; }
public SessionKey getIVKey() { return _ivKey; }
public void setCertificate(Certificate cert) { _certificate = cert; }
public Certificate getCertificate() { return _certificate; }
public void setReplyTag(SessionTag tag) { _replyTag = tag; }
public SessionTag getReplyTag() { return _replyTag; }
public void setReplyKey(SessionKey key) { _replyKey = key; }
public SessionKey getReplyKey() { return _replyKey; }
public void setReplyTunnel(TunnelId id) { _replyTunnel = id; }
public TunnelId getReplyTunnel() { return _replyTunnel; }
public void setReplyGateway(Hash peer) { _replyGateway = peer; }
public Hash getReplyGateway() { return _replyGateway; }
public void setNonce(long nonce) { _nonce = nonce; }
public long getNonce() { return _nonce; }
public void setIsGateway(boolean isGateway) { _isGateway = isGateway; }
public boolean getIsGateway() { return _isGateway; }
public Properties getOptions() { return _options; }
public void setOptions(Properties opts) { _options = opts; }
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException {
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
if (DataHelper.eq(INVALID_HASH.getData(), 0, data, offset, Hash.HASH_LENGTH)) {
_nextRouter = null;
} else {
_nextRouter = new Hash(new byte[Hash.HASH_LENGTH]);
System.arraycopy(data, offset, _nextRouter.getData(), 0, Hash.HASH_LENGTH);
}
offset += Hash.HASH_LENGTH;
long id = DataHelper.fromLong(data, offset, 4);
if (id > 0)
_nextTunnelId = new TunnelId(id);
offset += 4;
_durationSeconds = (int)DataHelper.fromLong(data, offset, 2);
offset += 2;
_layerKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
System.arraycopy(data, offset, _layerKey.getData(), 0, SessionKey.KEYSIZE_BYTES);
offset += SessionKey.KEYSIZE_BYTES;
_ivKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
System.arraycopy(data, offset, _ivKey.getData(), 0, SessionKey.KEYSIZE_BYTES);
offset += SessionKey.KEYSIZE_BYTES;
try {
Properties opts = new Properties();
_options = opts;
offset = DataHelper.fromProperties(data, offset, opts);
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error reading the options", dfe);
}
_replyGateway = new Hash(new byte[Hash.HASH_LENGTH]);
System.arraycopy(data, offset, _replyGateway.getData(), 0, Hash.HASH_LENGTH);
offset += Hash.HASH_LENGTH;
_replyTunnel = new TunnelId(DataHelper.fromLong(data, offset, 4));
offset += 4;
_replyTag = new SessionTag(new byte[SessionTag.BYTE_LENGTH]);
System.arraycopy(data, offset, _replyTag.getData(), 0, SessionTag.BYTE_LENGTH);
offset += SessionTag.BYTE_LENGTH;
_replyKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
System.arraycopy(data, offset, _replyKey.getData(), 0, SessionKey.KEYSIZE_BYTES);
offset += SessionKey.KEYSIZE_BYTES;
_nonce = DataHelper.fromLong(data, offset, 4);
offset += 4;
try {
Certificate cert = new Certificate();
_certificate = cert;
offset += cert.readBytes(data, offset);
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error reading the certificate", dfe);
}
Boolean b = DataHelper.fromBoolean(data, offset);
if (b == null)
throw new I2NPMessageException("isGateway == unknown?!");
_isGateway = b.booleanValue();
offset += DataHelper.BOOLEAN_LENGTH;
}
/** calculate the message body's length (not including the header and footer */
protected int calculateWrittenLength() {
int length = 0;
length += Hash.HASH_LENGTH; // nextRouter
length += 4; // nextTunnel
length += 2; // duration
length += SessionKey.KEYSIZE_BYTES; // layerKey
length += SessionKey.KEYSIZE_BYTES; // ivKey
if (_optionsCache == null) {
try {
_optionsCache = DataHelper.toProperties(_options);
length += _optionsCache.length;
} catch (DataFormatException dfe) {
}
}
length += Hash.HASH_LENGTH; // replyGateway
length += 4; // replyTunnel
length += SessionTag.BYTE_LENGTH; // replyTag
length += SessionKey.KEYSIZE_BYTES; // replyKey
length += 4; // nonce
if (_certificateCache == null)
_certificateCache = _certificate.toByteArray();
length += _certificateCache.length;
length += DataHelper.BOOLEAN_LENGTH;
return length;
}
/** write the message body to the output array, starting at the given index */
protected int writeMessageBody(byte data[], int offset) throws I2NPMessageException {
if (_nextRouter == null)
System.arraycopy(INVALID_HASH.getData(), 0, data, offset, Hash.HASH_LENGTH);
else
System.arraycopy(_nextRouter.getData(), 0, data, offset, Hash.HASH_LENGTH);
offset += Hash.HASH_LENGTH;
if (_nextTunnelId == null)
DataHelper.toLong(data, offset, 4, 0);
else
DataHelper.toLong(data, offset, 4, _nextTunnelId.getTunnelId());
offset += 4;
DataHelper.toLong(data, offset, 2, _durationSeconds);
offset += 2;
System.arraycopy(_layerKey.getData(), 0, data, offset, SessionKey.KEYSIZE_BYTES);
offset += SessionKey.KEYSIZE_BYTES;
System.arraycopy(_ivKey.getData(), 0, data, offset, SessionKey.KEYSIZE_BYTES);
offset += SessionKey.KEYSIZE_BYTES;
if (_optionsCache == null) {
try {
_optionsCache = DataHelper.toProperties(_options);
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error reading the options", dfe);
}
}
System.arraycopy(_optionsCache, 0, data, offset, _optionsCache.length);
offset += _optionsCache.length;
System.arraycopy(_replyGateway.getData(), 0, data, offset, Hash.HASH_LENGTH);
offset += Hash.HASH_LENGTH;
DataHelper.toLong(data, offset, 4, _replyTunnel.getTunnelId());
offset += 4;
System.arraycopy(_replyTag.getData(), 0, data, offset, SessionTag.BYTE_LENGTH);
offset += SessionTag.BYTE_LENGTH;
System.arraycopy(_replyKey.getData(), 0, data, offset, SessionKey.KEYSIZE_BYTES);
offset += SessionKey.KEYSIZE_BYTES;
DataHelper.toLong(data, offset, 4, _nonce);
offset += 4;
if (_certificateCache == null)
_certificateCache = _certificate.toByteArray();
System.arraycopy(_certificateCache, 0, data, offset, _certificateCache.length);
offset += _certificateCache.length;
DataHelper.toBoolean(data, offset, _isGateway);
offset += DataHelper.BOOLEAN_LENGTH;
return offset;
}
@Override
public byte[] toByteArray() {
byte rv[] = super.toByteArray();
if (rv == null)
throw new RuntimeException("unable to toByteArray(): " + toString());
return rv;
}
@Override
public int hashCode() {
return DataHelper.hashCode(getNextRouter()) +
DataHelper.hashCode(getNextTunnelId()) +
DataHelper.hashCode(getReplyGateway()) +
DataHelper.hashCode(getReplyTunnel());
}
@Override
public boolean equals(Object object) {
if ( (object != null) && (object instanceof TunnelCreateMessage) ) {
TunnelCreateMessage msg = (TunnelCreateMessage)object;
return DataHelper.eq(getNextRouter(), msg.getNextRouter()) &&
DataHelper.eq(getNextTunnelId(), msg.getNextTunnelId()) &&
DataHelper.eq(getReplyGateway(), msg.getReplyGateway()) &&
DataHelper.eq(getReplyTunnel(), msg.getReplyTunnel());
} else {
return false;
}
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("[TunnelCreateMessage: ");
buf.append("\n\tNext Router: ").append(getNextRouter());
buf.append("\n\tNext Tunnel: ").append(getNextTunnelId());
buf.append("\n\tReply Tunnel: ").append(getReplyTunnel());
buf.append("\n\tReply Peer: ").append(getReplyGateway());
buf.append("]");
return buf.toString();
}
public int getType() { return MESSAGE_TYPE; }
}