Changeset 435bf819 for router


Ignore:
Timestamp:
Jun 26, 2018 3:29:08 PM (2 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
0f048a7
Parents:
1a56d5a
Message:

NTCP2: Payload and options classes

Location:
router/java/src/net/i2p/router/transport/ntcp
Files:
1 added
1 edited

Legend:

Unmodified
Added
Removed
  • router/java/src/net/i2p/router/transport/ntcp/NTCP2Payload.java

    r1a56d5a r435bf819  
    33import java.io.ByteArrayInputStream;
    44import java.io.IOException;
     5import java.util.Arrays;
    56import java.util.List;
    67
     
    2122class NTCP2Payload {
    2223
     24    public static final int BLOCK_HEADER_SIZE = 3;
     25
    2326    private static final int BLOCK_DATETIME = 0;
    2427    private static final int BLOCK_OPTIONS = 1;
     
    2831    private static final int BLOCK_PADDING = 254;
    2932
     33    /**
     34     *  For all callbacks, recommend throwing exceptions only from the handshake.
     35     *  Exceptions will get thrown out of processPayload() and prevent
     36     *  processing of succeeding blocks.
     37     */
    3038    public interface PayloadCallback {
    31         public void gotDateTime(long time);
    32         public void gotI2NP(I2NPMessage msg);
    33         public void gotOptions(byte[] options, boolean isHandshake);
    34         public void gotRI(RouterInfo ri, boolean isHandshake);
    35         public void gotTermination(int reason);
     39        public void gotDateTime(long time) throws DataFormatException;
     40
     41        public void gotI2NP(I2NPMessage msg) throws I2NPMessageException;
     42
     43        /**
     44         *  @param isHandshake true only for message 3 part 2
     45         */
     46        public void gotOptions(byte[] options, boolean isHandshake) throws DataFormatException;
     47
     48        /**
     49         *  @param ri will already be validated
     50         *  @param isHandshake true only for message 3 part 2
     51         */
     52        public void gotRI(RouterInfo ri, boolean isHandshake, boolean flood) throws DataFormatException;
     53
     54        /**
     55         *  @param lastReceived in theory could wrap around to negative, but very unlikely
     56         */
     57        public void gotTermination(int reason, long lastReceived);
     58
     59        /**
     60         *  For stats.
     61         *  @param paddingLength the number of padding bytes, not including the 3-byte block header
     62         *  @param frameLength the total size of the frame, including all blocks and block headers
     63         */
     64        public void gotPadding(int paddingLength, int frameLength);
     65
    3666        public void gotUnknown(int type, int len);
    3767    }
     
    4676     */
    4777    public static int processPayload(I2PAppContext ctx, PayloadCallback cb,
    48                                      byte[] payload, int length, boolean isHandshake)
     78                                     byte[] payload, int off, int length, boolean isHandshake)
    4979                                    throws IOException, DataFormatException, I2NPMessageException {
    5080        int blocks = 0;
    51         boolean gotRI = false;
    5281        boolean gotPadding = false;
    53         int i = 0;
    54         while (i < length) {
     82        boolean gotTermination = false;
     83        int i = off;
     84        final int end = off + length;
     85        while (i < end) {
    5586            int type = payload[i++] & 0xff;
    5687            if (gotPadding)
    5788                throw new IOException("Illegal block after padding: " + type);
     89            if (gotTermination && type != BLOCK_PADDING)
     90                throw new IOException("Illegal block after termination: " + type);
     91            if (isHandshake && blocks == 0 && type != BLOCK_ROUTERINFO)
     92                throw new IOException("Illegal first block in handshake: " + type);
    5893            int len = (int) DataHelper.fromLong(payload, i, 2);
    5994            i += 2;
    60             if (i + len > length)
    61                 throw new IOException("Block runs over frame");
     95            if (i + len > end) {
     96                throw new IOException("Block " + blocks + " type " + type + " length " + len +
     97                                      " at offset " + (i - 3 - off) + " runs over frame of size " + length +
     98                                      '\n' + net.i2p.util.HexDump.dump(payload, off, length));
     99            }
    62100            switch (type) {
     101                // don't modify i inside switch
     102
    63103                case BLOCK_DATETIME:
    64104                    if (isHandshake)
     
    71111
    72112                case BLOCK_OPTIONS:
    73                     byte[] options = null;
     113                    byte[] options = new byte[len];
     114                    System.arraycopy(payload, i, options, 0, len);
    74115                    cb.gotOptions(options, isHandshake);
    75116                    break;
     
    78119                    int flag = payload[i] & 0xff;
    79120                    RouterInfo alice = new RouterInfo();
    80                     // TODO limit
    81121                    ByteArrayInputStream bais = new ByteArrayInputStream(payload, i + 1, len - 1);
    82122                    alice.readBytes(bais, true);
    83                     // TODO validate Alice static key, pass back somehow
    84                     cb.gotRI(alice, isHandshake);
    85                     gotRI = true;
     123                    cb.gotRI(alice, isHandshake, (flag & 0x01) != 0);
    86124                    break;
    87125
     
    96134                    if (isHandshake)
    97135                        throw new IOException("Illegal block in handshake: " + type);
    98                     if (len != 9)
     136                    if (len < 9)
    99137                        throw new IOException("Bad length for TERMINATION: " + len);
    100                     int rsn = payload[i] & 0xff;
    101                     cb.gotTermination(rsn);
     138                    long last = fromLong8(payload, i);
     139                    int rsn = payload[i + 8] & 0xff;
     140                    cb.gotTermination(rsn, last);
     141                    gotTermination = true;
    102142                    break;
    103143
    104144                case BLOCK_PADDING:
    105145                    gotPadding = true;
     146                    cb.gotPadding(len, length);
    106147                    break;
    107148
     
    116157            blocks++;
    117158        }
    118         if (isHandshake && !gotRI)
    119             throw new IOException("No RI block in handshake");
     159        if (isHandshake && blocks == 0)
     160            throw new IOException("No blocks in handshake");
    120161        return blocks;
    121162    }
     
    132173    }
    133174
     175    /**
     176     *  Base class for blocks to be transmitted.
     177     *  Not used for receive; we use callbacks instead.
     178     */
    134179    public static abstract class Block {
    135180        private final int type;
     
    139184        }
    140185
     186        /** @return new offset */
    141187        public int write(byte[] tgt, int off) {
    142188            tgt[off++] = (byte) type;
    143             DataHelper.toLong(tgt, off, 2, getDataLength());
    144             return writeData(tgt, off + 2);
    145         }
    146 
     189            // we do it this way so we don't call getDataLength(),
     190            // which may be inefficient
     191            // off is where the length goes
     192            int rv = writeData(tgt, off + 2);
     193            DataHelper.toLong(tgt, off, 2, rv - (off + 2));
     194            return rv;
     195        }
     196
     197        /**
     198         *  @return the size of the block, including the 3 byte header (type and size)
     199         */
     200        public int getTotalLength() {
     201            return BLOCK_HEADER_SIZE + getDataLength();
     202        }
     203
     204        /**
     205         *  @return the size of the block, NOT including the 3 byte header (type and size)
     206         */
    147207        public abstract int getDataLength();
    148208
     209        /** @return new offset */
    149210        public abstract int writeData(byte[] tgt, int off);
     211
     212        @Override
     213        public String toString() {
     214            return "Payload block type " + type + " length " + getDataLength();
     215        }
    150216    }
    151217
    152218    public static class RIBlock extends Block {
    153219        private final byte[] data;
    154 
    155         public RIBlock(RouterInfo ri) {
     220        private final boolean f;
     221
     222        public RIBlock(RouterInfo ri, boolean flood) {
    156223            super(BLOCK_ROUTERINFO);
    157224            data = ri.toByteArray();
     225            f = flood;
    158226        }
    159227
     
    163231
    164232        public int writeData(byte[] tgt, int off) {
    165             tgt[off++] = 0;    // flag
     233            tgt[off++] = (byte) (f ? 1 : 0);    // flag
    166234            System.arraycopy(data, 0, tgt, off, data.length);
    167235            return off + data.length;
     
    170238
    171239    public static class I2NPBlock extends Block {
    172         private final byte[] data;
     240        private final I2NPMessage m;
    173241
    174242        public I2NPBlock(I2NPMessage msg) {
    175243            super(BLOCK_I2NP);
    176             data = msg.toByteArray();
    177         }
    178 
    179         public int getDataLength() {
    180             return data.length - 7;
    181         }
    182 
    183         public int writeData(byte[] tgt, int off) {
    184             // type, ID, first 4 bytes of exp
    185             System.arraycopy(data, 0, tgt, off, 9);
    186             // skip last 4 bytes of exp, sz, checksum
    187             System.arraycopy(data, 16, tgt, off + 9, data.length - 16);
    188             return off + data.length - 7;
     244            m = msg;
     245        }
     246
     247        public int getDataLength() {
     248            return m.getMessageSize() - 7;
     249        }
     250
     251        public int writeData(byte[] tgt, int off) {
     252            return m.toRawByteArrayNTCP2(tgt, off);
    189253        }
    190254    }
    191255
    192256    public static class PaddingBlock extends Block {
    193         private final byte[] data;
    194 
    195         public PaddingBlock(I2PAppContext ctx, int size) {
     257        private final int sz;
     258        private final I2PAppContext ctx;
     259
     260        /** with zero-filled data */
     261        public PaddingBlock(int size) {
     262            this(null, size);
     263        }
     264
     265        /** with random data */
     266        public PaddingBlock(I2PAppContext context, int size) {
    196267            super(BLOCK_PADDING);
    197             data = new byte[size];
    198             if (size > 0)
    199                 ctx.random().nextBytes(data);
    200         }
    201 
    202         public int getDataLength() {
    203             return data.length;
    204         }
    205 
    206         public int writeData(byte[] tgt, int off) {
    207             System.arraycopy(tgt, off, data, 0, data.length);
    208             return off + data.length;
     268            sz = size;
     269            ctx = context;
     270        }
     271
     272        public int getDataLength() {
     273            return sz;
     274        }
     275
     276        public int writeData(byte[] tgt, int off) {
     277            if (ctx != null)
     278                ctx.random().nextBytes(tgt, off, sz);
     279            else
     280                Arrays.fill(tgt, off, off + sz, (byte) 0);
     281            return off + sz;
    209282        }
    210283    }
     
    227300        }
    228301    }
     302
     303    public static class OptionsBlock extends Block {
     304        private final byte[] opts;
     305
     306        public OptionsBlock(byte[] options) {
     307            super(BLOCK_OPTIONS);
     308            opts = options;
     309        }
     310
     311        public int getDataLength() {
     312            return opts.length;
     313        }
     314
     315        public int writeData(byte[] tgt, int off) {
     316            System.arraycopy(opts, 0, tgt, off, opts.length);
     317            return off + opts.length;
     318        }
     319    }
     320
     321    public static class TerminationBlock extends Block {
     322        private final byte rsn;
     323        private final long rcvd;
     324
     325        public TerminationBlock(int reason, long lastReceived) {
     326            super(BLOCK_TERMINATION);
     327            rsn = (byte) reason;
     328            rcvd = lastReceived;
     329        }
     330
     331        public int getDataLength() {
     332            return 9;
     333        }
     334
     335        public int writeData(byte[] tgt, int off) {
     336            toLong8(tgt, off, rcvd);
     337            tgt[off + 8] = rsn;
     338            return off + 9;
     339        }
     340    }
     341
     342    /**
     343     * Big endian.
     344     * Same as DataHelper.fromLong(src, offset, 8) but allows negative result
     345     *
     346     * Package private for NTCP2Payload.
     347     *
     348     * @throws ArrayIndexOutOfBoundsException
     349     * @since 0.9.36
     350     */
     351    private static long fromLong8(byte src[], int offset) {
     352        long rv = 0;
     353        int limit = offset + 8;
     354        for (int i = offset; i < limit; i++) {
     355            rv <<= 8;
     356            rv |= src[i] & 0xFF;
     357        }
     358        return rv;
     359    }
     360   
     361    /**
     362     * Big endian.
     363     * Same as DataHelper.toLong(target, offset, 8, value) but allows negative value
     364     *
     365     * @throws ArrayIndexOutOfBoundsException
     366     * @since 0.9.36
     367     */
     368    private static void toLong8(byte target[], int offset, long value) {
     369        for (int i = offset + 7; i >= offset; i--) {
     370            target[i] = (byte) value;
     371            value >>= 8;
     372        }
     373    }
    229374}
Note: See TracChangeset for help on using the changeset viewer.