diff --git a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java index 672744444..9bbaa840b 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java @@ -4,7 +4,6 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; import java.util.Collections; -import java.util.Date; import java.util.List; import net.i2p.I2PAppContext; @@ -156,7 +155,9 @@ class PacketBuilder { * included, it should be removed from the list. */ public UDPPacket buildPacket(OutboundMessageState state, int fragment, PeerState peer, List ackIdsRemaining, List partialACKsRemaining) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader((byte)(UDPPacket.PAYLOAD_TYPE_DATA << 4)); + byte data[] = packet.getPacket().getData(); + int off = HEADER_SIZE; StringBuilder msg = null; boolean acksIncluded = false; @@ -168,19 +169,6 @@ class PacketBuilder { msg.append("*"); } - byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int start = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - int off = start; - - // header - data[off] |= (UDPPacket.PAYLOAD_TYPE_DATA << 4); - // todo: add support for rekeying and extended options - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); - off += 4; - // ok, now for the body... // just always ask for an ACK for now... @@ -322,11 +310,14 @@ class PacketBuilder { /** * Build the ack packet. The list need not be sorted into full and partial; * this method will put all fulls before the partials in the outgoing packet. + * An ack packet is just a data packet with no data. * * @param ackBitfields list of ACKBitfield instances to either fully or partially ACK */ public UDPPacket buildACK(PeerState peer, List ackBitfields) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader((byte)(UDPPacket.PAYLOAD_TYPE_DATA << 4)); + byte data[] = packet.getPacket().getData(); + int off = HEADER_SIZE; StringBuilder msg = null; if (_log.shouldLog(Log.DEBUG)) { @@ -334,18 +325,6 @@ class PacketBuilder { msg.append("building ACK packet to ").append(peer.getRemotePeer().toBase64().substring(0,6)); } - byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - - // header - data[off] |= (UDPPacket.PAYLOAD_TYPE_DATA << 4); - // todo: add support for rekeying and extended options - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); - off += 4; - int fullACKCount = 0; int partialACKCount = 0; for (int i = 0; i < ackBitfields.size(); i++) { @@ -432,7 +411,9 @@ class PacketBuilder { * @return ready to send packet, or null if there was a problem */ public UDPPacket buildSessionCreatedPacket(InboundEstablishState state, int externalPort, SessionKey ourIntroKey) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader(SESSION_CREATED_FLAG_BYTE); + byte data[] = packet.getPacket().getData(); + int off = HEADER_SIZE; InetAddress to = null; try { @@ -446,17 +427,6 @@ class PacketBuilder { state.prepareSessionCreated(); - byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - - // header - data[off] = SESSION_CREATED_FLAG_BYTE; - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); - off += 4; - byte sentIP[] = state.getSentIP(); if ( (sentIP == null) || (sentIP.length <= 0) || ( (_transport != null) && (!_transport.isValid(sentIP)) ) ) { if (_log.shouldLog(Log.ERROR)) @@ -535,7 +505,10 @@ class PacketBuilder { * @return ready to send packet, or null if there was a problem */ public UDPPacket buildSessionRequestPacket(OutboundEstablishState state) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader(SESSION_REQUEST_FLAG_BYTE); + byte data[] = packet.getPacket().getData(); + int off = HEADER_SIZE; + byte toIP[] = state.getSentIP(); if ( (_transport !=null) && (!_transport.isValid(toIP)) ) { packet.release(); @@ -550,19 +523,8 @@ class PacketBuilder { packet.release(); return null; } - - byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - - // header - data[off] = SESSION_REQUEST_FLAG_BYTE; - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); if (_log.shouldLog(Log.DEBUG)) - _log.debug("Sending request with time = " + new Date(now*1000)); - off += 4; + _log.debug("Sending request"); // now for the body System.arraycopy(state.getSentX(), 0, data, off, state.getSentX().length); @@ -622,7 +584,10 @@ class PacketBuilder { * @return ready to send packets, or null if there was a problem */ public UDPPacket buildSessionConfirmedPacket(OutboundEstablishState state, int fragmentNum, int numFragments, byte identity[]) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader(SESSION_CONFIRMED_FLAG_BYTE); + byte data[] = packet.getPacket().getData(); + int off = HEADER_SIZE; + InetAddress to = null; try { to = InetAddress.getByAddress(state.getSentIP()); @@ -633,17 +598,6 @@ class PacketBuilder { return null; } - byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - - // header - data[off] = SESSION_CONFIRMED_FLAG_BYTE; - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); - off += 4; - // now for the body data[off] |= fragmentNum << 4; data[off] |= (numFragments & 0xF); @@ -699,6 +653,36 @@ class PacketBuilder { } + /** + * Build a destroy packet, which contains a header but no body. + * + * @since 0.8.1 + */ + public UDPPacket buildSessionDestroyPacket(PeerState peer) { + UDPPacket packet = buildPacketHeader((byte)(UDPPacket.PAYLOAD_TYPE_SESSION_DESTROY << 4)); + byte data[] = packet.getPacket().getData(); + int off = HEADER_SIZE; + + StringBuilder msg = null; + if (_log.shouldLog(Log.DEBUG)) { + msg = new StringBuilder(128); + msg.append("building session destroy packet to ").append(peer.getRemotePeer().toBase64().substring(0,6)); + } + + // no body in this message + + if (msg != null) + _log.debug(msg.toString()); + + // pad up so we're on the encryption boundary + if ( (off % 16) != 0) + off += 16 - (off % 16); + packet.getPacket().setLength(off); + authenticate(packet, peer.getCurrentCipherKey(), peer.getCurrentMACKey()); + setTo(packet, peer.getRemoteIPAddress(), peer.getRemotePort()); + return packet; + } + /** * full flag info for a peerTest message. this can be fixed, * since we never rekey on test, and don't need any extended options @@ -715,19 +699,11 @@ class PacketBuilder { return buildPeerTestFromAlice(toIP, toPort, toIntroKey, toIntroKey, nonce, aliceIntroKey); } public UDPPacket buildPeerTestFromAlice(InetAddress toIP, int toPort, SessionKey toCipherKey, SessionKey toMACKey, long nonce, SessionKey aliceIntroKey) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader(PEER_TEST_FLAG_BYTE); byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - - // header - data[off] = PEER_TEST_FLAG_BYTE; - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); + int off = HEADER_SIZE; if (_log.shouldLog(Log.DEBUG)) - _log.debug("Sending peer test " + nonce + " to Bob with time = " + new Date(now*1000)); - off += 4; + _log.debug("Sending peer test " + nonce + " to Bob"); // now for the body DataHelper.toLong(data, off, 4, nonce); @@ -757,19 +733,11 @@ class PacketBuilder { * @return ready to send packet, or null if there was a problem */ public UDPPacket buildPeerTestToAlice(InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, SessionKey charlieIntroKey, long nonce) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader(PEER_TEST_FLAG_BYTE); byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - - // header - data[off] = PEER_TEST_FLAG_BYTE; - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); + int off = HEADER_SIZE; if (_log.shouldLog(Log.DEBUG)) - _log.debug("Sending peer test " + nonce + " to Alice with time = " + new Date(now*1000)); - off += 4; + _log.debug("Sending peer test " + nonce + " to Alice"); // now for the body DataHelper.toLong(data, off, 4, nonce); @@ -804,19 +772,11 @@ class PacketBuilder { public UDPPacket buildPeerTestToCharlie(InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, long nonce, InetAddress charlieIP, int charliePort, SessionKey charlieCipherKey, SessionKey charlieMACKey) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader(PEER_TEST_FLAG_BYTE); byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - - // header - data[off] = PEER_TEST_FLAG_BYTE; - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); + int off = HEADER_SIZE; if (_log.shouldLog(Log.DEBUG)) - _log.debug("Sending peer test " + nonce + " to Charlie with time = " + new Date(now*1000)); - off += 4; + _log.debug("Sending peer test " + nonce + " to Charlie"); // now for the body DataHelper.toLong(data, off, 4, nonce); @@ -849,19 +809,11 @@ class PacketBuilder { * @return ready to send packet, or null if there was a problem */ public UDPPacket buildPeerTestToBob(InetAddress bobIP, int bobPort, InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, long nonce, SessionKey bobCipherKey, SessionKey bobMACKey) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader(PEER_TEST_FLAG_BYTE); byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - - // header - data[off] = PEER_TEST_FLAG_BYTE; - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); + int off = HEADER_SIZE; if (_log.shouldLog(Log.DEBUG)) - _log.debug("Sending peer test " + nonce + " to Bob with time = " + new Date(now*1000)); - off += 4; + _log.debug("Sending peer test " + nonce + " to Bob"); // now for the body DataHelper.toLong(data, off, 4, nonce); @@ -925,22 +877,15 @@ class PacketBuilder { } public UDPPacket buildRelayRequest(InetAddress introHost, int introPort, byte introKey[], long introTag, SessionKey ourIntroKey, long introNonce, boolean encrypt) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader(PEER_RELAY_REQUEST_FLAG_BYTE); byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; + int off = HEADER_SIZE; byte ourIP[] = getOurExplicitIP(); int ourPort = getOurExplicitPort(); - // header - data[off] = PEER_RELAY_REQUEST_FLAG_BYTE; - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); if (_log.shouldLog(Log.INFO)) _log.info("Sending intro relay request to " + introHost + ":" + introPort); // + " regarding " + state.getRemoteIdentity().calculateHash().toBase64()); - off += 4; // now for the body DataHelper.toLong(data, off, 4, introTag); @@ -994,19 +939,11 @@ class PacketBuilder { private static final byte PEER_RELAY_INTRO_FLAG_BYTE = (UDPPacket.PAYLOAD_TYPE_RELAY_INTRO << 4); UDPPacket buildRelayIntro(RemoteHostId alice, PeerState charlie, UDPPacketReader.RelayRequestReader request) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader(PEER_RELAY_INTRO_FLAG_BYTE); byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - - // header - data[off] = PEER_RELAY_INTRO_FLAG_BYTE; - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); + int off = HEADER_SIZE; if (_log.shouldLog(Log.INFO)) _log.info("Sending intro to " + charlie + " for " + alice); - off += 4; // now for the body byte ip[] = alice.getIP(); @@ -1051,17 +988,9 @@ class PacketBuilder { return null; } - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader(PEER_RELAY_RESPONSE_FLAG_BYTE); byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - - // header - data[off] = PEER_RELAY_RESPONSE_FLAG_BYTE; - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); - off += 4; + int off = HEADER_SIZE; if (_log.shouldLog(Log.INFO)) _log.info("Sending relay response to " + alice + " for " + charlie + " with alice's intro key " + aliceIntroKey.toBase64()); @@ -1105,7 +1034,6 @@ class PacketBuilder { UDPPacket packet = UDPPacket.acquire(_context, false); byte data[] = packet.getPacket().getData(); Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; int ipSize = reader.getRelayIntroReader().readIPSize(); byte ip[] = new byte[ipSize]; @@ -1134,6 +1062,33 @@ class PacketBuilder { return packet; } + /** if no extended options or rekey data, which we don't support */ + private static final int HEADER_SIZE = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE + 1 + 4; + + /** + * Create a new packet and add the flag byte and the time stamp. + * Caller should add data starting at HEADER_SIZE. + * At this point, adding support for extended options and rekeying is unlikely, + * but if we do, we'll have to change this. + * + * @param flagByte contains type and flags + * @since 0.8.1 + */ + private UDPPacket buildPacketHeader(byte flagByte) { + UDPPacket packet = UDPPacket.acquire(_context, false); + byte data[] = packet.getPacket().getData(); + Arrays.fill(data, 0, data.length, (byte)0x0); + int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; + + // header + data[off] = flagByte; + off++; + long now = _context.clock().now() / 1000; + DataHelper.toLong(data, off, 4, now); + // todo: add support for rekeying and extended options + return packet; + } + private static void setTo(UDPPacket packet, InetAddress ip, int port) { packet.getPacket().setAddress(ip); packet.getPacket().setPort(port); diff --git a/router/java/src/net/i2p/router/transport/udp/UDPPacket.java b/router/java/src/net/i2p/router/transport/udp/UDPPacket.java index 1548e6036..83e7d570e 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPPacket.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPPacket.java @@ -55,6 +55,7 @@ class UDPPacket { public static final int IV_SIZE = 16; public static final int MAC_SIZE = 16; + /** Message types, 4 bits max */ public static final int PAYLOAD_TYPE_SESSION_REQUEST = 0; public static final int PAYLOAD_TYPE_SESSION_CREATED = 1; public static final int PAYLOAD_TYPE_SESSION_CONFIRMED = 2;