From 3bec2b5c733ecc30e7b40001adfb2b90dfcc51ea Mon Sep 17 00:00:00 2001 From: zzz <zzz@i2pmail.org> Date: Thu, 18 Feb 2021 10:30:51 -0500 Subject: [PATCH] I2NP: New build messages part 2 (prop. 157) Rename ShortTunnelBuildReplyMessage to OutboundTunnelBuildReplyMessage (type 26) Add InboundTunnelBuildMessage (type 27) Add methods for plaintext record Update readMessage() and writeMessageBody() Fix calculateWrittenLength() Update javadocs WIP, untested, not hooked in yet --- .../data/i2np/InboundTunnelBuildMessage.java | 127 ++++++++++++++++++ .../i2np/OutboundTunnelBuildReplyMessage.java | 127 ++++++++++++++++++ .../data/i2np/ShortTunnelBuildMessage.java | 2 +- .../i2np/ShortTunnelBuildReplyMessage.java | 76 ----------- 4 files changed, 255 insertions(+), 77 deletions(-) create mode 100644 router/java/src/net/i2p/data/i2np/InboundTunnelBuildMessage.java create mode 100644 router/java/src/net/i2p/data/i2np/OutboundTunnelBuildReplyMessage.java delete mode 100644 router/java/src/net/i2p/data/i2np/ShortTunnelBuildReplyMessage.java diff --git a/router/java/src/net/i2p/data/i2np/InboundTunnelBuildMessage.java b/router/java/src/net/i2p/data/i2np/InboundTunnelBuildMessage.java new file mode 100644 index 0000000000..77f9a6b945 --- /dev/null +++ b/router/java/src/net/i2p/data/i2np/InboundTunnelBuildMessage.java @@ -0,0 +1,127 @@ +package net.i2p.data.i2np; + +import net.i2p.I2PAppContext; +import net.i2p.data.DataHelper; + +/** + * Sent from the tunnel creator to the IBGW via an outbound tunnel. + * Contains one plaintext variable-sized request record for the IBGW + * and a variable number of encrypted records for the following hops. + * This message must be garlic-encrypted to hide the contents from the OBEP. + * + * Preliminary, see proposal 157. + * + * @since 0.9.50 + */ +public class InboundTunnelBuildMessage extends TunnelBuildMessage { + public static final int MESSAGE_TYPE = 27; + public static final int SHORT_RECORD_SIZE = ShortTunnelBuildMessage.SHORT_RECORD_SIZE; + public static final int MAX_PLAINTEXT_RECORD_SIZE = OutboundTunnelBuildReplyMessage.MAX_PLAINTEXT_RECORD_SIZE; + + private int _plaintextSlot; + private byte[] _plaintextRecord; + + /** zero record count, will be set with readMessage() */ + public InboundTunnelBuildMessage(I2PAppContext context) { + super(context, 0); + } + + public InboundTunnelBuildMessage(I2PAppContext context, int records) { + super(context, records); + } + + /** + * Set the slot and data for the plaintext record. + * @throws IllegalArgumentException on bad slot or data length. + */ + public void setPlaintextRecord(int slot, byte[] data) { + if (slot < 0 || slot >= RECORD_COUNT || data.length == 0 || data.length > MAX_PLAINTEXT_RECORD_SIZE) + throw new IllegalArgumentException(); + _plaintextSlot = slot; + _plaintextRecord = data; + } + + /** + * Get the slot for the plaintext record. + * getRecord() for this slot will return null. + */ + public int getPlaintextSlot() { + return _plaintextSlot; + } + + /** + * Get the data for the plaintext record. + */ + public byte[] getPlaintextRecord() { + return _plaintextRecord; + } + + @Override + protected int calculateWrittenLength() { return 4 + _plaintextRecord.length + ((RECORD_COUNT - 1) * SHORT_RECORD_SIZE); } + + @Override + public int getType() { return MESSAGE_TYPE; } + + @Override + 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"); + int r = data[offset++] & 0xff; + if (r <= 0 || r > MAX_RECORD_COUNT) + throw new I2NPMessageException("Bad record count " + r); + RECORD_COUNT = r; + int _plaintextSlot = data[offset++] & 0xff; + if (_plaintextSlot < 0 || _plaintextSlot >= r) + throw new I2NPMessageException("Bad slot " + _plaintextSlot); + int size = (int) DataHelper.fromLong(data, offset, 2); + if (size <= 0 || size > MAX_PLAINTEXT_RECORD_SIZE) + throw new I2NPMessageException("Bad size " + size); + offset += 2; + _plaintextRecord = new byte[size]; + System.arraycopy(data, offset, _plaintextRecord, 0, size); + offset += size; + + if (dataSize != calculateWrittenLength()) + throw new I2NPMessageException("Wrong length (expects " + calculateWrittenLength() + ", recv " + dataSize + ")"); + _records = new EncryptedBuildRecord[RECORD_COUNT]; + for (int i = 0; i < RECORD_COUNT; i++) { + if (i == _plaintextSlot) + continue; + byte rec[] = new byte[SHORT_RECORD_SIZE]; + System.arraycopy(data, offset, rec, 0, SHORT_RECORD_SIZE); + setRecord(i, new ShortEncryptedBuildRecord(rec)); + offset += SHORT_RECORD_SIZE; + } + } + + @Override + protected int writeMessageBody(byte[] out, int curIndex) throws I2NPMessageException { + int remaining = out.length - (curIndex + calculateWrittenLength()); + if (remaining < 0) + throw new I2NPMessageException("Not large enough (too short by " + remaining + ")"); + if (RECORD_COUNT <= 0 || RECORD_COUNT > MAX_RECORD_COUNT) + throw new I2NPMessageException("Bad record count " + RECORD_COUNT); + out[curIndex++] = (byte) RECORD_COUNT; + out[curIndex++] = (byte) _plaintextSlot; + DataHelper.toLong(out, curIndex, 2, _plaintextRecord.length); + curIndex += 2; + System.arraycopy(_plaintextRecord, 0, out, curIndex, _plaintextRecord.length); + curIndex += _plaintextRecord.length; + for (int i = 0; i < RECORD_COUNT; i++) { + if (i == _plaintextSlot) + continue; + System.arraycopy(_records[i].getData(), 0, out, curIndex, SHORT_RECORD_SIZE); + curIndex += SHORT_RECORD_SIZE; + } + return curIndex; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(64); + buf.append("[InboundTunnelBuildMessage: " + + "\n\tRecords: ").append(getRecordCount()) + .append(']'); + return buf.toString(); + } +} diff --git a/router/java/src/net/i2p/data/i2np/OutboundTunnelBuildReplyMessage.java b/router/java/src/net/i2p/data/i2np/OutboundTunnelBuildReplyMessage.java new file mode 100644 index 0000000000..ba8b6a513b --- /dev/null +++ b/router/java/src/net/i2p/data/i2np/OutboundTunnelBuildReplyMessage.java @@ -0,0 +1,127 @@ +package net.i2p.data.i2np; + +import net.i2p.I2PAppContext; +import net.i2p.data.DataHelper; + +/** + * Sent from the OBEP to the tunnel creator via an inbound tunnel. + * Contains one plaintext variable-sized reply record for the creator + * and a variable number of encrypted records for the following hops. + * This message must be garlic-encrypted to hide the contents from the OBGW. + * + * Preliminary, see proposal 157. + * + * @since 0.9.50 + */ +public class OutboundTunnelBuildReplyMessage extends TunnelBuildReplyMessage { + public static final int MESSAGE_TYPE = 26; + public static final int SHORT_RECORD_SIZE = ShortTunnelBuildMessage.SHORT_RECORD_SIZE; + public static final int MAX_PLAINTEXT_RECORD_SIZE = 172; + + private int _plaintextSlot; + private byte[] _plaintextRecord; + + /** zero record count, will be set with readMessage() */ + public OutboundTunnelBuildReplyMessage(I2PAppContext context) { + super(context, 0); + } + + public OutboundTunnelBuildReplyMessage(I2PAppContext context, int records) { + super(context, records); + } + + /** + * Set the slot and data for the plaintext record. + * @throws IllegalArgumentException on bad slot or data length. + */ + public void setPlaintextRecord(int slot, byte[] data) { + if (slot < 0 || slot >= RECORD_COUNT || data.length == 0 || data.length > MAX_PLAINTEXT_RECORD_SIZE) + throw new IllegalArgumentException(); + _plaintextSlot = slot; + _plaintextRecord = data; + } + + /** + * Get the slot for the plaintext record. + * getRecord() for this slot will return null. + */ + public int getPlaintextSlot() { + return _plaintextSlot; + } + + /** + * Get the data for the plaintext record. + */ + public byte[] getPlaintextRecord() { + return _plaintextRecord; + } + + @Override + protected int calculateWrittenLength() { return 4 + _plaintextRecord.length + ((RECORD_COUNT - 1) * SHORT_RECORD_SIZE); } + + @Override + public int getType() { return MESSAGE_TYPE; } + + @Override + 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"); + int r = data[offset++] & 0xff; + if (r <= 0 || r > MAX_RECORD_COUNT) + throw new I2NPMessageException("Bad record count " + r); + RECORD_COUNT = r; + _plaintextSlot = data[offset++] & 0xff; + if (_plaintextSlot < 0 || _plaintextSlot >= r) + throw new I2NPMessageException("Bad slot " + _plaintextSlot); + int size = (int) DataHelper.fromLong(data, offset, 2); + if (size <= 0 || size > MAX_PLAINTEXT_RECORD_SIZE) + throw new I2NPMessageException("Bad size " + size); + offset += 2; + _plaintextRecord = new byte[size]; + System.arraycopy(data, offset, _plaintextRecord, 0, size); + offset += size; + + if (dataSize != calculateWrittenLength()) + throw new I2NPMessageException("Wrong length (expects " + calculateWrittenLength() + ", recv " + dataSize + ")"); + _records = new EncryptedBuildRecord[RECORD_COUNT]; + for (int i = 0; i < RECORD_COUNT; i++) { + if (i == _plaintextSlot) + continue; + byte rec[] = new byte[SHORT_RECORD_SIZE]; + System.arraycopy(data, offset, rec, 0, SHORT_RECORD_SIZE); + setRecord(i, new ShortEncryptedBuildRecord(rec)); + offset += SHORT_RECORD_SIZE; + } + } + + @Override + protected int writeMessageBody(byte[] out, int curIndex) throws I2NPMessageException { + int remaining = out.length - (curIndex + calculateWrittenLength()); + if (remaining < 0) + throw new I2NPMessageException("Not large enough (too short by " + remaining + ")"); + if (RECORD_COUNT <= 0 || RECORD_COUNT > MAX_RECORD_COUNT) + throw new I2NPMessageException("Bad record count " + RECORD_COUNT); + out[curIndex++] = (byte) RECORD_COUNT; + out[curIndex++] = (byte) _plaintextSlot; + DataHelper.toLong(out, curIndex, 2, _plaintextRecord.length); + curIndex += 2; + System.arraycopy(_plaintextRecord, 0, out, curIndex, _plaintextRecord.length); + curIndex += _plaintextRecord.length; + for (int i = 0; i < RECORD_COUNT; i++) { + if (i == _plaintextSlot) + continue; + System.arraycopy(_records[i].getData(), 0, out, curIndex, SHORT_RECORD_SIZE); + curIndex += SHORT_RECORD_SIZE; + } + return curIndex; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(64); + buf.append("[OutboundTunnelBuildReplyMessage: " + + "\n\tRecords: ").append(getRecordCount()) + .append(']'); + return buf.toString(); + } +} diff --git a/router/java/src/net/i2p/data/i2np/ShortTunnelBuildMessage.java b/router/java/src/net/i2p/data/i2np/ShortTunnelBuildMessage.java index a8591589d8..36821607f4 100644 --- a/router/java/src/net/i2p/data/i2np/ShortTunnelBuildMessage.java +++ b/router/java/src/net/i2p/data/i2np/ShortTunnelBuildMessage.java @@ -22,7 +22,7 @@ public class ShortTunnelBuildMessage extends TunnelBuildMessage { } @Override - protected int calculateWrittenLength() { return 1 + super.calculateWrittenLength(); } + protected int calculateWrittenLength() { return 1 + (RECORD_COUNT * SHORT_RECORD_SIZE); } @Override public int getType() { return MESSAGE_TYPE; } diff --git a/router/java/src/net/i2p/data/i2np/ShortTunnelBuildReplyMessage.java b/router/java/src/net/i2p/data/i2np/ShortTunnelBuildReplyMessage.java deleted file mode 100644 index fab4bf0677..0000000000 --- a/router/java/src/net/i2p/data/i2np/ShortTunnelBuildReplyMessage.java +++ /dev/null @@ -1,76 +0,0 @@ -package net.i2p.data.i2np; - -import net.i2p.I2PAppContext; -import net.i2p.data.DataHelper; - -/** - * Transmitted from the new outbound endpoint to the creator through a - * reply tunnel. - * Variable size, small records. - * Preliminary, see proposal 157. - * - * @since 0.9.49 - */ -public class ShortTunnelBuildReplyMessage extends TunnelBuildReplyMessage { - public static final int MESSAGE_TYPE = 26; - public static final int SHORT_RECORD_SIZE = ShortTunnelBuildMessage.SHORT_RECORD_SIZE; - - /** zero record count, will be set with readMessage() */ - public ShortTunnelBuildReplyMessage(I2PAppContext context) { - super(context, 0); - } - - public ShortTunnelBuildReplyMessage(I2PAppContext context, int records) { - super(context, records); - } - - @Override - protected int calculateWrittenLength() { return 1 + super.calculateWrittenLength(); } - - @Override - public int getType() { return MESSAGE_TYPE; } - - @Override - 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"); - int r = data[offset] & 0xff; - if (r <= 0 || r > MAX_RECORD_COUNT) - throw new I2NPMessageException("Bad record count " + r); - RECORD_COUNT = r; - if (dataSize != calculateWrittenLength()) - throw new I2NPMessageException("Wrong length (expects " + calculateWrittenLength() + ", recv " + dataSize + ")"); - _records = new EncryptedBuildRecord[RECORD_COUNT]; - offset++; - for (int i = 0; i < RECORD_COUNT; i++) { - byte rec[] = new byte[SHORT_RECORD_SIZE]; - System.arraycopy(data, offset, rec, 0, SHORT_RECORD_SIZE); - setRecord(i, new ShortEncryptedBuildRecord(rec)); - offset += SHORT_RECORD_SIZE; - } - } - - @Override - protected int writeMessageBody(byte[] out, int curIndex) throws I2NPMessageException { - int remaining = out.length - (curIndex + calculateWrittenLength()); - if (remaining < 0) - throw new I2NPMessageException("Not large enough (too short by " + remaining + ")"); - if (RECORD_COUNT <= 0 || RECORD_COUNT > MAX_RECORD_COUNT) - throw new I2NPMessageException("Bad record count " + RECORD_COUNT); - out[curIndex++] = (byte) RECORD_COUNT; - for (int i = 0; i < RECORD_COUNT; i++) { - System.arraycopy(_records[i].getData(), 0, out, curIndex, SHORT_RECORD_SIZE); - curIndex += SHORT_RECORD_SIZE; - } - return curIndex; - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(64); - buf.append("[ShortTunnelBuildReplyMessage: " + - "\n\tRecords: ").append(getRecordCount()) - .append(']'); - return buf.toString(); - } -} -- GitLab