package net.i2p.router.transport.udp;
import net.i2p.I2PAppContext;
import net.i2p.data.Base64;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.SessionKey;
import net.i2p.data.Signature;
import net.i2p.util.Log;
/**
* To read a packet, initialize this reader with the data and fetch out
* the appropriate fields. If the interesting bits are in message specific
* elements, grab the appropriate subreader.
*
* Many of the methods here and in the subclasses will throw AIOOBE on
* malformed packets, that should be caught also.
*
*/
class UDPPacketReader {
private final I2PAppContext _context;
private final Log _log;
private byte _message[];
private int _payloadBeginOffset;
private int _payloadLength;
private final SessionRequestReader _sessionRequestReader;
private final SessionCreatedReader _sessionCreatedReader;
private final SessionConfirmedReader _sessionConfirmedReader;
private final DataReader _dataReader;
private final PeerTestReader _peerTestReader;
private final RelayRequestReader _relayRequestReader;
private final RelayIntroReader _relayIntroReader;
private final RelayResponseReader _relayResponseReader;
private static final int KEYING_MATERIAL_LENGTH = 64;
public UDPPacketReader(I2PAppContext ctx) {
_context = ctx;
_log = ctx.logManager().getLog(UDPPacketReader.class);
_sessionRequestReader = new SessionRequestReader();
_sessionCreatedReader = new SessionCreatedReader();
_sessionConfirmedReader = new SessionConfirmedReader();
_dataReader = new DataReader();
_peerTestReader = new PeerTestReader();
_relayRequestReader = new RelayRequestReader();
_relayIntroReader = new RelayIntroReader();
_relayResponseReader = new RelayResponseReader();
}
public void initialize(UDPPacket packet) {
int off = packet.getPacket().getOffset();
int len = packet.getPacket().getLength();
off += UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
len -= UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
initialize(packet.getPacket().getData(), off, len);
}
private void initialize(byte message[], int payloadOffset, int payloadLength) {
_message = message;
_payloadBeginOffset = payloadOffset;
_payloadLength = payloadLength;
}
/** what type of payload is in here? */
public int readPayloadType() {
// 4 highest order bits == payload type
return (_message[_payloadBeginOffset] & 0xFF) >>> 4;
}
/**
* Does this packet include rekeying data in the header?
* Unused, should always be false.
*/
public boolean isRekeyingIncluded() {
return (_message[_payloadBeginOffset] & UDPPacket.HEADER_FLAG_REKEY) != 0;
}
/**
* Does this packet include extended options in the header?
*/
public boolean isExtendedOptionsIncluded() {
return (_message[_payloadBeginOffset] & UDPPacket.HEADER_FLAG_EXTENDED_OPTIONS) != 0;
}
/** @return seconds */
public long readTimestamp() {
// Note, this is unsigned, so we're good until February 2106
return DataHelper.fromLong(_message, _payloadBeginOffset + 1, 4);
}
/**
* Returns rekeying data (64 bytes), or null if none.
* Unused, should always return null.
*
* @deprecated unused
*/
@Deprecated
public byte[] readKeyingMaterial() {
if (!isRekeyingIncluded())
return null;
byte[] rv = new byte[KEYING_MATERIAL_LENGTH];
System.arraycopy(_message, _payloadBeginOffset + 1 + 4, rv, 0, KEYING_MATERIAL_LENGTH);
return rv;
}
/**
* Returns extended option data, 0-255 bytes, or null if none.
* Returned array does NOT include the length byte.
*
* @return extended options or null if none is included
* @since 0.9.24
*/
public byte[] readExtendedOptions() {
if (!isExtendedOptionsIncluded())
return null;
int offset = _payloadBeginOffset + 1 + 4;
if (isRekeyingIncluded())
offset += KEYING_MATERIAL_LENGTH;
int optionsSize = _message[offset++] & 0xff;
byte[] rv = new byte[optionsSize];
System.arraycopy(_message, offset, rv, 0, optionsSize);
return rv;
}
/** index into the message where the body begins */
private int readBodyOffset() {
int offset = _payloadBeginOffset + 1 + 4;
if (isRekeyingIncluded())
offset += KEYING_MATERIAL_LENGTH;
if (isExtendedOptionsIncluded()) {
int optionsSize = _message[offset] & 0xff;
offset += optionsSize + 1;
}
return offset;
}
public SessionRequestReader getSessionRequestReader() { return _sessionRequestReader; }
public SessionCreatedReader getSessionCreatedReader() { return _sessionCreatedReader; }
public SessionConfirmedReader getSessionConfirmedReader() { return _sessionConfirmedReader; }
public DataReader getDataReader() { return _dataReader; }
public PeerTestReader getPeerTestReader() { return _peerTestReader; }
public RelayRequestReader getRelayRequestReader() { return _relayRequestReader; }
public RelayIntroReader getRelayIntroReader() { return _relayIntroReader; }
public RelayResponseReader getRelayResponseReader() { return _relayResponseReader; }
@Override
public String toString() {
int type = readPayloadType();
switch (type) {
case UDPPacket.PAYLOAD_TYPE_DATA:
return _dataReader.toString();
case UDPPacket.PAYLOAD_TYPE_SESSION_CONFIRMED:
return "Session confirmed packet";
case UDPPacket.PAYLOAD_TYPE_SESSION_CREATED:
return "Session created packet";
case UDPPacket.PAYLOAD_TYPE_SESSION_REQUEST:
return "Session request packet";
case UDPPacket.PAYLOAD_TYPE_TEST:
return "Peer test packet";
case UDPPacket.PAYLOAD_TYPE_RELAY_INTRO:
return "Relay intro packet";
case UDPPacket.PAYLOAD_TYPE_RELAY_REQUEST:
return "Relay request packet";
case UDPPacket.PAYLOAD_TYPE_RELAY_RESPONSE:
return "Relay response packet";
case UDPPacket.PAYLOAD_TYPE_SESSION_DESTROY:
return "Session destroyed packet";
default:
return "Unknown packet type " + type;
}
}
public void toRawString(StringBuilder buf) {
if (_message != null)
buf.append(Base64.encode(_message, _payloadBeginOffset, _payloadLength));
}
/* ------- Begin Reader Classes ------- */
/**
* Base
*
* @since 0.9.24
*/
public abstract class Reader {
/**
* Returns extended option data from the header, 0-255 bytes, or null if none.
* Returned array does NOT include the length byte.
*
* @return extended options or null if none is included
* @since 0.9.24
*/
public byte[] readExtendedOptions() {
return UDPPacketReader.this.readExtendedOptions();
}
}
/** Help read the SessionRequest payload */
public class SessionRequestReader extends Reader {
public static final int X_LENGTH = 256;
public void readX(byte target[], int targetOffset) {
int readOffset = readBodyOffset();
System.arraycopy(_message, readOffset, target, targetOffset, X_LENGTH);
}
public int readIPSize() {
int offset = readBodyOffset() + X_LENGTH;
return _message[offset] & 0xff;
}
/** what IP bob is reachable on */
public void readIP(byte target[], int targetOffset) {
int offset = readBodyOffset() + X_LENGTH;
int size = _message[offset] & 0xff;
offset++;
System.arraycopy(_message, offset, target, targetOffset, size);
}
}
/** Help read the SessionCreated payload */
public class SessionCreatedReader extends Reader {
public static final int Y_LENGTH = 256;
public void readY(byte target[], int targetOffset) {
int readOffset = readBodyOffset();
System.arraycopy(_message, readOffset, target, targetOffset, Y_LENGTH);
}
/** sizeof(IP) */
public int readIPSize() {
int offset = readBodyOffset() + Y_LENGTH;
return _message[offset] & 0xff;
}
/** what IP do they think we are coming on? */
public void readIP(byte target[], int targetOffset) {
int offset = readBodyOffset() + Y_LENGTH;
int size = _message[offset] & 0xff;
offset++;
System.arraycopy(_message, offset, target, targetOffset, size);
}
/** what port do they think we are coming from? */
public int readPort() {
int offset = readBodyOffset() + Y_LENGTH + 1 + readIPSize();
return (int)DataHelper.fromLong(_message, offset, 2);
}
/** read in the 4 byte relayAs tag */
public long readRelayTag() {
int offset = readBodyOffset() + Y_LENGTH + 1 + readIPSize() + 2;
return DataHelper.fromLong(_message, offset, 4);
}
public long readSignedOnTime() {
int offset = readBodyOffset() + Y_LENGTH + 1 + readIPSize() + 2 + 4;
long rv = DataHelper.fromLong(_message, offset, 4);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Signed on time offset: " + offset + " val: " + rv
+ "\nRawCreated: " + Base64.encode(_message, _payloadBeginOffset, _payloadLength));
return rv;
}
/** @param size the amount to be copied, including padding to mod 16 */
public void readEncryptedSignature(byte target[], int targetOffset, int size) {
int offset = readBodyOffset() + Y_LENGTH + 1 + readIPSize() + 2 + 4 + 4;
System.arraycopy(_message, offset, target, targetOffset, size);
}
public void readIV(byte target[], int targetOffset) {
int offset = _payloadBeginOffset - UDPPacket.IV_SIZE;
System.arraycopy(_message, offset, target, targetOffset, UDPPacket.IV_SIZE);
}
}
/** parse out the confirmed message */
public class SessionConfirmedReader extends Reader {
/** which fragment is this? */
public int readCurrentFragmentNum() {
int readOffset = readBodyOffset();
return (_message[readOffset] & 0xFF) >>> 4;
}
/** how many fragments will there be? */
public int readTotalFragmentNum() {
int readOffset = readBodyOffset();
return (_message[readOffset] & 0xF);
}
public int readCurrentFragmentSize() {
int readOffset = readBodyOffset() + 1;
return (int)DataHelper.fromLong(_message, readOffset, 2);
}
/** read the fragment data from the nonterminal sessionConfirmed packet */
public void readFragmentData(byte target[], int targetOffset) {
int readOffset = readBodyOffset() + 1 + 2;
int len = readCurrentFragmentSize();
System.arraycopy(_message, readOffset, target, targetOffset, len);
}
/**
* Read the time at which the signature was generated.
* TODO must be completely in final fragment.
* Time and sig cannot be split across fragments.
*/
public long readFinalFragmentSignedOnTime() {
if (readCurrentFragmentNum() != readTotalFragmentNum()-1)
throw new IllegalStateException("This is not the final fragment");
int readOffset = readBodyOffset() + 1 + 2 + readCurrentFragmentSize();
return DataHelper.fromLong(_message, readOffset, 4);
}
/**
* Read the signature from the final sessionConfirmed packet.
* TODO must be completely in final fragment.
* Time and sig cannot be split across fragments.
* @param size not including padding
*/
public void readFinalSignature(byte target[], int targetOffset, int size) {
if (readCurrentFragmentNum() != readTotalFragmentNum()-1)
throw new IllegalStateException("This is not the final fragment");
int readOffset = _payloadBeginOffset + _payloadLength - size;
if (readOffset < readBodyOffset() + (1 + 2 + 4))
throw new IllegalStateException("Sig split across fragments");
System.arraycopy(_message, readOffset, target, targetOffset, size);
}
}
/** parse out the data message */
public class DataReader extends Reader {
/**
* @return the data size, NOT including IP header, UDP header, IV, or MAC
*/
public int getPacketSize() { return _payloadLength; }
public boolean readACKsIncluded() {
return flagSet(UDPPacket.DATA_FLAG_EXPLICIT_ACK);
}
public boolean readACKBitfieldsIncluded() {
return flagSet(UDPPacket.DATA_FLAG_ACK_BITFIELDS);
}
public boolean readECN() {
return flagSet(UDPPacket.DATA_FLAG_ECN);
}
public boolean readWantPreviousACKs() {
return flagSet(UDPPacket.DATA_FLAG_WANT_ACKS);
}
public boolean readReplyRequested() {
return flagSet(UDPPacket.DATA_FLAG_WANT_REPLY);
}
public boolean readExtendedDataIncluded() {
return flagSet(UDPPacket.DATA_FLAG_EXTENDED);
}
public int readACKCount() {
if (!readACKsIncluded()) return 0;
int off = readBodyOffset() + 1;
return _message[off] & 0xff;
}
public long readACK(int index) {
if (!readACKsIncluded()) return -1;
int off = readBodyOffset() + 1;
//int num = (int)DataHelper.fromLong(_message, off, 1);
off++;
return DataHelper.fromLong(_message, off + (4 * index), 4);
}
public ACKBitfield[] readACKBitfields() throws DataFormatException {
if (!readACKBitfieldsIncluded()) return null;
int off = readBodyOffset() + 1;
if (readACKsIncluded()) {
int numACKs = _message[off] & 0xff;
off++;
off += 4 * numACKs;
}
int numBitfields = _message[off] & 0xff;
off++;
PacketACKBitfield rv[] = new PacketACKBitfield[numBitfields];
for (int i = 0; i < numBitfields; i++) {
rv[i] = new PacketACKBitfield(off);
off += rv[i].getByteLength();
}
return rv;
}
public int readFragmentCount() throws DataFormatException {
int off = readBodyOffset() + 1;
if (readACKsIncluded()) {
int numACKs = _message[off] & 0xff;
off++;
off += 4 * numACKs;
}
if (readACKBitfieldsIncluded()) {
int numBitfields = _message[off] & 0xff;
off++;
for (int i = 0; i < numBitfields; i++) {
PacketACKBitfield bf = new PacketACKBitfield(off);
off += bf.getByteLength();
}
}
if (readExtendedDataIncluded()) {
int size = _message[off] & 0xff;
off++;
off += size;
}
return _message[off];
}
public long readMessageId(int fragmentNum) throws DataFormatException {
int fragmentBegin = getFragmentBegin(fragmentNum);
return DataHelper.fromLong(_message, fragmentBegin, 4);
}
public int readMessageFragmentNum(int fragmentNum) throws DataFormatException {
int off = getFragmentBegin(fragmentNum);
off += 4; // messageId
return (_message[off] & 0xFF) >>> 1;
}
public boolean readMessageIsLast(int fragmentNum) throws DataFormatException {
int off = getFragmentBegin(fragmentNum);
off += 4; // messageId
return ((_message[off] & 1) != 0);
}
public int readMessageFragmentSize(int fragmentNum) throws DataFormatException {
int off = getFragmentBegin(fragmentNum);
off += 5; // messageId + fragment info
return ((int)DataHelper.fromLong(_message, off, 2)) & 0x3FFF;
}
public void readMessageFragment(int fragmentNum, byte target[], int targetOffset)
throws DataFormatException {
int off = getFragmentBegin(fragmentNum);
off += 5; // messageId + fragment info
int size = ((int)DataHelper.fromLong(_message, off, 2)) & 0x3FFF;
off += 2;
System.arraycopy(_message, off, target, targetOffset, size);
}
private int getFragmentBegin(int fragmentNum) throws DataFormatException {
int off = readBodyOffset() + 1;
if (readACKsIncluded()) {
int numACKs = _message[off] & 0xff;
off++;
off += 4 * numACKs;
}
if (readACKBitfieldsIncluded()) {
int numBitfields = _message[off] & 0xff;
off++;
PacketACKBitfield bf[] = new PacketACKBitfield[numBitfields];
for (int i = 0; i < numBitfields; i++) {
bf[i] = new PacketACKBitfield(off);
off += bf[i].getByteLength();
}
}
if (readExtendedDataIncluded()) {
int size = _message[off] & 0xff;
off++;
off += size;
}
off++; // # fragments
if (fragmentNum > 0) {
for (int i = 0; i < fragmentNum; i++) {
off += 5; // messageId+info
off += ((int)DataHelper.fromLong(_message, off, 2)) & 0x3FFF;
off += 2;
}
}
return off;
}
private boolean flagSet(byte flag) {
int flagOffset = readBodyOffset();
return ((_message[flagOffset] & flag) != 0);
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(512);
long msAgo = _context.clock().now() - readTimestamp()*1000;
buf.append("Data packet sent ").append(msAgo).append("ms ago ");
buf.append("IV ");
buf.append(Base64.encode(_message, _payloadBeginOffset-UDPPacket.IV_SIZE, UDPPacket.IV_SIZE));
buf.append(" ");
int off = readBodyOffset() + 1;
if (readACKsIncluded()) {
int numACKs = _message[off] & 0xff;
off++;
buf.append("with ACKs for ");
for (int i = 0; i < numACKs; i++) {
buf.append(DataHelper.fromLong(_message, off, 4)).append(' ');
off += 4;
}
}
if (readACKBitfieldsIncluded()) {
int numBitfields = _message[off] & 0xff;
off++;
buf.append("with partial ACKs for ");
try {
for (int i = 0; i < numBitfields; i++) {
PacketACKBitfield bf = new PacketACKBitfield(off);
buf.append(bf.getMessageId()).append(' ');
off += bf.getByteLength();
}
} catch (DataFormatException dfe) {
buf.append("CORRUPT");
return buf.toString();
}
}
if (readExtendedDataIncluded()) {
int size = _message[off] & 0xff;
off++;
buf.append("with extended size of ");
buf.append(size);
buf.append(' ');
off += size;
}
int numFragments = _message[off] & 0xff;
off++;
buf.append("with fragmentCount of ");
buf.append(numFragments);
buf.append(' ');
for (int i = 0; i < numFragments; i++) {
buf.append("containing messageId ");
buf.append(DataHelper.fromLong(_message, off, 4));
off += 4;
int fragNum = (_message[off] & 0xFF) >>> 1;
boolean isLast = (_message[off] & 1) != 0;
off++;
buf.append(" frag# ").append(fragNum);
buf.append(" isLast? ").append(isLast);
buf.append(" info ").append(_message[off-1]);
int size = ((int)DataHelper.fromLong(_message, off, 2)) & 0x3FFF;
off += 2;
buf.append(" with ").append(size).append(" bytes; ");
off += size;
}
return buf.toString();
}
public void toRawString(StringBuilder buf) throws DataFormatException {
UDPPacketReader.this.toRawString(buf);
buf.append(" payload: ");
int off = getFragmentBegin(0); // first fragment
off += 4 + 1; // messageId + fragment info
int size = ((int)DataHelper.fromLong(_message, off, 2)) & 0x3FFF;
off += 2;
buf.append(Base64.encode(_message, off, size));
}
}
/**
* Helper class to fetch the particular bitfields from the raw packet
*/
private class PacketACKBitfield implements ACKBitfield {
private final int _start;
private final int _bitfieldStart;
private final int _bitfieldSize;
public PacketACKBitfield(int start) throws DataFormatException {
_start = start;
_bitfieldStart = start + 4;
int bfsz = 1;
// bitfield is an array of bytes where the high bit is 1 if
// further bytes in the bitfield follow
while ((_message[_bitfieldStart + bfsz - 1] & UDPPacket.BITFIELD_CONTINUATION) != 0x0) {
bfsz++;
//if (bfsz > InboundMessageState.MAX_PARTIAL_BITFIELD_BYTES)
// throw new DataFormatException();
}
if (bfsz > InboundMessageState.MAX_PARTIAL_BITFIELD_BYTES)
throw new DataFormatException("bitfield size: " + bfsz);
_bitfieldSize = bfsz;
}
public long getMessageId() { return DataHelper.fromLong(_message, _start, 4); }
public int getByteLength() { return 4 + _bitfieldSize; }
public int fragmentCount() { return _bitfieldSize * 7; }
public boolean receivedComplete() { return false; }
/**
* Number of fragments acked in this bitfield.
* Faster than looping through received()
* @since 0.9.16
*/
public int ackCount() {
int rv = 0;
for (int i = _bitfieldStart; i < _bitfieldStart + _bitfieldSize; i++) {
byte b = _message[i];
if ((b & 0x7f) != 0) {
for (int j = 0; j < 7; j++) {
if ((b & 0x01) != 0)
rv++;
b >>= 1;
}
}
}
return rv;
}
/**
* Highest fragment number acked in this bitfield.
* @return highest fragment number acked, or -1 if none
* @since 0.9.16
*/
public int highestReceived() {
for (int i = _bitfieldSize - 1; i >= 0; i--) {
byte b = _message[_bitfieldStart + i];
if ((b & 0x7f) == 0)
continue;
for (int j = 6; j >= 0; j--) {
if ((b & 0x40) != 0)
return (7 * i) + j;
b <<= 1;
}
}
return -1;
}
public boolean received(int fragmentNum) {
if ( (fragmentNum < 0) || (fragmentNum >= _bitfieldSize*7) )
return false;
// the fragment has been received if the bit is set
int byteNum = _bitfieldStart + (fragmentNum/7);
int flagNum = fragmentNum % 7;
return (_message[byteNum] & (1 << flagNum)) != 0x0;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(64);
buf.append("IB Partial ACK of ");
buf.append(getMessageId());
buf.append(" highest: ").append(highestReceived());
buf.append(" with ACKs for: [");
int numFrags = fragmentCount();
for (int i = 0; i < numFrags; i++) {
if (!received(i))
buf.append('!');
buf.append(i).append(' ');
}
buf.append("] / ").append(numFrags);
return buf.toString();
}
}
/** Help read the PeerTest payload */
public class PeerTestReader extends Reader {
private static final int NONCE_LENGTH = 4;
public long readNonce() {
int readOffset = readBodyOffset();
return DataHelper.fromLong(_message, readOffset, NONCE_LENGTH);
}
public int readIPSize() {
int offset = readBodyOffset() + NONCE_LENGTH;
return _message[offset] & 0xff;
}
/** what IP Alice is reachable on */
public void readIP(byte target[], int targetOffset) {
int offset = readBodyOffset() + NONCE_LENGTH;
int size = _message[offset] & 0xff;
offset++;
System.arraycopy(_message, offset, target, targetOffset, size);
}
/** what IP Alice is reachable on */
public int readPort() {
int offset = readBodyOffset() + NONCE_LENGTH;
int size = _message[offset] & 0xff;
offset++;
offset += size; // skip the IP
return (int)DataHelper.fromLong(_message, offset, 2);
}
/** what Alice's intro key is (if known - if unknown, the key is INVALID_KEY) */
public void readIntroKey(byte target[], int targetOffset) {
int offset = readBodyOffset() + NONCE_LENGTH;
int size = _message[offset] & 0xff;
offset += 1 + 2; // skip the size + port
offset += size; // skip the IP
System.arraycopy(_message, offset, target, targetOffset, SessionKey.KEYSIZE_BYTES);
}
}
/** Help read the RelayRequest payload */
public class RelayRequestReader extends Reader {
public long readTag() {
long rv = DataHelper.fromLong(_message, readBodyOffset(), 4);
if (_log.shouldLog(Log.DEBUG))
_log.debug("read alice tag: " + rv);
return rv;
}
public int readIPSize() {
int offset = readBodyOffset() + 4;
int rv = _message[offset] & 0xff;
if (_log.shouldLog(Log.DEBUG))
_log.debug("read alice ip size: " + rv);
return rv;
}
/** what IP Alice is reachable on */
public void readIP(byte target[], int targetOffset) {
int offset = readBodyOffset() + 4;
int size = _message[offset] & 0xff;
offset++;
System.arraycopy(_message, offset, target, targetOffset, size);
if (_log.shouldLog(Log.DEBUG))
_log.debug("read alice ip: " + Base64.encode(target, targetOffset, size));
}
public int readPort() {
int offset = readBodyOffset() + 4;
offset += _message[offset] & 0xff;
offset++;
int rv = (int)DataHelper.fromLong(_message, offset, 2);
if (_log.shouldLog(Log.DEBUG))
_log.debug("read alice port: " + rv);
return rv;
}
/** unused */
public int readChallengeSize() {
int offset = readBodyOffset() + 4;
offset += _message[offset] & 0xff;
offset += 1 + 2;
int rv = _message[offset] & 0xff;
if (_log.shouldLog(Log.DEBUG))
_log.debug("read challenge size: " + rv);
return rv;
}
/** unused */
public void readChallengeSize(byte target[], int targetOffset) {
int offset = readBodyOffset() + 4;
offset += _message[offset] & 0xff;
offset += 1 + 2;
int sz = _message[offset] & 0xff;
offset++;
System.arraycopy(_message, offset, target, targetOffset, sz);
if (_log.shouldLog(Log.DEBUG))
_log.debug("read challenge data: " + Base64.encode(target));
}
public void readAliceIntroKey(byte target[], int targetOffset) {
int offset = readBodyOffset() + 4;
offset += _message[offset] & 0xff;
offset += 1 + 2;
int sz = _message[offset] & 0xff;
offset++;
offset += sz;
System.arraycopy(_message, offset, target, targetOffset, SessionKey.KEYSIZE_BYTES);
if (_log.shouldLog(Log.DEBUG))
_log.debug("read alice intro key: " + Base64.encode(target, targetOffset, SessionKey.KEYSIZE_BYTES)
+ " packet size: " + _payloadLength + " off: " + offset + " data: " + Base64.encode(_message));
}
public long readNonce() {
int offset = readBodyOffset() + 4;
offset += _message[offset] & 0xff;
offset += 1 + 2;
int sz = _message[offset] & 0xff;
offset++;
offset += sz;
offset += SessionKey.KEYSIZE_BYTES;
long rv = DataHelper.fromLong(_message, offset, 4);
if (_log.shouldLog(Log.DEBUG))
_log.debug("read request nonce: " + rv);
return rv;
}
}
/** Help read the RelayIntro payload */
public class RelayIntroReader extends Reader {
public int readIPSize() {
int offset = readBodyOffset();
return _message[offset] & 0xff;
}
/** what IP Alice is reachable on */
public void readIP(byte target[], int targetOffset) {
int offset = readBodyOffset();
int size = _message[offset] & 0xff;
offset++;
System.arraycopy(_message, offset, target, targetOffset, size);
}
public int readPort() {
int offset = readBodyOffset();
offset += _message[offset] & 0xff;
offset++;
return (int)DataHelper.fromLong(_message, offset, 2);
}
/** unused */
public int readChallengeSize() {
int offset = readBodyOffset();
offset += _message[offset] & 0xff;
offset += 1 + 2;
return _message[offset] & 0xff;
}
/** unused */
public void readChallengeSize(byte target[], int targetOffset) {
int offset = readBodyOffset();
offset += _message[offset] & 0xff;
offset += 1 + 2;
int sz = _message[offset] & 0xff;
offset++;
System.arraycopy(_message, offset, target, targetOffset, sz);
}
}
/** Help read the RelayResponse payload */
public class RelayResponseReader extends Reader {
public int readCharlieIPSize() {
int offset = readBodyOffset();
return _message[offset] & 0xff;
}
/** what IP charlie is reachable on */
public void readCharlieIP(byte target[], int targetOffset) {
int offset = readBodyOffset();
int size = _message[offset] & 0xff;
offset++;
System.arraycopy(_message, offset, target, targetOffset, size);
}
/** what port charlie is reachable on */
public int readCharliePort() {
int offset = readBodyOffset();
offset += _message[offset] & 0xff;
offset++;
return (int)DataHelper.fromLong(_message, offset, 2);
}
/** @deprecated unused */
@Deprecated
public int readAliceIPSize() {
int offset = readBodyOffset();
offset += _message[offset] & 0xff;
offset += 1 + 2;
return _message[offset] & 0xff;
}
/** @deprecated unused */
@Deprecated
public void readAliceIP(byte target[], int targetOffset) {
int offset = readBodyOffset();
offset += _message[offset] & 0xff;
offset += 1 + 2;
int sz = _message[offset] & 0xff;
offset++;
System.arraycopy(_message, offset, target, targetOffset, sz);
}
/** @deprecated unused */
@Deprecated
public int readAlicePort() {
int offset = readBodyOffset();
offset += _message[offset] & 0xff;
offset += 1 + 2;
int sz = _message[offset] & 0xff;
offset++;
offset += sz;
return (int)DataHelper.fromLong(_message, offset, 2);
}
public long readNonce() {
int offset = readBodyOffset();
offset += _message[offset] & 0xff;
offset += 1 + 2;
int sz = _message[offset] & 0xff;
offset += 1 + 2; // sz + port
offset += sz;
return DataHelper.fromLong(_message, offset, 4);
}
}
/* ------- End Reader Classes ------- */
/******
public static void main(String args[]) {
I2PAppContext ctx = I2PAppContext.getGlobalContext();
try {
PacketBuilder b = new PacketBuilder(ctx, null);
InetAddress introHost = InetAddress.getLocalHost();
int introPort = 1234;
byte introKey[] = new byte[SessionKey.KEYSIZE_BYTES];
ctx.random().nextBytes(introKey);
long introTag = ctx.random().nextLong(0xFFFFFFFFl);
long introNonce = ctx.random().nextLong(0xFFFFFFFFl);
SessionKey ourIntroKey = ctx.keyGenerator().generateSessionKey();
UDPPacket packet = b.buildRelayRequest(introHost, introPort, introKey, introTag, ourIntroKey, introNonce, false);
UDPPacketReader r = new UDPPacketReader(ctx);
r.initialize(packet);
RelayRequestReader reader = r.getRelayRequestReader();
System.out.println("Nonce: " + reader.readNonce() + " / " + introNonce);
System.out.println("Tag : " + reader.readTag() + " / " + introTag);
byte readKey[] = new byte[SessionKey.KEYSIZE_BYTES];
reader.readAliceIntroKey(readKey, 0);
System.out.println("Key : " + Base64.encode(readKey) + " / " + ourIntroKey.toBase64());
} catch (Exception e) {
e.printStackTrace();
}
}
*******/
}