From 7f3db25080ce604b8a2cf0161d7055ae1a2a6cd1 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 18 May 2024 11:50:30 +0000 Subject: [PATCH] Transports: SSU1 removal part 6/n --- .../transport/udp/EstablishmentManager.java | 29 +- .../udp/OutboundMessageFragments.java | 83 +- .../router/transport/udp/PacketBuilder.java | 1588 ----------------- .../i2p/router/transport/udp/PeerState.java | 709 +------- .../i2p/router/transport/udp/PeerState2.java | 11 - .../router/transport/udp/PeerTestManager.java | 2 - .../router/transport/udp/PeerTestState.java | 3 - .../router/transport/udp/UDPTransport.java | 23 +- .../udp/UDPEndpointTestStandalone.java | 4 + 9 files changed, 22 insertions(+), 2430 deletions(-) diff --git a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java index f493a176b..dd9d51982 100644 --- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java +++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java @@ -990,17 +990,11 @@ class EstablishmentManager { RouterIdentity remote = state.getConfirmedIdentity(); PeerState peer; int version = state.getVersion(); - if (version == 1) { - peer = new PeerState(_context, _transport, - state.getSentIP(), state.getSentPort(), remote.calculateHash(), true, state.getRTT(), - state.getCipherKey(), state.getMACKey()); - peer.setWeRelayToThemAs(state.getSentRelayTag()); - } else { + InboundEstablishState2 state2 = (InboundEstablishState2) state; peer = state2.getPeerState(); // now handled in IES2.createPeerState() //peer.setWeRelayToThemAs(state.getSentRelayTag()); - } if (version == 1) { // Lookup the peer's MTU from the netdb, since it isn't included in the protocol setup (yet) @@ -1125,20 +1119,11 @@ class EstablishmentManager { if (claimed != null) _outboundByClaimedAddress.remove(claimed, state); _outboundByHash.remove(remote.calculateHash(), state); - int version = state.getVersion(); - PeerState peer; - if (version == 1) { - peer = new PeerState(_context, _transport, - state.getSentIP(), state.getSentPort(), remote.calculateHash(), false, state.getRTT(), - state.getCipherKey(), state.getMACKey()); - int mtu = state.getRemoteAddress().getMTU(); - if (mtu > 0) - peer.setHisMTU(mtu); - } else { + OutboundEstablishState2 state2 = (OutboundEstablishState2) state; // OES2 sets PS2 MTU - peer = state2.getPeerState(); - } + PeerState peer = state2.getPeerState(); + peer.setTheyRelayToUsAs(state.getReceivedRelayTag()); // 0 is the default //peer.setWeRelayToThemAs(0); @@ -1153,12 +1138,6 @@ class EstablishmentManager { _context.statManager().addRateData("udp.outboundEstablishTime", state.getLifetime(now)); DatabaseStoreMessage dbsm = null; - if (version == 1) { - // version 2 sends our RI in handshake - if (!state.isFirstMessageOurDSM()) { - dbsm = getOurInfo(); - } - } List msgs = new ArrayList(8); OutNetMessage msg; diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundMessageFragments.java b/router/java/src/net/i2p/router/transport/udp/OutboundMessageFragments.java index eff7ed632..858408cfc 100644 --- a/router/java/src/net/i2p/router/transport/udp/OutboundMessageFragments.java +++ b/router/java/src/net/i2p/router/transport/udp/OutboundMessageFragments.java @@ -53,8 +53,6 @@ class OutboundMessageFragments { private Iterator _iterator; private volatile boolean _alive; - private final PacketBuilder _builder; - // null if SSU2 not enabled private final PacketBuilder2 _builder2; /** if we can handle more messages explicitly, set this to true */ @@ -72,7 +70,6 @@ class OutboundMessageFragments { _transport = transport; // _throttle = throttle; _activePeers = new ConcurrentHashSet(256); - _builder = transport.getBuilder(); _builder2 = transport.getBuilder2(); _alive = true; // _allowExcess = false; @@ -392,34 +389,6 @@ class OutboundMessageFragments { if (states == null || peer == null) return null; - List msgIds; - int newFullAckCount; - List partialACKBitfields; - int piggybackedPartialACK; - Set remaining; - int before; - if (peer.getVersion() == 1) { - // ok, simplest possible thing is to always tack on the bitfields if - msgIds = peer.getCurrentFullACKs(); - newFullAckCount = msgIds.size(); - msgIds.addAll(peer.getCurrentResendACKs()); - partialACKBitfields = new ArrayList(); - peer.fetchPartialACKs(partialACKBitfields); - piggybackedPartialACK = partialACKBitfields.size(); - // getCurrentFullACKs() already makes a copy, do we need to copy again? - // YES because buildPacket() now removes them (maybe) - remaining = new HashSet(msgIds); - before = remaining.size(); - } else { - // all unused - msgIds = null; - newFullAckCount = 0; - partialACKBitfields = null; - piggybackedPartialACK = 0; - remaining = null; - before = 0; - } - // build the list of fragments to send List toSend = new ArrayList(8); for (OutboundMessageState state : states) { @@ -427,10 +396,7 @@ class OutboundMessageFragments { // per-state stats if (queued > 0 && state.getMaxSends() > 1) { int maxPktSz = state.fragmentSize(0); - if (peer.getVersion() == 1) - maxPktSz += (peer.isIPv6() ? PacketBuilder.MIN_IPV6_DATA_PACKET_OVERHEAD : PacketBuilder.MIN_DATA_PACKET_OVERHEAD); - else - maxPktSz += SSU2Payload.BLOCK_HEADER_SIZE + + maxPktSz += SSU2Payload.BLOCK_HEADER_SIZE + (peer.isIPv6() ? PacketBuilder2.MIN_IPV6_DATA_PACKET_OVERHEAD : PacketBuilder2.MIN_DATA_PACKET_OVERHEAD); peer.messageRetransmitted(queued, maxPktSz); // _packetsRetransmitted += toSend; // lifetime for the transport @@ -466,24 +432,18 @@ class OutboundMessageFragments { if (_log.shouldDebug()) _log.debug("Building packet for " + next + " to " + peer); int curTotalDataSize = state.fragmentSize(next.num); - if (peer.getVersion() > 1) { - curTotalDataSize += SSU2Util.FIRST_FRAGMENT_HEADER_SIZE; - if (next.num > 0) - curTotalDataSize += SSU2Util.DATA_FOLLOWON_EXTRA_SIZE; - } + curTotalDataSize += SSU2Util.FIRST_FRAGMENT_HEADER_SIZE; + if (next.num > 0) + curTotalDataSize += SSU2Util.DATA_FOLLOWON_EXTRA_SIZE; // now stuff in more fragments if they fit if (i +1 < toSend.size()) { - int maxAvail; - if (peer.getVersion() == 1) - maxAvail = PacketBuilder.getMaxAdditionalFragmentSize(peer, sendNext.size(), curTotalDataSize); - else - maxAvail = PacketBuilder2.getMaxAdditionalFragmentSize(peer, sendNext.size(), curTotalDataSize); + int maxAvail = PacketBuilder2.getMaxAdditionalFragmentSize(peer, sendNext.size(), curTotalDataSize); // if less than 16, just use it for acks, don't even try to look for a tiny fragment if (maxAvail >= 16) { for (int j = i + 1; j < toSend.size(); j++) { next = toSend.get(j); int nextDataSize = next.state.fragmentSize(next.num); - if (next.num > 0 && peer.getVersion() > 1) + if (next.num > 0) nextDataSize += SSU2Util.DATA_FOLLOWON_EXTRA_SIZE; //if (PacketBuilder.canFitAnotherFragment(peer, sendNext.size(), curTotalDataSize, nextDataSize)) { //if (_builder.canFitAnotherFragment(peer, sendNext.size(), curTotalDataSize, nextDataSize)) { @@ -493,10 +453,7 @@ class OutboundMessageFragments { j--; sendNext.add(next); curTotalDataSize += nextDataSize; - if (peer.getVersion() == 1) - maxAvail = PacketBuilder.getMaxAdditionalFragmentSize(peer, sendNext.size(), curTotalDataSize); - else - maxAvail = PacketBuilder2.getMaxAdditionalFragmentSize(peer, sendNext.size(), curTotalDataSize); + maxAvail = PacketBuilder2.getMaxAdditionalFragmentSize(peer, sendNext.size(), curTotalDataSize); if (_log.shouldLog(Log.INFO)) _log.info("Adding in additional " + next + " to " + peer); // if less than 16, just use it for acks, don't even try to look for a tiny fragment @@ -508,15 +465,11 @@ class OutboundMessageFragments { } UDPPacket pkt; - if (peer.getVersion() == 1) { - pkt = _builder.buildPacket(sendNext, peer, remaining, newFullAckCount, partialACKBitfields); - } else { try { pkt = _builder2.buildPacket(sendNext, (PeerState2) peer); } catch (IOException ioe) { pkt = null; } - } if (pkt != null) { if (_log.shouldDebug()) _log.debug("Built packet with " + sendNext.size() + " fragments totalling " + curTotalDataSize + @@ -530,25 +483,6 @@ class OutboundMessageFragments { } rv.add(pkt); - if (peer.getVersion() == 1) { - int after = remaining.size(); - newFullAckCount = Math.max(0, newFullAckCount - (before - after)); - int piggybackedAck = 0; - if (msgIds.size() != remaining.size()) { - for (int j = 0; j < msgIds.size(); j++) { - Long id = msgIds.get(j); - if (!remaining.contains(id)) { - peer.removeACKMessage(id); - piggybackedAck++; - } - } - } - if (piggybackedAck > 0) - _context.statManager().addRateData("udp.sendPiggyback", piggybackedAck); - if (piggybackedPartialACK - partialACKBitfields.size() > 0) - _context.statManager().addRateData("udp.sendPiggybackPartial", piggybackedPartialACK - partialACKBitfields.size(), state.getLifetime()); - } - // following for debugging and stats pkt.setFragmentCount(sendNext.size()); pkt.setMessageType(msgType); //type of first fragment @@ -559,8 +493,7 @@ class OutboundMessageFragments { int sent = rv.size(); peer.packetsTransmitted(sent); - if (newFullAckCount <= 0) - peer.clearWantedACKSendSince(); + peer.clearWantedACKSendSince(); if (_log.shouldDebug()) _log.debug("Sent " + fragmentsToSend + " fragments of " + states.size() + " messages in " + sent + " packets to " + peer); 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 ed7518ee1..a71e29775 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java @@ -1,112 +1,11 @@ package net.i2p.router.transport.udp; -import java.net.DatagramPacket; -import java.net.InetAddress; -import java.net.Inet6Address; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -import net.i2p.data.Base64; -import net.i2p.data.DataHelper; -import net.i2p.data.Hash; -import net.i2p.data.router.RouterIdentity; -import net.i2p.data.SessionKey; -import net.i2p.data.Signature; -import net.i2p.data.router.RouterAddress; import net.i2p.router.OutNetMessage; -import net.i2p.router.RouterContext; -import net.i2p.router.transport.TransportUtil; -import net.i2p.util.Addresses; -import net.i2p.util.Log; -import net.i2p.util.SimpleByteCache; /** - * Big ol' class to do all our packet formatting. The UDPPackets generated are - * fully authenticated, encrypted, and configured for delivery to the peer. - * - * The following is from udp.html on the website: - -

-All UDP datagrams begin with a 16 byte MAC (Message Authentication Code) -and a 16 byte IV (Initialization Vector -followed by a variable -size payload encrypted with the appropriate key. The MAC used is -HMAC-MD5, truncated to 16 bytes, while the key is a full 32 byte AES256 -key. The specific construct of the MAC is the first 16 bytes from:

-
-  HMAC-MD5(payload || IV || (payloadLength ^ protocolVersion), macKey)
-
- -

The protocol version is currently 0.

- -

The payload itself is AES256/CBC encrypted with the IV and the -sessionKey, with replay prevention addressed within its body, -explained below. The payloadLength in the MAC is a 2 byte unsigned -integer in 2s complement.

- -

The protocolVersion is a 2 byte unsigned integer in 2s complement, -and currently set to 0. Peers using a different protocol version will -not be able to communicate with this peer, though earlier versions not -using this flag are.

- -

Payload

- -

Within the AES encrypted payload, there is a minimal common structure -to the various messages - a one byte flag and a four byte sending -timestamp (*seconds* since the unix epoch). The flag byte contains -the following bitfields:

-
-Bit order: 76543210
-  bits 7-4: payload type
-     bit 3: rekey?
-     bit 2: extended options included
-  bits 1-0: reserved
-
- -

If the rekey flag is set, 64 bytes of keying material follow the -timestamp. If the extended options flag is set, a one byte option -size value is appended to, followed by that many extended option -bytes, which are currently uninterpreted.

- -

When rekeying, the first 32 bytes of the keying material is fed -into a SHA256 to produce the new MAC key, and the next 32 bytes are -fed into a SHA256 to produce the new session key, though the keys are -not immediately used. The other side should also reply with the -rekey flag set and that same keying material. Once both sides have -sent and received those values, the new keys should be used and the -previous keys discarded. It may be useful to keep the old keys -around briefly, to address packet loss and reordering.

- -

NOTE: Rekeying is currently unimplemented.

- -
- Header: 37+ bytes
- +----+----+----+----+----+----+----+----+
- |                  MAC                  |
- |                                       |
- +----+----+----+----+----+----+----+----+
- |                   IV                  |
- |                                       |
- +----+----+----+----+----+----+----+----+
- |flag|        time       | (optionally  |
- +----+----+----+----+----+              |
- | this may have 64 byte keying material |
- | and/or a one+N byte extended options) |
- +---------------------------------------|
-
- - * * */ class PacketBuilder { - private final RouterContext _context; - private final Log _log; - private final UDPTransport _transport; /** * For debugging and stats only - does not go out on the wire. @@ -190,24 +89,6 @@ class PacketBuilder { static final int PRIORITY_HIGH = 550; private static final int PRIORITY_LOW = OutNetMessage.PRIORITY_LOWEST; - /** - * No state, all methods are thread-safe. - * - * @param transport may be null for unit testing only - */ - public PacketBuilder(RouterContext ctx, UDPTransport transport) { - _context = ctx; - _transport = transport; - _log = ctx.logManager().getLog(PacketBuilder.class); - // all createRateStat in UDPTransport - } - -/**** - public UDPPacket buildPacket(OutboundMessageState state, int fragment, PeerState peer) { - return buildPacket(state, fragment, peer, null, null); - } -****/ - /** * Class for passing multiple fragments to buildPacket() * @@ -250,1473 +131,4 @@ class PacketBuilder { // _log.debug("now: " + numFragments + " / " + curDataSize + " avail: " + available); return available; } - - /** - * This builds a data packet (PAYLOAD_TYPE_DATA). - * See the methods below for the other message types. - * - * Note that while the UDP message spec allows for more than one fragment in a message, - * this method writes exactly one fragment. - * For no fragments use buildAck(). - * Multiple fragments in a single packet is not supported. - * Rekeying and extended options are not supported. - * - * Packet format: - *
-     *    16 byte MAC
-     *    16 byte IV
-     *     1 byte flag
-     *     4 byte date
-     *     1 byte flag
-     *     1 byte explicit ack count IF included
-     *   4*n byte explict acks IF included
-     *     1 byte ack bitfield count IF included
-     *   4*n + ?? ack bitfields IF included
-     *     1 byte fragment count (always 1)
-     *     4 byte message ID
-     *     3 byte fragment info
-     *     n byte fragment
-     *  0-15 bytes padding
-     *
- * - * So ignoring the ack bitfields, and assuming we have explicit acks, - * it's (47 + 4*explict acks + padding) added to the - * fragment length. - * - * @param ackIdsRemaining list of messageIds (Long) that should be acked by this packet. - * The list itself is passed by reference, and if a messageId is - * transmitted it will be removed from the list. - * Not all message IDs will necessarily be sent, there may not be room. - * non-null. - * - * @param newAckCount the number of ackIdsRemaining entries that are new. These must be the first - * ones in the list - * - * @param partialACKsRemaining list of messageIds (ACKBitfield) that should be acked by this packet. - * The list itself is passed by reference, and if a messageId is - * included, it should be removed from the list. - * Full acks in this list are skipped, they are NOT transmitted. - * non-null. - * Not all acks will necessarily be sent, there may not be room. - * - * @return null on error - */ - public UDPPacket buildPacket(OutboundMessageState state, int fragment, PeerState peer, - Collection ackIdsRemaining, int newAckCount, - List partialACKsRemaining) { - List frags = Collections.singletonList(new Fragment(state, fragment)); - return buildPacket(frags, peer, ackIdsRemaining, newAckCount, partialACKsRemaining); - } - - /* - * Multiple fragments - * - * @since 0.9.16 - */ - public UDPPacket buildPacket(List fragments, PeerState peer, - Collection ackIdsRemaining, int newAckCount, - List partialACKsRemaining) { - StringBuilder msg = null; - if (_log.shouldLog(Log.INFO)) { - msg = new StringBuilder(256); - msg.append("Data pkt to ").append(peer.getRemotePeer().toBase64()); - } - - // calculate data size - int numFragments = fragments.size(); - int dataSize = 0; - int priority = 0; - for (int i = 0; i < numFragments; i++) { - Fragment frag = fragments.get(i); - OutboundMessageState state = frag.state; - int pri = state.getPriority(); - if (pri > priority) - priority = pri; - int fragment = frag.num; - int sz = state.fragmentSize(fragment); - dataSize += sz; - if (msg != null) { - msg.append(" Fragment ").append(i); - msg.append(": msg ").append(state.getMessageId()).append(' ').append(fragment); - msg.append('/').append(state.getFragmentCount()); - msg.append(' ').append(sz); - } - } - - if (dataSize < 0) - return null; - - // calculate size available for acks - int currentMTU = peer.getMTU(); - int availableForAcks = currentMTU - dataSize; - int ipHeaderSize; - if (peer.isIPv6()) { - availableForAcks -= MIN_IPV6_DATA_PACKET_OVERHEAD; - ipHeaderSize = IPV6_HEADER_SIZE; - } else { - availableForAcks -= MIN_DATA_PACKET_OVERHEAD; - ipHeaderSize = IP_HEADER_SIZE; - } - if (numFragments > 1) - availableForAcks -= (numFragments - 1) * FRAGMENT_HEADER_SIZE; - int availableForExplicitAcks = availableForAcks; - - // make the packet - UDPPacket packet = buildPacketHeader(DATA_FLAG_BYTE); - DatagramPacket pkt = packet.getPacket(); - byte data[] = pkt.getData(); - int off = HEADER_SIZE; - - // ok, now for the body... - - // just always ask for an ACK for now... - data[off] |= UDPPacket.DATA_FLAG_WANT_REPLY; - - // partial acks have priority but they are after explicit acks in the packet - // so we have to compute the space in advance - int partialAcksToSend = 0; - if (availableForExplicitAcks >= 6 && !partialACKsRemaining.isEmpty()) { - for (ACKBitfield bf : partialACKsRemaining) { - if (partialAcksToSend >= ABSOLUTE_MAX_ACKS) - break; // ack count - // only send what we have to - //int acksz = 4 + (bf.fragmentCount() / 7) + 1; - int bits = bf.highestReceived() + 1; - if (bits <= 0) - continue; - int acksz = bits / 7; - if (bits % 7 > 0) - acksz++; - acksz += 4; - if (partialAcksToSend == 0) - acksz++; // ack count - if (availableForExplicitAcks >= acksz) { - availableForExplicitAcks -= acksz; - partialAcksToSend++; - } else { - break; - } - } - if (partialAcksToSend > 0) - data[off] |= UDPPacket.DATA_FLAG_ACK_BITFIELDS; - } - - - // Only include acks if we have at least 5 bytes available and at least - // one ack is requested. - if (availableForExplicitAcks >= 5 && !ackIdsRemaining.isEmpty()) { - data[off] |= UDPPacket.DATA_FLAG_EXPLICIT_ACK; - } - off++; - - if (msg != null) { - msg.append(" Total data: ").append(dataSize).append(" bytes, mtu: ") - .append(currentMTU).append(", ") - .append(newAckCount).append(" new full acks requested, ") - .append(ackIdsRemaining.size() - newAckCount).append(" resend acks requested, ") - .append(partialACKsRemaining.size()).append(" partial acks requested, ") - .append(availableForAcks).append(" avail. for all acks, ") - .append(availableForExplicitAcks).append(" for full acks, "); - } - - // always send all the new acks if we have room - int explicitToSend = Math.min(ABSOLUTE_MAX_ACKS, - Math.min(newAckCount + (currentMTU > PeerState.MIN_MTU ? MAX_RESEND_ACKS_LARGE : MAX_RESEND_ACKS_SMALL), - Math.min((availableForExplicitAcks - 1) / 4, ackIdsRemaining.size()))); - if (explicitToSend > 0) { - if (msg != null) - msg.append(explicitToSend).append(" full acks included:"); - data[off++] = (byte) explicitToSend; - Iterator iter = ackIdsRemaining.iterator(); - for (int i = 0; i < explicitToSend && iter.hasNext(); i++) { - Long ackId = iter.next(); - iter.remove(); - // NPE here, how did a null get in the List? - DataHelper.toLong(data, off, 4, ackId.longValue()); - off += 4; - if (msg != null) // logging it - msg.append(' ').append(ackId.longValue()); - } - //acksIncluded = true; - } - - if (partialAcksToSend > 0) { - if (msg != null) - msg.append(partialAcksToSend).append(" partial acks included:"); - int origNumRemaining = partialACKsRemaining.size(); - int numPartialOffset = off; - // leave it blank for now, since we could skip some - off++; - Iterator iter = partialACKsRemaining.iterator(); - for (int i = 0; i < partialAcksToSend && iter.hasNext(); i++) { - ACKBitfield bitfield = iter.next(); - if (bitfield.receivedComplete()) continue; - // only send what we have to - //int bits = bitfield.fragmentCount(); - int bits = bitfield.highestReceived() + 1; - if (bits <= 0) - continue; - int size = bits / 7; - if (bits % 7 > 0) - size++; - DataHelper.toLong(data, off, 4, bitfield.getMessageId()); - off += 4; - for (int curByte = 0; curByte < size; curByte++) { - if (curByte + 1 < size) - data[off] = (byte)(1 << 7); - else - data[off] = 0; - - for (int curBit = 0; curBit < 7; curBit++) { - if (bitfield.received(curBit + 7*curByte)) - data[off] |= (byte)(1 << curBit); - } - off++; - } - iter.remove(); - if (msg != null) // logging it - msg.append(' ').append(bitfield).append(" with ack bytes: ").append(size); - } - //acksIncluded = true; - // now jump back and fill in the number of bitfields *actually* included - DataHelper.toLong(data, numPartialOffset, 1, origNumRemaining - partialACKsRemaining.size()); - } - - //if ( (msg != null) && (acksIncluded) ) - // _log.debug(msg.toString()); - - data[off++] = (byte) numFragments; - - // now write each fragment - int sizeWritten = 0; - for (int i = 0; i < numFragments; i++) { - Fragment frag = fragments.get(i); - OutboundMessageState state = frag.state; - int fragment = frag.num; - - DataHelper.toLong(data, off, 4, state.getMessageId()); - off += 4; - - data[off] = (byte) (fragment << 1); - if (fragment == state.getFragmentCount() - 1) - data[off] |= 1; // isLast - off++; - - int fragSize = state.fragmentSize(fragment); - DataHelper.toLong(data, off, 2, fragSize); - data[off] &= (byte)0x3F; // 2 highest bits are reserved - off += 2; - - int sz = state.writeFragment(data, off, fragment); - off += sz; - sizeWritten += sz; - } - - if (sizeWritten != dataSize) { - if (sizeWritten < 0) { - // probably already freed from OutboundMessageState - if (_log.shouldLog(Log.WARN)) - _log.warn("Write failed for " + DataHelper.toString(fragments)); - } else { - _log.error("Size written: " + sizeWritten + " but size: " + dataSize + - " for " + DataHelper.toString(fragments)); - } - packet.release(); - return null; - //} else if (_log.shouldLog(Log.DEBUG)) { - // _log.debug("Size written: " + sizeWritten + " for fragment " + fragment - // + " of " + state.getMessageId()); - } - - // put this after writeFragment() since dataSize will be zero for use-after-free - if (dataSize == 0) { - // OK according to the protocol but if we send it, it's a bug - _log.error("Sending zero-size fragment??? for " + DataHelper.toString(fragments)); - } - - // pad up so we're on the encryption boundary - off = pad1(data, off); - off = pad2(data, off, currentMTU - (ipHeaderSize + UDP_HEADER_SIZE)); - pkt.setLength(off); - - if (msg != null) { - // verify multi-fragment packet - //if (numFragments > 1) { - // msg.append("\nDataReader dump\n:"); - // UDPPacketReader reader = new UDPPacketReader(_context); - // reader.initialize(packet); - // UDPPacketReader.DataReader dreader = reader.getDataReader(); - // try { - // msg.append(dreader.toString()); - // } catch (Exception e) { - // _log.info("blowup, dump follows", e); - // msg.append('\n'); - // msg.append(net.i2p.util.HexDump.dump(data, 0, off)); - // } - //} - msg.append(" pkt size ").append(off + (ipHeaderSize + UDP_HEADER_SIZE)); - _log.info(msg.toString()); - } - - authenticate(packet, peer.getCurrentCipherKey(), peer.getCurrentMACKey()); - setTo(packet, peer.getRemoteIPAddress(), peer.getRemotePort()); - - // FIXME ticket #2675 - // the packet could have been built before the current mtu got lowered, so - // compare to LARGE_MTU - // Also happens on switch between IPv4 and IPv6 - if (_log.shouldWarn()) { - int maxMTU = peer.isIPv6() ? PeerState.MAX_IPV6_MTU : PeerState.LARGE_MTU; - if (off + (ipHeaderSize + UDP_HEADER_SIZE) > maxMTU) { - _log.warn("Size is " + off + " for " + packet + - " data size " + dataSize + - " pkt size " + (off + (ipHeaderSize + UDP_HEADER_SIZE)) + - " MTU " + currentMTU + - ' ' + availableForAcks + " for all acks, " + - availableForExplicitAcks + " for full acks, " + - explicitToSend + " full acks included, " + - partialAcksToSend + " partial acks included, " + - " Fragments: " + DataHelper.toString(fragments), new Exception()); - } - } - - packet.setPriority(priority); - return packet; - } - - /** - * An ACK packet with no acks. - * We use this for keepalive purposes. - * It doesn't generate a reply, but that's ok. - */ - public UDPPacket buildPing(PeerState peer) { - return buildACK(peer, Collections. emptyList()); - } - - /** - * 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. - * See buildPacket() for format. - * - * TODO MTU not enforced. - * TODO handle huge number of acks better - * - * @param ackBitfields list of ACKBitfield instances to either fully or partially ACK - */ - public UDPPacket buildACK(PeerState peer, List ackBitfields) { - UDPPacket packet = buildPacketHeader(DATA_FLAG_BYTE); - DatagramPacket pkt = packet.getPacket(); - byte data[] = pkt.getData(); - int off = HEADER_SIZE; - - StringBuilder msg = null; - if (_log.shouldLog(Log.DEBUG)) { - msg = new StringBuilder(128); - msg.append("building ACK packet to ").append(peer.getRemotePeer().toBase64(), 0, 6); - } - - int fullACKCount = 0; - int partialACKCount = 0; - for (int i = 0; i < ackBitfields.size(); i++) { - if (ackBitfields.get(i).receivedComplete()) - fullACKCount++; - else - partialACKCount++; - } - // FIXME do better than this, we could still exceed MTU - if (fullACKCount > ABSOLUTE_MAX_ACKS || - partialACKCount > ABSOLUTE_MAX_ACKS) - throw new IllegalArgumentException("Too many acks full/partial " + fullACKCount + - '/' + partialACKCount); - - // ok, now for the body... - if (fullACKCount > 0) - data[off] |= UDPPacket.DATA_FLAG_EXPLICIT_ACK; - if (partialACKCount > 0) - data[off] |= UDPPacket.DATA_FLAG_ACK_BITFIELDS; - // add ECN if (peer.getSomethingOrOther()) - off++; - - if (fullACKCount > 0) { - data[off++] = (byte) fullACKCount; - for (int i = 0; i < ackBitfields.size(); i++) { - ACKBitfield bf = ackBitfields.get(i); - if (bf.receivedComplete()) { - DataHelper.toLong(data, off, 4, bf.getMessageId()); - off += 4; - if (msg != null) // logging it - msg.append(" full ack: ").append(bf.getMessageId()); - } - } - } - - if (partialACKCount > 0) { - data[off++] = (byte) partialACKCount; - for (int i = 0; i < ackBitfields.size(); i++) { - ACKBitfield bitfield = ackBitfields.get(i); - if (bitfield.receivedComplete()) continue; - DataHelper.toLong(data, off, 4, bitfield.getMessageId()); - off += 4; - // only send what we have to - //int bits = bitfield.fragmentCount(); - int bits = bitfield.highestReceived() + 1; - int size = bits / 7; - if (bits == 0 || bits % 7 > 0) - size++; - for (int curByte = 0; curByte < size; curByte++) { - if (curByte + 1 < size) - data[off] = (byte)(1 << 7); - else - data[off] = 0; - - for (int curBit = 0; curBit < 7; curBit++) { - if (bitfield.received(curBit + 7*curByte)) - data[off] |= (byte)(1 << curBit); - } - off++; - } - - if (msg != null) // logging it - msg.append(" partial ack: ").append(bitfield).append(" with ack bytes: ").append(size); - } - } - - data[off++] = 0; // no fragments in this message - - if (msg != null) - _log.debug(msg.toString()); - - // pad up so we're on the encryption boundary - off = pad1(data, off); - off = pad2(data, off); - pkt.setLength(off); - authenticate(packet, peer.getCurrentCipherKey(), peer.getCurrentMACKey()); - setTo(packet, peer.getRemoteIPAddress(), peer.getRemotePort()); - packet.setPriority((fullACKCount > 0 || partialACKCount > 0) ? PRIORITY_HIGH : PRIORITY_LOW); - return packet; - } - - /** - * Build a new SessionCreated packet for the given peer, encrypting it - * as necessary. - * - * @return ready to send packet, or null if there was a problem - */ - public UDPPacket buildSessionCreatedPacket(InboundEstablishState state, int externalPort, SessionKey ourIntroKey) { - UDPPacket packet = buildPacketHeader(SESSION_CREATED_FLAG_BYTE); - DatagramPacket pkt = packet.getPacket(); - byte data[] = pkt.getData(); - int off = HEADER_SIZE; - - InetAddress to = null; - try { - to = InetAddress.getByAddress(state.getSentIP()); - } catch (UnknownHostException uhe) { - if (_log.shouldLog(Log.ERROR)) - _log.error("How did we think this was a valid IP? " + state.getRemoteHostId().toString()); - packet.release(); - return null; - } - - state.prepareSessionCreated(); - - byte sentIP[] = state.getSentIP(); - if ( (sentIP == null) || (sentIP.length <= 0) || (!_transport.isValid(sentIP))) { - if (_log.shouldLog(Log.ERROR)) - _log.error("How did our sent IP become invalid? " + state); - state.fail(); - packet.release(); - return null; - } - // now for the body - System.arraycopy(state.getSentY(), 0, data, off, state.getSentY().length); - off += state.getSentY().length; - data[off++] = (byte) sentIP.length; - System.arraycopy(sentIP, 0, data, off, sentIP.length); - off += sentIP.length; - DataHelper.toLong(data, off, 2, state.getSentPort()); - off += 2; - DataHelper.toLong(data, off, 4, state.getSentRelayTag()); - off += 4; - DataHelper.toLong(data, off, 4, state.getSentSignedOnTime()); - off += 4; - - // handle variable signature size - Signature sig = state.getSentSignature(); - int siglen = sig.length(); - System.arraycopy(sig.getData(), 0, data, off, siglen); - off += siglen; - // ok, we need another few bytes of random padding - int rem = siglen % 16; - int padding; - if (rem > 0) { - padding = 16 - rem; - _context.random().nextBytes(data, off, padding); - off += padding; - } else { - padding = 0; - } - - if (_log.shouldLog(Log.DEBUG)) { - StringBuilder buf = new StringBuilder(128); - buf.append("Sending sessionCreated:"); - buf.append(" Alice: ").append(Addresses.toString(sentIP, state.getSentPort())); - buf.append(" Bob: ").append(Addresses.toString(state.getReceivedOurIP(), externalPort)); - buf.append(" RelayTag: ").append(state.getSentRelayTag()); - buf.append(" SignedOn: ").append(state.getSentSignedOnTime()); - buf.append(" signature: ").append(Base64.encode(sig.getData())); - buf.append("\nRawCreated: ").append(Base64.encode(data, 0, off)); - buf.append("\nsignedTime: ").append(Base64.encode(data, off - padding - siglen - 4, 4)); - _log.debug(buf.toString()); - } - - // ok, now the full data is in there, but we also need to encrypt - // the signature, which means we need the IV - byte[] iv = SimpleByteCache.acquire(UDPPacket.IV_SIZE); - _context.random().nextBytes(iv); - - int encrWrite = siglen + padding; - int sigBegin = off - encrWrite; - _context.aes().encrypt(data, sigBegin, data, sigBegin, state.getCipherKey(), iv, encrWrite); - - // pad up so we're on the encryption boundary - off = pad1(data, off); - off = pad2(data, off); - pkt.setLength(off); - authenticate(packet, ourIntroKey, ourIntroKey, iv); - setTo(packet, to, state.getSentPort()); - SimpleByteCache.release(iv); - packet.setMessageType(TYPE_CREAT); - packet.setPriority(PRIORITY_HIGH); - return packet; - } - - /** - * Build a new SessionRequest packet for the given peer, encrypting it - * as necessary. - * - * @return ready to send packet, or null if there was a problem - */ - public UDPPacket buildSessionRequestPacket(OutboundEstablishState state) { - int off = HEADER_SIZE; - byte[] options; - boolean ext = state.isExtendedOptionsAllowed(); - if (ext) { - options = new byte[UDPPacket.SESS_REQ_MIN_EXT_OPTIONS_LENGTH]; - boolean intro = state.needIntroduction(); - if (intro) - options[1] = (byte) UDPPacket.SESS_REQ_EXT_FLAG_REQUEST_RELAY_TAG; - if (_log.shouldInfo()) - _log.info("send sess req. w/ ext. options, need intro? " + intro + ' ' + state); - off += UDPPacket.SESS_REQ_MIN_EXT_OPTIONS_LENGTH + 1; - } else { - options = null; - } - UDPPacket packet = buildPacketHeader(SESSION_REQUEST_FLAG_BYTE, options); - DatagramPacket pkt = packet.getPacket(); - byte data[] = pkt.getData(); - - byte toIP[] = state.getSentIP(); - if (!_transport.isValid(toIP)) { - packet.release(); - return null; - } - InetAddress to = null; - try { - to = InetAddress.getByAddress(toIP); - } catch (UnknownHostException uhe) { - if (_log.shouldLog(Log.ERROR)) - _log.error("How did we think this was a valid IP? " + state.getRemoteHostId().toString()); - packet.release(); - return null; - } - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Sending request to " + Addresses.toString(toIP)); - - // now for the body - byte[] x = state.getSentX(); - System.arraycopy(x, 0, data, off, x.length); - off += x.length; - data[off++] = (byte) toIP.length; - System.arraycopy(toIP, 0, data, off, toIP.length); - off += toIP.length; - int port = state.getSentPort(); - DataHelper.toLong(data, off, 2, port); - off += 2; - - // we can pad here if we want, maybe randomized? - - // pad up so we're on the encryption boundary - off = pad1(data, off); - off = pad2(data, off); - pkt.setLength(off); - authenticate(packet, state.getIntroKey(), state.getIntroKey()); - setTo(packet, to, port); - packet.setMessageType(TYPE_SREQ); - packet.setPriority(PRIORITY_HIGH); - return packet; - } - - private static final int MAX_IDENTITY_FRAGMENT_SIZE = 512; - - /** - * Build a new series of SessionConfirmed packets for the given peer, - * encrypting it as necessary. - * - * Note that while a SessionConfirmed could in theory be fragmented, - * in practice a RouterIdentity is 387 bytes and a single fragment is 512 bytes max, - * so it will never be fragmented. - * - * @return ready to send packets, or null if there was a problem - * - * TODO: doesn't really return null, and caller doesn't handle null return - * (null SigningPrivateKey should cause this?) - * Should probably return null if buildSessionConfirmedPacket() returns null for any fragment - */ - public UDPPacket[] buildSessionConfirmedPackets(OutboundEstablishState state, RouterIdentity ourIdentity) { - byte identity[] = ourIdentity.toByteArray(); - int numFragments = identity.length / MAX_IDENTITY_FRAGMENT_SIZE; - if (numFragments * MAX_IDENTITY_FRAGMENT_SIZE != identity.length) - numFragments++; - UDPPacket packets[] = new UDPPacket[numFragments]; - for (int i = 0; i < numFragments; i++) - packets[i] = buildSessionConfirmedPacket(state, i, numFragments, identity); - return packets; - } - - /** - * Build a new SessionConfirmed packet for the given peer - * - * @return ready to send packets, or null if there was a problem - */ - private UDPPacket buildSessionConfirmedPacket(OutboundEstablishState state, int fragmentNum, int numFragments, byte identity[]) { - UDPPacket packet = buildPacketHeader(SESSION_CONFIRMED_FLAG_BYTE); - DatagramPacket pkt = packet.getPacket(); - byte data[] = pkt.getData(); - int off = HEADER_SIZE; - - InetAddress to = null; - try { - to = InetAddress.getByAddress(state.getSentIP()); - } catch (UnknownHostException uhe) { - if (_log.shouldLog(Log.ERROR)) - _log.error("How did we think this was a valid IP? " + state.getRemoteHostId().toString()); - packet.release(); - return null; - } - - // now for the body - data[off] = (byte) (fragmentNum << 4); - data[off] |= (numFragments & 0xF); - off++; - - int curFragSize = MAX_IDENTITY_FRAGMENT_SIZE; - if (fragmentNum == numFragments-1) { - if (identity.length % MAX_IDENTITY_FRAGMENT_SIZE != 0) - curFragSize = identity.length % MAX_IDENTITY_FRAGMENT_SIZE; - } - - DataHelper.toLong(data, off, 2, curFragSize); - off += 2; - - int curFragOffset = fragmentNum * MAX_IDENTITY_FRAGMENT_SIZE; - System.arraycopy(identity, curFragOffset, data, off, curFragSize); - off += curFragSize; - - if (fragmentNum == numFragments - 1) { - DataHelper.toLong(data, off, 4, state.getSentSignedOnTime()); - off += 4; - - // handle variable signature size - // we need to pad this so we're at the encryption boundary - Signature sig = state.getSentSignature(); - int siglen = sig.length(); - int mod = (off + siglen) & 0x0f; - if (mod != 0) { - int paddingRequired = 16 - mod; - // add an arbitrary number of 16byte pad blocks too ??? - _context.random().nextBytes(data, off, paddingRequired); - off += paddingRequired; - } - // We cannot have non-mod16 (pad2) padding here, since the signature - // is at the end. As of 0.9.7 we won't decrypt past the end of the packet - // so trailing non-mod-16 data is ignored. That truncates the sig. - - // BUG: NPE here if null signature - System.arraycopy(sig.getData(), 0, data, off, siglen); - off += siglen; - } else { - // We never get here (see above) - - // nothing more to add beyond the identity fragment - // pad up so we're on the encryption boundary - off = pad1(data, off); - // allowed but untested - //off = pad2(data, off); - } - pkt.setLength(off); - authenticate(packet, state.getCipherKey(), state.getMACKey()); - setTo(packet, to, state.getSentPort()); - packet.setMessageType(TYPE_CONF); - packet.setPriority(PRIORITY_HIGH); - return packet; - } - - - /** - * Build a destroy packet, which contains a header but no body. - * Session must be established or this will NPE in authenticate(). - * Unused until 0.8.9. - * - * @since 0.8.1 - */ - public UDPPacket buildSessionDestroyPacket(PeerState peer) { - if (_log.shouldLog(Log.DEBUG)) { - _log.debug("building session destroy packet to " + peer.getRemotePeer()); - } - return buildSessionDestroyPacket(peer.getCurrentCipherKey(), peer.getCurrentMACKey(), - peer.getRemoteIPAddress(), peer.getRemotePort()); - } - - /** - * Build a destroy packet, which contains a header but no body. - * If the keys and ip/port are not yet set, this will return null. - * - * @return packet or null - * @since 0.9.2 - */ - public UDPPacket buildSessionDestroyPacket(OutboundEstablishState peer) { - SessionKey cipherKey = peer.getCipherKey(); - SessionKey macKey = peer.getMACKey(); - byte[] ip = peer.getSentIP(); - int port = peer.getSentPort(); - if (cipherKey == null || macKey == null || ip == null || port <= 0) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Cannot send destroy, incomplete " + peer); - return null; - } - InetAddress addr; - try { - addr = InetAddress.getByAddress(ip); - } catch (UnknownHostException uhe) { - return null; - } - if (_log.shouldLog(Log.DEBUG)) - _log.debug("building session destroy packet to " + peer); - return buildSessionDestroyPacket(cipherKey, macKey, addr, port); - } - - - /** - * Build a destroy packet, which contains a header but no body. - * If the keys and ip/port are not yet set, this will return null. - * - * @return packet or null - * @since 0.9.2 - */ - public UDPPacket buildSessionDestroyPacket(InboundEstablishState peer) { - SessionKey cipherKey = peer.getCipherKey(); - SessionKey macKey = peer.getMACKey(); - byte[] ip = peer.getSentIP(); - int port = peer.getSentPort(); - if (cipherKey == null || macKey == null || ip == null || port <= 0) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Cannot send destroy, incomplete " + peer); - return null; - } - InetAddress addr; - try { - addr = InetAddress.getByAddress(ip); - } catch (UnknownHostException uhe) { - return null; - } - if (_log.shouldLog(Log.DEBUG)) - _log.debug("building session destroy packet to " + peer); - return buildSessionDestroyPacket(cipherKey, macKey, addr, port); - } - - /** - * Build a destroy packet, which contains a header but no body. - * @param cipherKey non-null - * @param macKey non-null - * @since 0.9.2 - */ - private UDPPacket buildSessionDestroyPacket(SessionKey cipherKey, SessionKey macKey, InetAddress addr, int port) { - UDPPacket packet = buildPacketHeader(SESSION_DESTROY_FLAG_BYTE); - int off = HEADER_SIZE; - - // no body in this message - - // pad up so we're on the encryption boundary - DatagramPacket pkt = packet.getPacket(); - byte data[] = pkt.getData(); - off = pad1(data, off); - off = pad2(data, off); - pkt.setLength(off); - authenticate(packet, cipherKey, macKey); - setTo(packet, addr, port); - packet.setPriority(PRIORITY_LOW); - return packet; - } - - /** - * Build a packet as if we are Alice and we either want Bob to begin a - * peer test or Charlie to finish a peer test. - * - * @return ready to send packet, or null if there was a problem - */ - public UDPPacket buildPeerTestFromAlice(InetAddress toIP, int toPort, SessionKey toIntroKey, long nonce, SessionKey aliceIntroKey) { - return buildPeerTestFromAlice(toIP, toPort, toIntroKey, toIntroKey, nonce, aliceIntroKey); - } - - /** - * Build a packet as if we are Alice and we either want Bob to begin a - * peer test or Charlie to finish a peer test. - * - * @return ready to send packet, or null if there was a problem - */ - public UDPPacket buildPeerTestFromAlice(InetAddress toIP, int toPort, SessionKey toCipherKey, SessionKey toMACKey, - long nonce, SessionKey aliceIntroKey) { - UDPPacket packet = buildPacketHeader(PEER_TEST_FLAG_BYTE); - DatagramPacket pkt = packet.getPacket(); - byte data[] = pkt.getData(); - int off = HEADER_SIZE; - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Sending peer test " + nonce + " to Bob"); - - // now for the body - DataHelper.toLong(data, off, 4, nonce); - off += 4; - data[off++] = 0; // neither Bob nor Charlie need Alice's IP from her - DataHelper.toLong(data, off, 2, 0); // neither Bob nor Charlie need Alice's port from her - off += 2; - System.arraycopy(aliceIntroKey.getData(), 0, data, off, SessionKey.KEYSIZE_BYTES); - off += SessionKey.KEYSIZE_BYTES; - - // pad up so we're on the encryption boundary - off = pad1(data, off); - off = pad2(data, off); - pkt.setLength(off); - authenticate(packet, toCipherKey, toMACKey); - setTo(packet, toIP, toPort); - packet.setMessageType(TYPE_TFA); - packet.setPriority(PRIORITY_LOW); - return packet; - } - - /** - * Build a packet as if we are either Bob or Charlie and we are helping test Alice. - * Not for use as Bob, as of 0.9.52; use in-session cipher/mac keys instead. - * - * @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) { - return buildPeerTestToAlice(aliceIP, alicePort, aliceIntroKey, aliceIntroKey, charlieIntroKey, nonce); - } - - /** - * Build a packet as if we are either Bob or Charlie and we are helping test Alice. - * - * @param aliceCipherKey the intro key if we are Charlie - * @param aliceMACKey the intro key if we are Charlie - * @return ready to send packet, or null if there was a problem - * @since 0.9.52 - */ - public UDPPacket buildPeerTestToAlice(InetAddress aliceIP, int alicePort, - SessionKey aliceCipherKey, SessionKey aliceMACKey, - SessionKey charlieIntroKey, long nonce) { - UDPPacket packet = buildPacketHeader(PEER_TEST_FLAG_BYTE); - DatagramPacket pkt = packet.getPacket(); - byte data[] = pkt.getData(); - int off = HEADER_SIZE; - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Sending peer test " + nonce + " to Alice"); - - // now for the body - DataHelper.toLong(data, off, 4, nonce); - off += 4; - byte ip[] = aliceIP.getAddress(); - data[off++] = (byte) ip.length; - System.arraycopy(ip, 0, data, off, ip.length); - off += ip.length; - DataHelper.toLong(data, off, 2, alicePort); - off += 2; - System.arraycopy(charlieIntroKey.getData(), 0, data, off, SessionKey.KEYSIZE_BYTES); - off += SessionKey.KEYSIZE_BYTES; - - // pad up so we're on the encryption boundary - off = pad1(data, off); - off = pad2(data, off); - pkt.setLength(off); - authenticate(packet, aliceCipherKey, aliceMACKey); - setTo(packet, aliceIP, alicePort); - packet.setMessageType(TYPE_TTA); - packet.setPriority(PRIORITY_LOW); - return packet; - } - - /** - * Build a packet as if we are Bob sending Charlie a packet to help test Alice. - * - * @return ready to send packet, or null if there was a problem - */ - public UDPPacket buildPeerTestToCharlie(InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, long nonce, - InetAddress charlieIP, int charliePort, - SessionKey charlieCipherKey, SessionKey charlieMACKey) { - UDPPacket packet = buildPacketHeader(PEER_TEST_FLAG_BYTE); - DatagramPacket pkt = packet.getPacket(); - byte data[] = pkt.getData(); - int off = HEADER_SIZE; - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Sending peer test " + nonce + " to Charlie"); - - // now for the body - DataHelper.toLong(data, off, 4, nonce); - off += 4; - byte ip[] = aliceIP.getAddress(); - data[off++] = (byte) ip.length; - System.arraycopy(ip, 0, data, off, ip.length); - off += ip.length; - DataHelper.toLong(data, off, 2, alicePort); - off += 2; - System.arraycopy(aliceIntroKey.getData(), 0, data, off, SessionKey.KEYSIZE_BYTES); - off += SessionKey.KEYSIZE_BYTES; - - // pad up so we're on the encryption boundary - off = pad1(data, off); - off = pad2(data, off); - pkt.setLength(off); - authenticate(packet, charlieCipherKey, charlieMACKey); - setTo(packet, charlieIP, charliePort); - packet.setMessageType(TYPE_TBC); - packet.setPriority(PRIORITY_LOW); - return packet; - } - - /** - * Build a packet as if we are Charlie sending Bob a packet verifying that we will help test Alice. - * - * @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 = buildPacketHeader(PEER_TEST_FLAG_BYTE); - DatagramPacket pkt = packet.getPacket(); - byte data[] = pkt.getData(); - int off = HEADER_SIZE; - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Sending peer test " + nonce + " to Bob"); - - // now for the body - DataHelper.toLong(data, off, 4, nonce); - off += 4; - byte ip[] = aliceIP.getAddress(); - data[off++] = (byte) ip.length; - System.arraycopy(ip, 0, data, off, ip.length); - off += ip.length; - DataHelper.toLong(data, off, 2, alicePort); - off += 2; - System.arraycopy(aliceIntroKey.getData(), 0, data, off, SessionKey.KEYSIZE_BYTES); - off += SessionKey.KEYSIZE_BYTES; - - // pad up so we're on the encryption boundary - off = pad1(data, off); - off = pad2(data, off); - pkt.setLength(off); - authenticate(packet, bobCipherKey, bobMACKey); - setTo(packet, bobIP, bobPort); - packet.setMessageType(TYPE_TCB); - packet.setPriority(PRIORITY_LOW); - return packet; - } - - /** - * build intro packets for each of the published introducers - * - * @param emgr only to call emgr.isValid() - * @return empty list on failure - */ - public List buildRelayRequest(UDPTransport transport, EstablishmentManager emgr, - OutboundEstablishState state, SessionKey ourIntroKey) { - UDPAddress addr = state.getRemoteAddress(); - int count = addr.getIntroducerCount(); - List rv = new ArrayList(count); - long cutoff = _context.clock().now() + 5*60*1000L; - for (int i = 0; i < count; i++) { - InetAddress iaddr = addr.getIntroducerHost(i); - int iport = addr.getIntroducerPort(i); - byte ikey[] = addr.getIntroducerKey(i); - long tag = addr.getIntroducerTag(i); - long exp = addr.getIntroducerExpiration(i); - // let's not use an introducer on a privileged port, sounds like trouble - if (iaddr == null) { - // normal, SSU2 slots - if (_log.shouldDebug()) - _log.debug("Cannot build a relay request for " + state.getRemoteIdentity().calculateHash() - + " slot " + i + " no address"); - continue; - } - if (ikey == null || tag <= 0) { - if (_log.shouldWarn()) - _log.warn("Cannot build a relay request for " + state.getRemoteIdentity().calculateHash() - + " slot " + i + " no key/tag"); - continue; - } - if (exp > 0 && exp < cutoff) { - if (_log.shouldWarn()) - _log.warn("Cannot build a relay request for " + state.getRemoteIdentity().calculateHash() - + ", expired " + DataHelper.formatTime(exp) - + " : " + Addresses.toString(iaddr.getAddress(), iport)); - continue; - } - // we must use the same isValid() as EstablishmentManager.receiveRelayResponse(). - // If an introducer isn't valid, we shouldn't send to it - if (!emgr.isValid(iaddr.getAddress(), iport) || - // FIXME this will have already failed in isValid() above, right? - (Arrays.equals(iaddr.getAddress(), _transport.getExternalIP()) && !_transport.allowLocal())) { - if (_log.shouldWarn()) - _log.warn("Cannot build a relay request for " + state.getRemoteIdentity().calculateHash() - + ", introducer address is invalid or blocklisted: " + Addresses.toString(iaddr.getAddress(), iport)); - // TODO implement some sort of introducer banlist - continue; - } - - // lookup session so we can use session key if available - SessionKey cipherKey = null; - SessionKey macKey = null; - // first look up by ikey, it is equal to router hash for now - PeerState bobState = null; - if (ikey.length == Hash.HASH_LENGTH) { - bobState = transport.getPeerState(new Hash(ikey)); - } - if (bobState == null) { - RemoteHostId rhid = new RemoteHostId(iaddr.getAddress(), iport); - bobState = transport.getPeerState(rhid); - } - if (bobState != null) { - if (bobState.getVersion() > 1) { - // TODO cross-version relaying, maybe - if (_log.shouldWarn()) - _log.warn("Cannot build SSU1 relay request for " + state.getRemoteIdentity().calculateHash() - + " through SSU2-connected introducer " + bobState); - continue; - } - // established session (since 0.9.12) - cipherKey = bobState.getCurrentCipherKey(); - macKey = bobState.getCurrentMACKey(); - } - if (cipherKey == null || macKey == null) { - // no session, use intro key (was only way before 0.9.12) - cipherKey = new SessionKey(ikey); - macKey = cipherKey; - if (_log.shouldLog(Log.INFO)) - _log.info("Sending relay request (w/ intro key) to " + Addresses.toString(iaddr.getAddress(), iport)); - } else { - if (_log.shouldLog(Log.INFO)) - _log.info("Sending relay request (in-session) to " + Addresses.toString(iaddr.getAddress(), iport)); - } - - UDPPacket pkt = buildRelayRequest(iaddr, iport, cipherKey, macKey, tag, ourIntroKey, state.getIntroNonce()); - if (pkt != null) - rv.add(pkt); - else if (_log.shouldWarn()) - _log.warn("Cannot build a relay request for " + state.getRemoteIdentity().calculateHash() - + ", as we don't have an address to send to: " + Addresses.toString(iaddr.getAddress(), iport)); - } - return rv; - } - - /** - * @return null on failure - */ - private UDPPacket buildRelayRequest(InetAddress introHost, int introPort, - SessionKey cipherKey, SessionKey macKey, - long introTag, SessionKey ourIntroKey, long introNonce) { - UDPPacket packet = buildPacketHeader(PEER_RELAY_REQUEST_FLAG_BYTE); - DatagramPacket pkt = packet.getPacket(); - byte data[] = pkt.getData(); - int off = HEADER_SIZE; - - // Must specify these if request is going over IPv6 for v4 or vice versa - byte ourIP[]; - int ourPort; - if (introHost instanceof Inet6Address) { - RouterAddress ra = _transport.getCurrentExternalAddress(true); - if (ra == null) { - ra = _transport.getCurrentExternalAddress(false); - if (ra == null) - return null; - } - byte[] ip = ra.getIP(); - if (ip == null) - return null; - if (ip.length != 16) { - ourIP = ip; - ourPort = _transport.getRequestedPort(); - } else { - ourIP = null; - ourPort = 0; - } - } else { - // TODO IPv4 introducer, IPv6 introduction - ourIP = null; - ourPort = 0; - } - - // now for the body - DataHelper.toLong(data, off, 4, introTag); - off += 4; - if (ourIP != null) { - data[off++] = (byte) ourIP.length; - System.arraycopy(ourIP, 0, data, off, ourIP.length); - off += ourIP.length; - } else { - data[off++] = 0; - } - - DataHelper.toLong(data, off, 2, ourPort); - off += 2; - - // challenge... - data[off++] = 0; - - System.arraycopy(ourIntroKey.getData(), 0, data, off, SessionKey.KEYSIZE_BYTES); - off += SessionKey.KEYSIZE_BYTES; - - if (_log.shouldLog(Log.DEBUG)) - _log.debug("wrote alice intro key: " + Base64.encode(data, off-SessionKey.KEYSIZE_BYTES, SessionKey.KEYSIZE_BYTES) - + " with nonce " + introNonce + " size=" + (off+4 + (16 - (off+4)%16)) - + " and data: " + Base64.encode(data, 0, off)); - - DataHelper.toLong(data, off, 4, introNonce); - off += 4; - - // pad up so we're on the encryption boundary - off = pad1(data, off); - off = pad2(data, off); - pkt.setLength(off); - authenticate(packet, cipherKey, macKey); - setTo(packet, introHost, introPort); - packet.setMessageType(TYPE_RREQ); - packet.setPriority(PRIORITY_HIGH); - return packet; - } - - UDPPacket buildRelayIntro(RemoteHostId alice, PeerState charlie, UDPPacketReader.RelayRequestReader request) { - UDPPacket packet = buildPacketHeader(PEER_RELAY_INTRO_FLAG_BYTE); - DatagramPacket pkt = packet.getPacket(); - byte data[] = pkt.getData(); - int off = HEADER_SIZE; - if (_log.shouldLog(Log.INFO)) - _log.info("Sending intro to " + charlie + " for " + alice); - - // now for the body - byte ip[] = alice.getIP(); - data[off++] = (byte) ip.length; - System.arraycopy(ip, 0, data, off, ip.length); - off += ip.length; - DataHelper.toLong(data, off, 2, alice.getPort()); - off += 2; - - int sz = request.readChallengeSize(); - data[off++] = (byte) sz; - if (sz > 0) { - request.readChallengeData(data, off); - off += sz; - } - - // pad up so we're on the encryption boundary - off = pad1(data, off); - off = pad2(data, off); - pkt.setLength(off); - authenticate(packet, charlie.getCurrentCipherKey(), charlie.getCurrentMACKey()); - setTo(packet, charlie.getRemoteIPAddress(), charlie.getRemotePort()); - packet.setMessageType(TYPE_INTRO); - packet.setPriority(PRIORITY_LOW); - return packet; - } - - UDPPacket buildRelayResponse(RemoteHostId alice, PeerState charlie, long nonce, - SessionKey cipherKey, SessionKey macKey) { - InetAddress aliceAddr = null; - try { - aliceAddr = InetAddress.getByAddress(alice.getIP()); - } catch (UnknownHostException uhe) { - return null; - } - - UDPPacket packet = buildPacketHeader(PEER_RELAY_RESPONSE_FLAG_BYTE); - DatagramPacket pkt = packet.getPacket(); - byte data[] = pkt.getData(); - int off = HEADER_SIZE; - - if (_log.shouldLog(Log.INFO)) - _log.info("Sending relay response to " + alice + " for " + charlie + " with key " + cipherKey); - - // now for the body - byte charlieIP[] = charlie.getRemoteIP(); - data[off++] = (byte) charlieIP.length; - System.arraycopy(charlieIP, 0, data, off, charlieIP.length); - off += charlieIP.length; - DataHelper.toLong(data, off, 2, charlie.getRemotePort()); - off += 2; - - // Alice IP/Port currently ignored on receive - see UDPPacketReader - byte aliceIP[] = alice.getIP(); - data[off++] = (byte) aliceIP.length; - System.arraycopy(aliceIP, 0, data, off, aliceIP.length); - off += aliceIP.length; - DataHelper.toLong(data, off, 2, alice.getPort()); - off += 2; - - DataHelper.toLong(data, off, 4, nonce); - off += 4; - - // pad up so we're on the encryption boundary - off = pad1(data, off); - off = pad2(data, off); - pkt.setLength(off); - authenticate(packet, cipherKey, macKey); - setTo(packet, aliceAddr, alice.getPort()); - packet.setMessageType(TYPE_RESP); - packet.setPriority(PRIORITY_LOW); - return packet; - } - - /** - * Creates an empty unauthenticated packet for hole punching. - * Parameters must be validated previously. - */ - public UDPPacket buildHolePunch(InetAddress to, int port) { - UDPPacket packet = UDPPacket.acquire(_context, false); - if (_log.shouldLog(Log.INFO)) - _log.info("Sending relay hole punch to " + to + ":" + port); - - // the packet is empty and does not need to be authenticated, since - // its just for hole punching - packet.getPacket().setLength(0); - setTo(packet, to, port); - - packet.setMessageType(TYPE_PUNCH); - packet.setPriority(PRIORITY_HIGH); - return packet; - } - - /** - * TESTING ONLY. - * Creates an arbitrary packet for unit testing. - * Null transport in constructor OK. - * - * @since IPv6 - */ - public UDPPacket buildPacket(byte[] data, InetAddress to, int port) { - UDPPacket packet = UDPPacket.acquire(_context, false); - byte d[] = packet.getPacket().getData(); - System.arraycopy(data, 0, d, 0, data.length); - packet.getPacket().setLength(data.length); - setTo(packet, to, port); - return packet; - } - - /** - * Create a new packet and add the flag byte and the time stamp. - * Caller should add data starting at HEADER_SIZE. - * Does not include extended options or rekeying. - * - * @param flagByte contains type and flags - * @since 0.8.1 - */ - private UDPPacket buildPacketHeader(byte flagByte) { - return buildPacketHeader(flagByte, null); - } - - /** - * Create a new packet and add the flag byte and the time stamp. - * Caller should add data starting at HEADER_SIZE. - * (if extendedOptions != null, at HEADER_SIZE + 1 + extendedOptions.length) - * Does not include rekeying. - * - * @param flagByte contains type and flags - * @param extendedOptions May be null. If non-null, we will add the associated flag here. - * 255 bytes max. - * @since 0.9.24 - */ - private UDPPacket buildPacketHeader(byte flagByte, byte[] extendedOptions) { - 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 - if (extendedOptions != null) - flagByte |= UDPPacket.HEADER_FLAG_EXTENDED_OPTIONS; - data[off] = flagByte; - off++; - // Note, this is unsigned, so we're good until February 2106 - long now = (_context.clock().now() + 500) / 1000; - DataHelper.toLong(data, off, 4, now); - // todo: add support for rekeying - // extended options - if (extendedOptions != null) { - off+= 4; - int len = extendedOptions.length; - if (len > 255) - throw new IllegalArgumentException(); - data[off++] = (byte) len; - System.arraycopy(extendedOptions, 0, data, off, len); - } - return packet; - } - - private static void setTo(UDPPacket packet, InetAddress ip, int port) { - DatagramPacket pkt = packet.getPacket(); - pkt.setAddress(ip); - pkt.setPort(port); - } - - /** - * Pad up to next 16 byte boundary and return new offset. - * These bytes will be encrypted. - * This must be called before encryption. - * - * @return new offset - * @since 0.9.7 - */ - private int pad1(byte[] data, int off) { - int mod = off & 0x0f; - if (mod == 0) - return off; - int padSize = 16 - mod; - _context.random().nextBytes(data, off, padSize); - return off + padSize; - } - - /** max is one less */ - private static final int MAX_PAD2 = 16; - - /** - * Pad a random amount (not mod 16) and return new offset. - * Packet must be well under the max length (MTU). - * These bytes will be included in the MAC calculation but not encrypted. - * This must be called before encryption. - * - * @return new offset - * @since 0.9.7 - */ - private int pad2(byte[] data, int off) { - if (!_context.getProperty(PROP_PADDING, DEFAULT_ENABLE_PADDING)) - return off; - int padSize = _context.random().nextInt(MAX_PAD2); - if (padSize == 0) - return off; - _context.random().nextBytes(data, off, padSize); - return off + padSize; - } - - /** - * Pad a random amount (not mod 16) and return new offset, - * while honoring the max length. - * These bytes will be included in the MAC calculation but not encrypted. - * This must be called before encryption. - * - * @return new offset - * @since 0.9.7 - */ - private int pad2(byte[] data, int off, int maxLen) { - if (!_context.getProperty(PROP_PADDING, DEFAULT_ENABLE_PADDING)) - return off; - if (off >= maxLen) - return off; - int padSize = _context.random().nextInt(Math.min(MAX_PAD2, 1 + maxLen - off)); - if (padSize == 0) - return off; - _context.random().nextBytes(data, off, padSize); - return off + padSize; - } - - /** - * Encrypt the packet with the cipher key and a new random IV, generate a - * MAC for that encrypted data and IV, and store the result in the packet. - * - * @param packet prepared packet with the first 32 bytes empty and a length - * whose size is mod 16 - * @param cipherKey key to encrypt the payload - * @param macKey key to generate the, er, MAC - */ - private void authenticate(UDPPacket packet, SessionKey cipherKey, SessionKey macKey) { - byte[] iv = SimpleByteCache.acquire(UDPPacket.IV_SIZE); - _context.random().nextBytes(iv); - authenticate(packet, cipherKey, macKey, iv); - SimpleByteCache.release(iv); - } - - /** - * Encrypt the packet with the cipher key and the given IV, generate a - * MAC for that encrypted data and IV, and store the result in the packet. - * The MAC used is: - * HMAC-SHA256(payload || IV || (payloadLength ^ protocolVersion), macKey)[0:15] - * - * @param packet prepared packet with the first 32 bytes empty and a length - * whose size is mod 16. - * As of 0.9.7, length non-mod-16 is allowed; the - * last 1-15 bytes are included in the MAC calculation but are not encrypted. - * @param cipherKey key to encrypt the payload - * @param macKey key to generate the, er, MAC - * @param iv IV to deliver - */ - private void authenticate(UDPPacket packet, SessionKey cipherKey, SessionKey macKey, byte[] iv) { - //long before = System.currentTimeMillis(); - DatagramPacket pkt = packet.getPacket(); - int off = pkt.getOffset(); - int hmacOff = off; - int encryptOffset = off + UDPPacket.IV_SIZE + UDPPacket.MAC_SIZE; - // including 1-15 pad - int totalSize = pkt.getLength() - UDPPacket.IV_SIZE - UDPPacket.MAC_SIZE - off; - int mod = totalSize & 0x0f; - // not including 1-15 pad - int encryptSize = totalSize - mod; - byte data[] = pkt.getData(); - _context.aes().encrypt(data, encryptOffset, data, encryptOffset, cipherKey, iv, encryptSize); - - // ok, now we need to prepare things for the MAC, which requires reordering - // Payload + IV + payloadLength - System.arraycopy(data, encryptOffset, data, off, totalSize); - off += totalSize; - System.arraycopy(iv, 0, data, off, UDPPacket.IV_SIZE); - off += UDPPacket.IV_SIZE; - // version is zero, unlikely to ever change - int plval = totalSize /* ^ PacketBuilder.PROTOCOL_VERSION */ ; - // network ID cross-check, proposal 147 - int netid = _context.router().getNetworkID(); - if (netid != 2) { - plval ^= (netid - 2) << 8; - } - DataHelper.toLong(data, off, 2, plval); - - int hmacLen = totalSize + UDPPacket.IV_SIZE + 2; - //Hash hmac = _context.hmac().calculate(macKey, data, hmacOff, hmacLen); - byte[] ba = SimpleByteCache.acquire(Hash.HASH_LENGTH); - _transport.getHMAC().calculate(macKey, data, hmacOff, hmacLen, ba, 0); - - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Authenticating " + pkt.getLength() + - "\nIV: " + Base64.encode(iv) + - "\nraw mac: " + Base64.encode(ba) + - "\nMAC key: " + macKey); - // ok, now lets put it back where it belongs... - // MAC + IV + payload - System.arraycopy(data, hmacOff, data, encryptOffset, totalSize); - //System.arraycopy(hmac.getData(), 0, data, hmacOff, UDPPacket.MAC_SIZE); - System.arraycopy(ba, 0, data, hmacOff, UDPPacket.MAC_SIZE); - SimpleByteCache.release(ba); - System.arraycopy(iv, 0, data, hmacOff + UDPPacket.MAC_SIZE, UDPPacket.IV_SIZE); - // avg. 0.06 ms on a 2005-era PC - //long timeToAuth = System.currentTimeMillis() - before; - //_context.statManager().addRateData("udp.packetAuthTime", timeToAuth, timeToAuth); - //if (timeToAuth > 100) - // _context.statManager().addRateData("udp.packetAuthTimeSlow", timeToAuth, timeToAuth); - } } diff --git a/router/java/src/net/i2p/router/transport/udp/PeerState.java b/router/java/src/net/i2p/router/transport/udp/PeerState.java index 253c99d9f..8b68b469e 100644 --- a/router/java/src/net/i2p/router/transport/udp/PeerState.java +++ b/router/java/src/net/i2p/router/transport/udp/PeerState.java @@ -46,23 +46,7 @@ public class PeerState { * is established. */ protected final Hash _remotePeer; - /** - * The AES key used to verify packets, set only after the connection is - * established. - */ - private final SessionKey _currentMACKey; - /** - * The AES key used to encrypt/decrypt packets, set only after the - * connection is established. - */ - private final SessionKey _currentCipherKey; - /** - * The pending AES key for verifying packets if we are rekeying the - * connection, or null if we are not in the process of rekeying. - */ - private SessionKey _nextMACKey; - /** when were the current cipher and MAC keys established/rekeyed? */ protected final long _keyEstablishedTime; /** @@ -86,21 +70,6 @@ public class PeerState { /** when did we last have a failed send (beginning of period) */ // private long _lastFailedSendPeriod; - /** - * Set of messageIds (Long) that we have received but not yet sent - * Since even with the smallest MTU we can fit 131 acks in a message, - * we are unlikely to get backed up on acks, so we don't keep - * them in any particular order. - */ - private final Set _currentACKs; - - /** - * list of the most recent messageIds (Long) that we have received and sent - * an ACK for. We keep a few of these around to retransmit with _currentACKs, - * hopefully saving some spurious retransmissions - */ - private final Queue _currentACKsResend; - /** when did we last send ACKs to the peer? */ protected volatile long _lastACKSend; /** when did we decide we need to ACK to this peer? */ @@ -198,8 +167,6 @@ public class PeerState { */ //private final CoDelPriorityBlockingQueue _outboundQueue; private final PriBlockingQueue _outboundQueue; - /** Message ID to sequence number */ - private final Map _ackedMessages; /** when the retransmit timer is about to trigger */ private long _retransmitTimer; @@ -321,66 +288,6 @@ public class PeerState { /** if this many acks arrive out of order, fast rtx */ private static final int FAST_RTX_ACKS = 3; - /** - * SSU 1 only. - * - * @param rtt from the EstablishState, or 0 if not available - */ - public PeerState(RouterContext ctx, UDPTransport transport, - byte[] remoteIP, int remotePort, Hash remotePeer, boolean isInbound, int rtt, - SessionKey cipherKey, SessionKey macKey) { - _context = ctx; - _log = ctx.logManager().getLog(PeerState.class); - _transport = transport; - long now = ctx.clock().now(); - _keyEstablishedTime = now; - _lastSendTime = now; - _lastReceiveTime = now; - _currentACKs = new ConcurrentHashSet(); - _currentACKsResend = new LinkedBlockingQueue(); - _slowStartThreshold = MAX_SEND_WINDOW_BYTES/2; - _receivePeriodBegin = now; - _remotePort = remotePort; - if (remoteIP.length == 4) { - _mtu = DEFAULT_MTU; - _mtuReceive = DEFAULT_MTU; - _largeMTU = transport.getMTU(false); - _minMTU = MIN_MTU; - } else { - _mtu = MIN_IPV6_MTU; - _mtuReceive = MIN_IPV6_MTU; - _largeMTU = transport.getMTU(true); - _minMTU = MIN_IPV6_MTU; - } - // RFC 5681 sec. 3.1 - if (_mtu > 1095) - _sendWindowBytes = 3 * _mtu; - else - _sendWindowBytes = 4 * _mtu; - _sendWindowBytesRemaining = _sendWindowBytes; - - _rto = INIT_RTO; - _rtt = INIT_RTT; - if (rtt > 0) - recalculateTimeouts(rtt); - else - _rttDeviation = _rtt; - - _inboundMessages = new HashMap(8); - _outboundMessages = new CachedIteratorCollection(); - //_outboundQueue = new CoDelPriorityBlockingQueue(ctx, "UDP-PeerState", 32); - _outboundQueue = new PriBlockingQueue(ctx, "UDP-PeerState", 32); - _ackedMessages = new AckedMessages(); - // all createRateStat() moved to EstablishmentManager - _remoteIP = remoteIP; - _remotePeer = remotePeer; - _isInbound = isInbound; - _remoteHostId = new RemoteHostId(remoteIP, remotePort); - _bwEstimator = new SimpleBandwidthEstimator(ctx, this); - _currentCipherKey = cipherKey; - _currentMACKey = macKey; - } - /** * For SSU2 * @@ -425,12 +332,6 @@ public class PeerState { _isInbound = isInbound; _remoteHostId = new RemoteHostId(_remoteIP, _remotePort); _bwEstimator = new SimpleBandwidthEstimator(ctx, this); - // Unused in SSU2 - _currentACKs = null; - _currentACKsResend = null; - _ackedMessages = null; - _currentCipherKey = null; - _currentMACKey = null; } /** @@ -453,41 +354,6 @@ public class PeerState { * The peer are we talking to. Non-null. */ public Hash getRemotePeer() { return _remotePeer; } - /** - * The AES key used to verify packets, set only after the connection is - * established. - * - * SSU 1 only. - */ - SessionKey getCurrentMACKey() { return _currentMACKey; } - /** - * The AES key used to encrypt/decrypt packets, set only after the - * connection is established. - * - * SSU 1 only. - */ - SessionKey getCurrentCipherKey() { return _currentCipherKey; } - - /** - * The pending AES key for verifying packets if we are rekeying the - * connection, or null if we are not in the process of rekeying. - * - * SSU 1 only. - * - * @return null always, rekeying unimplemented - */ - SessionKey getNextMACKey() { return _nextMACKey; } - - /** - * The pending AES key for encrypting/decrypting packets if we are - * rekeying the connection, or null if we are not in the process - * of rekeying. - * - * SSU 1 only. - * - * @return null always, rekeying unimplemented - */ - SessionKey getNextCipherKey() { return null; } /** * When were the current cipher and MAC keys established/rekeyed? @@ -794,9 +660,6 @@ public class PeerState { _receiveBytes = 0; _receivePeriodBegin = now; } - // null for PeerState2 - if (_currentACKs != null) - _currentACKs.add(messageId); messagePartiallyReceived(now); } @@ -809,14 +672,12 @@ public class PeerState { /** * We received a partial message, or we want to send some acks. + * SSU1 only, see override + * * @since 0.9.52 */ protected synchronized void messagePartiallyReceived(long now) { - _lastReceiveTime = now; - if (_wantACKSendSince <= 0) { - _wantACKSendSince = now; - new ACKTimer(); - } + throw new UnsupportedOperationException(); } /** @@ -894,254 +755,6 @@ public class PeerState { " BWE: " + DataHelper.formatSize2Decimal((long) (bwe * 1000), false) + "bps"); } - /** - * Grab a list of message ids (Long) that we want to send to the remote - * peer, regardless of the packet size, but don't remove it from our - * "want to send" list. If the message id is transmitted to the peer, - * removeACKMessage(Long) should be called. - * - * The returned list contains acks not yet sent only. - * The caller should NOT transmit all of them all the time, - * even if there is room, - * or the packets will have way too much overhead. - * - * SSU 1 only. - * - * @return a new list, do as you like with it - */ - List getCurrentFullACKs() { - // no such element exception seen here - List rv = new ArrayList(_currentACKs); - return rv; - } - - /** - * Grab a list of message ids (Long) that we want to send to the remote - * peer, regardless of the packet size, but don't remove it from our - * "want to send" list. - * - * The returned list contains - * a random assortment of acks already sent. - * The caller should NOT transmit all of them all the time, - * even if there is room, - * or the packets will have way too much overhead. - * - * SSU 1 only. - * - * @return a new list, do as you like with it - * @since 0.8.12 was included in getCurrentFullACKs() - */ - List getCurrentResendACKs() { - int sz = _currentACKsResend.size(); - List randomResends = new ArrayList(sz); - if (sz > 0) { - long cutoff = _context.clock().now() - RESEND_ACK_TIMEOUT; - int i = 0; - for (Iterator iter = _currentACKsResend.iterator(); iter.hasNext(); ) { - ResendACK rack = iter.next(); - if (rack.time > cutoff && i++ < MAX_RESEND_ACKS) { - randomResends.add(rack.id); - } else { - iter.remove(); - if (_log.shouldDebug()) - _log.debug("Expired ack " + rack.id + " sent " + (cutoff + RESEND_ACK_TIMEOUT - rack.time) + - " ago, now " + _currentACKsResend.size() + " resend acks"); - } - } - if (i > 1) - Collections.shuffle(randomResends, _context.random()); - } - return randomResends; - } - - /** - * The ack was sent. - * Side effect - sets _lastACKSend - * - * SSU 1 only. - */ - void removeACKMessage(Long messageId) { - boolean removed = _currentACKs.remove(messageId); - if (removed) { - // only add if removed from current, as this may be called for - // acks already in _currentACKsResend. - _currentACKsResend.offer(new ResendACK(messageId, _context.clock().now())); - // trim happens in getCurrentResendACKs above - if (_log.shouldDebug()) - _log.debug("Sent ack " + messageId + " now " + _currentACKs.size() + " current and " + - _currentACKsResend.size() + " resend acks"); - } - // should we only do this if removed? - _lastACKSend = _context.clock().now(); - } - - /** - * Only called by ACKTimer with alwaysIncludeRetransmissions = false. - * So this is only for ACK-only packets, so all the size limiting is useless. - * FIXME. - * - * Caller should sync on this. - * - * Side effect - sets _lastACKSend to now if rv is non-empty. - * Side effect - sets _wantACKSendSince to 0 if _currentACKs is now empty. - * - * SSU 1 only. - * - * @return non-null, possibly empty - */ - private List retrieveACKBitfields(boolean alwaysIncludeRetransmissions) { - int bytesRemaining = countMaxACKData(); - - // Limit the overhead of all the resent acks when using small MTU - // 64 bytes in a 608-byte packet is too much... - // Send a random subset of all the queued resend acks. - int resendSize = _currentACKsResend.size(); - int maxResendAcks; - if (bytesRemaining < MIN_MTU) - maxResendAcks = MAX_RESEND_ACKS_SMALL; - else - maxResendAcks = MAX_RESEND_ACKS_LARGE; - List rv = new ArrayList(maxResendAcks); - - // save to add to currentACKsResend later so we don't include twice - List currentACKsRemoved = new ArrayList(_currentACKs.size()); - // As explained above, we include the acks in any order - // since we are unlikely to get backed up - - // just take them using the Set iterator. - Iterator iter = _currentACKs.iterator(); - while (bytesRemaining >= 4 && iter.hasNext()) { - Long val = iter.next(); - iter.remove(); - long id = val.longValue(); - rv.add(new FullACKBitfield(id)); - currentACKsRemoved.add(val); - bytesRemaining -= 4; - } - if (_currentACKs.isEmpty()) - _wantACKSendSince = 0; - if (alwaysIncludeRetransmissions || !rv.isEmpty()) { - List randomResends = getCurrentResendACKs(); - // now repeat by putting in some old ACKs - // randomly selected from the Resend queue. - // Maybe we should only resend each one a certain number of times... - int oldIndex = Math.min(resendSize, maxResendAcks); - iter = randomResends.iterator(); - while (bytesRemaining >= 4 && oldIndex-- > 0 && iter.hasNext()) { - Long cur = iter.next(); - long c = cur.longValue(); - FullACKBitfield bf = new FullACKBitfield(c); - // try to avoid duplicates ?? - // ACKsResend is not checked for dups at add time - //if (rv.contains(bf)) { - // iter.remove(); - //} else { - rv.add(bf); - bytesRemaining -= 4; - //} - } - if (!currentACKsRemoved.isEmpty()) { - long now = _context.clock().now(); - for (Long val : currentACKsRemoved) { - _currentACKsResend.offer(new ResendACK(val, now)); - } - // trim happens in getCurrentResendACKs above - } - } - - int partialIncluded = 0; - if (bytesRemaining > 4) { - // ok, there's room to *try* to fit in some partial ACKs, so - // we should try to find some packets to partially ACK - // (preferably the ones which have the most received fragments) - List partial = new ArrayList(); - fetchPartialACKs(partial); - // we may not be able to use them all, but lets try... - for (int i = 0; (bytesRemaining > 4) && (i < partial.size()); i++) { - ACKBitfield bitfield = partial.get(i); - int bytes = (bitfield.fragmentCount() / 7) + 1; - if (bytesRemaining > bytes + 4) { // msgId + bitfields - rv.add(bitfield); - bytesRemaining -= bytes + 4; - partialIncluded++; - } else { - // continue on to another partial, in case there's a - // smaller one that will fit - } - } - } - - if (!rv.isEmpty()) - _lastACKSend = _context.clock().now(); - if (partialIncluded > 0) - _context.statManager().addRateData("udp.sendACKPartial", partialIncluded, rv.size() - partialIncluded); - return rv; - } - - /** - * SSU 1 only. - * - * @param rv out parameter, populated with true partial ACKBitfields. - * no full bitfields are included. - */ - void fetchPartialACKs(List rv) { - List states = null; - int curState = 0; - synchronized (_inboundMessages) { - int numMessages = _inboundMessages.size(); - if (numMessages <= 0) - return; - // todo: make this a list instead of a map, so we can iterate faster w/out the memory overhead? - for (Iterator iter = _inboundMessages.values().iterator(); iter.hasNext(); ) { - InboundMessageState state = iter.next(); - if (state.isExpired()) { - //if (_context instanceof RouterContext) - // ((RouterContext)_context).messageHistory().droppedInboundMessage(state.getMessageId(), state.getFrom(), "expired partially received: " + state.toString()); - iter.remove(); - // state.releaseResources() ?? - } else { - if (!state.isComplete()) { - if (states == null) - states = new ArrayList(numMessages); - states.add(state); - } - } - } - } - if (states != null) { - for (InboundMessageState ims : states) { - ACKBitfield abf = ims.createACKBitfield(); - rv.add(abf); - } - } - } - - /** - * A dummy "partial" ack which represents a full ACK of a message - * - * SSU 1 only. - */ - private static class FullACKBitfield implements ACKBitfield { - private final long _msgId; - - public FullACKBitfield(long id) { _msgId = id; } - - public int fragmentCount() { return 1; } - public int ackCount() { return 1; } - public int highestReceived() { return 0; } - public long getMessageId() { return _msgId; } - public boolean received(int fragmentNum) { return true; } - public boolean receivedComplete() { return true; } - @Override - public int hashCode() { return (int) _msgId; } - @Override - public boolean equals(Object o) { - if (!(o instanceof FullACKBitfield)) return false; - return _msgId == ((ACKBitfield)o).getMessageId(); - } - @Override - public String toString() { return "Full ACK " + _msgId; } - } - /** * We sent a message which was ACKed containing the given # of bytes. * Caller should synch on this @@ -1391,48 +1004,12 @@ public class PeerState { /** * All acks have been sent. * - * SSU 1 only. + * SSU 1 only, see override * * @since 0.9.52 */ synchronized void clearWantedACKSendSince() { - // race prevention - if (_currentACKs.isEmpty()) - _wantACKSendSince = 0; - } - - /** - * Are we out of room to send all the current unsent acks in a single packet? - * This is a huge threshold (134 for small MTU and 255 for large MTU) - * that is rarely if ever exceeded in practice. - * So just use a fixed threshold of half the resend acks, so that if the - * packet is lost the acks have a decent chance of getting retransmitted. - * Used only by ACKSender. - * - * SSU 1 only. - */ - boolean unsentACKThresholdReached() { - return _currentACKs.size() >= MAX_RESEND_ACKS / 2; - } - - /** - * SSU 1 only. - * - * @return how many bytes available for acks in an ack-only packet, == MTU - 83 - * Max of 1020 - */ - private int countMaxACKData() { - return Math.min(PacketBuilder.ABSOLUTE_MAX_ACKS * 4, - _mtu - - (_remoteIP.length == 4 ? PacketBuilder.IP_HEADER_SIZE : PacketBuilder.IPV6_HEADER_SIZE) - - PacketBuilder.UDP_HEADER_SIZE - - UDPPacket.IV_SIZE - - UDPPacket.MAC_SIZE - - 1 // type flag - - 4 // timestamp - - 1 // data flag - - 1 // # ACKs - - 16); // padding safety + throw new UnsupportedOperationException(); } /** @@ -1860,197 +1437,6 @@ public class PeerState { } } - /** - * A full ACK was received. - * TODO if messages awaiting ack were a HashMap<Long, OutboundMessageState> this would be faster. - * - * SSU 1 only. - * - * @param highestSeqNumAcked in/out param, will modify if this seq. number is higher - * @return true if the message was acked for the first time - */ - boolean acked(long messageId, ModifiableLong highestSeqNumAcked) { - if (_dead) return false; - OutboundMessageState state = null; - boolean anyPending; - synchronized (_outboundMessages) { - for (Iterator iter = _outboundMessages.iterator(); iter.hasNext(); ) { - state = iter.next(); - if (state.getMessageId() == messageId) { - iter.remove(); - break; - } else if (state.getPushCount() <= 0) { - // _outboundMessages is ordered, so once we get to a msg that - // hasn't been transmitted yet, we can stop - state = null; - break; - } else { - state = null; - } - } - anyPending = !_outboundMessages.isEmpty(); - } - - if (state != null) { - int numSends = state.getMaxSends(); - long lifetime = state.getLifetime(); - if (_log.shouldDebug()) - _log.debug("Received ack of " + messageId + " by " + _remotePeer - + " after " + lifetime + " and " + numSends + " sends"); - _context.statManager().addRateData("udp.sendConfirmTime", lifetime); - if (state.getFragmentCount() > 1) - _context.statManager().addRateData("udp.sendConfirmFragments", state.getFragmentCount()); - _context.statManager().addRateData("udp.sendConfirmVolley", numSends); - _transport.succeeded(state); - boolean anyQueued; - if (anyPending) { - // locked_messageACKed will nudge() - anyQueued = false; - } else { - synchronized (_outboundQueue) { - anyQueued = !_outboundQueue.isEmpty(); - } - } - long sn = state.getSeqNum(); - if (sn > highestSeqNumAcked.value) - highestSeqNumAcked.value = sn; - synchronized(_ackedMessages) { - _ackedMessages.put(Integer.valueOf((int) messageId), Long.valueOf(sn)); - } - // this adjusts the rtt/rto/window/etc - int maxPktSz = state.fragmentSize(0) + - (isIPv6() ? PacketBuilder.MIN_IPV6_DATA_PACKET_OVERHEAD : PacketBuilder.MIN_DATA_PACKET_OVERHEAD); - messageACKed(state.getUnackedSize(), maxPktSz, lifetime, numSends, anyPending, anyQueued); - } else { - // dupack, likely - Long seq; - synchronized(_ackedMessages) { - seq = _ackedMessages.get(Integer.valueOf((int) messageId)); - } - if (seq != null) { - long sn = seq.longValue(); - if (sn > highestSeqNumAcked.value) - highestSeqNumAcked.value = sn; - } - //if (_log.shouldLog(Log.DEBUG)) - // _log.debug("Received an ACK for a message not pending: " + messageId); - } - return state != null; - } - - /** - * A partial ACK was received. This is much less common than full ACKs. - * - * SSU 1 only. - * - * @param highestSeqNumAcked in/out param, will modify if this seq. number is higher - * @return true if any fragment of the message was completely acked for the first time - */ - boolean acked(ACKBitfield bitfield, ModifiableLong highestSeqNumAcked) { - if (_dead) - return false; - - final long messageId = bitfield.getMessageId(); - if (bitfield.receivedComplete()) { - return acked(messageId, highestSeqNumAcked); - } - - OutboundMessageState state = null; - boolean isComplete = false; - boolean anyPending; - int ackedSize = 0; - synchronized (_outboundMessages) { - for (Iterator iter = _outboundMessages.iterator(); iter.hasNext(); ) { - state = iter.next(); - if (state.getMessageId() == messageId) { - ackedSize = state.getUnackedSize(); - boolean complete = state.acked(bitfield); - if (complete) { - isComplete = true; - iter.remove(); - } else { - ackedSize -= state.getUnackedSize(); - } - break; - } else if (state.getPushCount() <= 0) { - // _outboundMessages is ordered, so once we get to a msg that - // hasn't been transmitted yet, we can stop - state = null; - break; - } else { - state = null; - } - } - anyPending = !_outboundMessages.isEmpty(); - } - - if (state != null) { - int numSends = state.getMaxSends(); - - int numACKed = bitfield.ackCount(); - _context.statManager().addRateData("udp.partialACKReceived", numACKed); - - long lifetime = state.getLifetime(); - if (isComplete) { - _context.statManager().addRateData("udp.sendConfirmTime", lifetime); - if (state.getFragmentCount() > 1) - _context.statManager().addRateData("udp.sendConfirmFragments", state.getFragmentCount()); - _context.statManager().addRateData("udp.sendConfirmVolley", numSends); - _transport.succeeded(state); - if (_log.shouldDebug()) - _log.debug("Received partial ack of " + messageId + " by " + _remotePeer - + " newly-acked: " + ackedSize - + ", now complete for: " + state); - } else { - if (_log.shouldDebug()) - _log.debug("Received partial ack of " + messageId + " by " + _remotePeer - + " after " + lifetime + " and " + numSends + " sends" - + " complete? false" - + " newly-acked: " + ackedSize - + ' ' + bitfield - + " for: " + state); - } - if (ackedSize > 0) { - state.clearNACKs(); - boolean anyQueued; - if (anyPending) { - // locked_messageACKed will nudge() - anyQueued = false; - } else { - synchronized (_outboundQueue) { - anyQueued = !_outboundQueue.isEmpty(); - } - } - // this adjusts the rtt/rto/window/etc - messageACKed(ackedSize, 0, lifetime, numSends, anyPending, anyQueued); - } - // we do this even if only partial - long sn = state.getSeqNum(); - if (sn > highestSeqNumAcked.value) - highestSeqNumAcked.value = sn; - if (isComplete) { - synchronized(_ackedMessages) { - _ackedMessages.put(Integer.valueOf((int) messageId), Long.valueOf(sn)); - } - } - return ackedSize > 0; - } else { - // dupack - Long seq; - synchronized(_ackedMessages) { - seq = _ackedMessages.get(Integer.valueOf((int) messageId)); - } - if (seq != null) { - long sn = seq.longValue(); - if (sn > highestSeqNumAcked.value) - highestSeqNumAcked.value = sn; - } - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Received an ACK for a message not pending: " + bitfield); - return false; - } - } - /** * An ACK of a fragment was received. * @@ -2268,28 +1654,6 @@ public class PeerState { _sendWindowBytes = oldPeer._sendWindowBytes; oldPeer._dead = true; - if (getVersion() == 1 && oldPeer.getVersion() == 1) { - List tmp = new ArrayList(); - // AIOOBE from concurrent access - //tmp.addAll(oldPeer._currentACKs); - for (Long l : oldPeer._currentACKs) { - tmp.add(l); - } - oldPeer._currentACKs.clear(); - - if (!_dead) { - _currentACKs.addAll(tmp); - } - - List tmp3 = new ArrayList(); - tmp3.addAll(oldPeer._currentACKsResend); - oldPeer._currentACKsResend.clear(); - - if (!_dead) { - _currentACKsResend.addAll(tmp3); - } - } - if (getVersion() == oldPeer.getVersion()) { Map msgs = new HashMap(); synchronized (oldPeer._inboundMessages) { @@ -2356,69 +1720,6 @@ public class PeerState { } } - /** - * A timer to send an ack-only packet. - * - * SSU 1 only. - * - * @since 0.9.52 - */ - private class ACKTimer extends SimpleTimer2.TimedEvent { - public ACKTimer() { - super(_context.simpleTimer2()); - long delta = Math.max(10, Math.min(_rtt/6, ACK_FREQUENCY)); - if (_log.shouldDebug()) - _log.debug("Sending delayed ack in " + delta + ": " + PeerState.this); - schedule(delta); - } - - /** - * Send an ack-only packet, unless acks were already sent - * as indicated by _wantACKSendSince == 0. - * Will not requeue unless the acks don't all fit (unlikely). - */ - public void timeReached() { - synchronized(PeerState.this) { - long wanted = _wantACKSendSince; - if (wanted <= 0) { - if (_log.shouldDebug()) - _log.debug("Already acked:" + PeerState.this); - return; - } - List ackBitfields = retrieveACKBitfields(false); - - if (!ackBitfields.isEmpty()) { - UDPPacket ack = _transport.getBuilder().buildACK(PeerState.this, ackBitfields); - ack.markType(1); - ack.setFragmentCount(-1); - ack.setMessageType(PacketBuilder.TYPE_ACK); - - if (_log.shouldDebug()) { - //_log.debug("Sending " + ackBitfields + " to " + PeerState.this); - _log.debug("Sending " + ackBitfields.size() + " acks to " + PeerState.this); - } - // locking issues, we ignore the result, and acks are small, - // so don't even bother allocating - //peer.allocateSendingBytes(ack.getPacket().getLength(), true); - // ignore whether its ok or not, its a bloody ack. this should be fixed, probably. - _transport.send(ack); - - if (_wantACKSendSince > 0) { - // still full packets left to be ACKed, since wanted time - // is reset by retrieveACKBitfields when all of the IDs are - // removed - if (_log.shouldInfo()) - _log.info("Requeueing more ACKs for " + PeerState.this); - reschedule(25); - } - } else { - if (_log.shouldDebug()) - _log.debug("No more acks:" + PeerState.this); - } - } - } - } - // why removed? Some risk of dups in OutboundMessageFragments._activePeers ??? /* diff --git a/router/java/src/net/i2p/router/transport/udp/PeerState2.java b/router/java/src/net/i2p/router/transport/udp/PeerState2.java index ef46abe2c..f32a62bcc 100644 --- a/router/java/src/net/i2p/router/transport/udp/PeerState2.java +++ b/router/java/src/net/i2p/router/transport/udp/PeerState2.java @@ -326,17 +326,6 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback return true; } - // SSU 1 unsupported things - - @Override - List getCurrentFullACKs() { throw new UnsupportedOperationException(); } - @Override - List getCurrentResendACKs() { throw new UnsupportedOperationException(); } - @Override - void removeACKMessage(Long messageId) { throw new UnsupportedOperationException(); } - @Override - void fetchPartialACKs(List rv) { throw new UnsupportedOperationException(); } - // SSU 2 things /// begin SSU2Sender interface /// diff --git a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java index a94ab89b7..93dcb13ef 100644 --- a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java +++ b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java @@ -138,7 +138,6 @@ class PeerTestManager { private final RouterContext _context; private final Log _log; private final UDPTransport _transport; - private final PacketBuilder _packetBuilder; private final PacketBuilder2 _packetBuilder2; /** map of Long(nonce) to PeerTestState for tests currently in progress (as Bob/Charlie) */ private final Map _activeTests; @@ -196,7 +195,6 @@ class PeerTestManager { _log = context.logManager().getLog(PeerTestManager.class); _activeTests = new ConcurrentHashMap(); _recentTests = new LinkedBlockingQueue(); - _packetBuilder = transport.getBuilder(); _packetBuilder2 = transport.getBuilder2(); _throttle = new IPThrottler(MAX_PER_IP, THROTTLE_CLEAN_TIME); _context.statManager().createRateStat("udp.statusKnownCharlie", "How often the bob we pick passes us to a charlie we already have a session with?", "udp", UDPTransport.RATES); diff --git a/router/java/src/net/i2p/router/transport/udp/PeerTestState.java b/router/java/src/net/i2p/router/transport/udp/PeerTestState.java index d9b93177c..be38c0738 100644 --- a/router/java/src/net/i2p/router/transport/udp/PeerTestState.java +++ b/router/java/src/net/i2p/router/transport/udp/PeerTestState.java @@ -176,9 +176,6 @@ class PeerTestState { public SessionKey getCharlieIntroKey() { return _charlieIntroKey; } public void setCharlieIntroKey(SessionKey key) { _charlieIntroKey = key; } - public SessionKey getBobCipherKey() { return _bob.getCurrentCipherKey(); } - public SessionKey getBobMACKey() { return _bob.getCurrentMACKey(); } - /** when did this test begin? */ public long getBeginTime() { return _beginTime; } diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java index 90556119a..b18a52185 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -93,7 +93,6 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority private final IntroductionManager _introManager; private final ExpirePeerEvent _expireEvent; private final PeerTestEvent _testEvent; - private final PacketBuilder _packetBuilder; private Status _reachabilityStatus; private Status _reachabilityStatusPending; // only for logging, to be removed @@ -407,8 +406,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority _cachedBid[i] = new SharedBid(BID_VALUES[i]); } - _packetBuilder = (dh != null) ? new PacketBuilder(_context, this) : null; - _packetBuilder2 = (xdh != null) ? new PacketBuilder2(_context, this) : null; + _packetBuilder2 = new PacketBuilder2(_context, this); _fragments = new OutboundMessageFragments(_context, this, _activeThrottle); _inboundFragments = new InboundMessageFragments(_context, _fragments, this); //if (SHOULD_FLOOD_PEERS) @@ -2397,18 +2395,11 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority */ void sendDestroy(PeerState peer, int reasonCode) { UDPPacket pkt; - if (peer.getVersion() == 1) { - // peer must be fully established - if (peer.getCurrentCipherKey() == null) - return; - pkt = _packetBuilder.buildSessionDestroyPacket(peer); - } else { try { pkt = _packetBuilder2.buildSessionDestroyPacket(reasonCode, (PeerState2) peer); } catch (IOException ioe) { return; } - } if (_log.shouldLog(Log.DEBUG)) _log.debug("Sending destroy to : " + peer); send(pkt); @@ -3749,14 +3740,6 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority return _hmac; } - /** - * @return the PacketBuilder, or null if SSU1 disabled - * @since 0.9.52 - */ - PacketBuilder getBuilder() { - return _packetBuilder; - } - /** * @return null if not configured for SSU2 * @since 0.9.54 @@ -3891,15 +3874,11 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority // or else session will stay open forever? //peer.setLastSendTime(now); UDPPacket ping; - if (peer.getVersion() == 2) { try { ping = _packetBuilder2.buildPing((PeerState2) peer); } catch (IOException ioe) { continue; } - } else { - ping = _packetBuilder.buildPing(peer); - } send(ping); peer.setLastPingTime(now); // If external port is different, it may be changing the port for every diff --git a/router/java/test/junit/net/i2p/router/transport/udp/UDPEndpointTestStandalone.java b/router/java/test/junit/net/i2p/router/transport/udp/UDPEndpointTestStandalone.java index a57da24f5..2bb5a7967 100644 --- a/router/java/test/junit/net/i2p/router/transport/udp/UDPEndpointTestStandalone.java +++ b/router/java/test/junit/net/i2p/router/transport/udp/UDPEndpointTestStandalone.java @@ -95,6 +95,9 @@ public class UDPEndpointTestStandalone { _endpoint = peer; } public void run() { + System.out.println("rewrite me for SSU2"); + throw new UnsupportedOperationException("rewrite me for SSU2"); +/* while (!_beginTest) { try { Thread.sleep(2000); } catch (InterruptedException ie) {} } @@ -149,6 +152,7 @@ public class UDPEndpointTestStandalone { _log.error("Test failed, " + _sentNotReceived.size() + " not received"); System.exit(1); } +*/ } }