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 92173a10d52b64661f21118ce94a3ba3ced6f8b7..7fba352139cf5737396ed1dc169ea6e51f38efb4 100644 --- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java +++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import net.i2p.data.Base64; import net.i2p.data.Hash; import net.i2p.data.router.RouterAddress; import net.i2p.data.router.RouterIdentity; @@ -382,7 +383,17 @@ class EstablishmentManager { } } else { // must have a valid session key - byte[] keyBytes = addr.getIntroKey(); + byte[] keyBytes; + int version = _transport.getSSUVersion(ra); + if (version == 1) { + keyBytes = addr.getIntroKey(); + } else { + String siv = ra.getOption("i"); + if (siv != null) + keyBytes = Base64.decode(siv); + else + keyBytes = null; + } if (keyBytes == null) { _transport.markUnreachable(toHash); _transport.failed(msg, "Peer has no key, cannot establish"); @@ -403,7 +414,6 @@ class EstablishmentManager { // don't ask if they are indirect boolean requestIntroduction = allowExtendedOptions && !isIndirect && _transport.introducersMaybeRequired(TransportUtil.isIPv6(ra)); - int version = _transport.getSSUVersion(ra); if (version == 1) { state = new OutboundEstablishState(_context, maybeTo, to, toIdentity, allowExtendedOptions, @@ -751,7 +761,7 @@ class EstablishmentManager { */ void receiveRetry(OutboundEstablishState2 state, UDPPacket packet) { try { - state.receiveSessionCreated(packet); + state.receiveRetry(packet); } catch (GeneralSecurityException gse) { if (_log.shouldWarn()) _log.warn("Corrupt Retry from: " + state, gse); @@ -760,7 +770,7 @@ class EstablishmentManager { } notifyActivity(); if (_log.shouldLog(Log.DEBUG)) - _log.debug("Receive retry from: " + state); + _log.debug("Receive retry with token " + state.getToken() + " from: " + state); } /** @@ -1108,15 +1118,20 @@ class EstablishmentManager { public static final long MAX_TAG_VALUE = 0xFFFFFFFFl; /** - * This may be called more than once + * This handles both initial send and retransmission of Session Created, + * and, for SSU2, send of Retry. + * Retry is never retransmnitted. + * + * This may be called more than once. + * + * Caller must synch on state. */ private void sendCreated(InboundEstablishState state) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Send created to: " + state); - int version = state.getVersion(); UDPPacket pkt; if (version == 1) { + if (_log.shouldDebug()) + _log.debug("Send created to: " + state); try { state.generateSessionKey(); } catch (DHSessionKeyBuilder.InvalidPublicParameterException ippe) { @@ -1130,13 +1145,27 @@ class EstablishmentManager { _transport.getExternalPort(state.getSentIP().length == 16), _transport.getIntroKey()); } else { - // if already sent, get from the state to retx InboundEstablishState2 state2 = (InboundEstablishState2) state; InboundEstablishState.InboundState istate = state2.getState(); - if (istate == IB_STATE_CREATED_SENT) + if (istate == IB_STATE_CREATED_SENT) { + if (_log.shouldDebug()) + _log.debug("Send created to: " + state); + // if already sent, get from the state to retx pkt = state2.getRetransmitSessionCreatedPacket(); - else - pkt = _builder2.buildSessionCreatedPacket((InboundEstablishState2) state); + } else if (istate == IB_STATE_REQUEST_RECEIVED) { + if (_log.shouldDebug()) + _log.debug("Send created to: " + state); + pkt = _builder2.buildSessionCreatedPacket(state2); + } else if (istate == IB_STATE_TOKEN_REQUEST_RECEIVED || + istate == IB_STATE_REQUEST_BAD_TOKEN_RECEIVED) { + if (_log.shouldDebug()) + _log.debug("Send retry to: " + state); + pkt = _builder2.buildRetryPacket(state2); + } else { + if (_log.shouldWarn()) + _log.warn("Unhandled state " + istate + " on " + state); + return; + } } if (pkt == null) { if (_log.shouldLog(Log.WARN)) @@ -1152,23 +1181,44 @@ class EstablishmentManager { } /** - * Caller should probably synch on outboundState + * This handles both initial send and retransmission of Session Request, + * and, for SSU2, initial send and retransmission of Token Request. + * + * This may be called more than once. + * + * Caller must synch on state. */ private void sendRequest(OutboundEstablishState state) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Send SessionRequest to: " + state); int version = state.getVersion(); UDPPacket packet; if (version == 1) { + if (_log.shouldDebug()) + _log.debug("Send Session Request to: " + state); packet = _builder.buildSessionRequestPacket(state); } else { - // if already sent, get from the state to retx OutboundEstablishState2 state2 = (OutboundEstablishState2) state; OutboundEstablishState.OutboundState ostate = state2.getState(); - if (ostate == OB_STATE_REQUEST_SENT) + if (ostate == OB_STATE_REQUEST_SENT || + ostate == OB_STATE_REQUEST_SENT_NEW_TOKEN) { + if (_log.shouldDebug()) + _log.debug("Send Session Request to: " + state); + // if already sent, get from the state to retx packet = state2.getRetransmitSessionRequestPacket(); - else + } else if (ostate == OB_STATE_NEEDS_TOKEN || + ostate == OB_STATE_TOKEN_REQUEST_SENT) { + if (_log.shouldDebug()) + _log.debug("Send Token Request to: " + state); + packet = _builder2.buildTokenRequestPacket(state2); + } else if (ostate == OB_STATE_UNKNOWN || + ostate == OB_STATE_RETRY_RECEIVED) { + if (_log.shouldDebug()) + _log.debug("Send Session Request to: " + state); packet = _builder2.buildSessionRequestPacket(state2); + } else { + if (_log.shouldWarn()) + _log.warn("Unhandled state " + ostate + " on " + state); + return; + } } if (packet != null) { _transport.send(packet); @@ -1315,7 +1365,8 @@ class EstablishmentManager { * 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. - * Caller should probably synch on state. + * + * Caller must synch on state. */ private void sendConfirmation(OutboundEstablishState state) { boolean valid = state.validateSessionCreated(); @@ -1379,9 +1430,13 @@ class EstablishmentManager { * ack to the SessionConfirmed - otherwise we haven't generated the keys. * Caller should probably synch on state. * + * SSU1 only. + * * @since 0.9.2 */ private void sendDestroy(OutboundEstablishState state) { + if (state.getVersion() > 1) + return; UDPPacket packet = _builder.buildSessionDestroyPacket(state); if (packet != null) { if (_log.shouldLog(Log.DEBUG)) @@ -1397,9 +1452,13 @@ class EstablishmentManager { * Otherwise we haven't generated the keys. * Caller should probably synch on state. * + * SSU1 only. + * * @since 0.9.2 */ private void sendDestroy(InboundEstablishState state) { + if (state.getVersion() > 1) + return; UDPPacket packet = _builder.buildSessionDestroyPacket(state); if (packet != null) { if (_log.shouldLog(Log.DEBUG)) @@ -1463,8 +1522,11 @@ class EstablishmentManager { //if (_log.shouldLog(Log.DEBUG)) // _log.debug("Processing for inbound: " + inboundState); synchronized (inboundState) { - switch (inboundState.getState()) { + InboundEstablishState.InboundState istate = inboundState.getState(); + switch (istate) { case IB_STATE_REQUEST_RECEIVED: + case IB_STATE_TOKEN_REQUEST_RECEIVED: // SSU2 + case IB_STATE_REQUEST_BAD_TOKEN_RECEIVED: // SSU2 if (expired) processExpired(inboundState); else @@ -1473,11 +1535,18 @@ class EstablishmentManager { case IB_STATE_CREATED_SENT: // fallthrough case IB_STATE_CONFIRMED_PARTIALLY: + case IB_STATE_RETRY_SENT: // SSU2 if (expired) { sendDestroy(inboundState); processExpired(inboundState); } else if (inboundState.getNextSendTime() <= now) { - sendCreated(inboundState); + if (istate == IB_STATE_RETRY_SENT) { + // Retry is never retransmitted + inboundState.fail(); + processExpired(inboundState); + } else { + sendCreated(inboundState); + } } break; @@ -1510,6 +1579,12 @@ class EstablishmentManager { // Can't happen, always call receiveSessionRequest() before putting in map if (_log.shouldLog(Log.ERROR)) _log.error("hrm, state is unknown for " + inboundState); + break; + + default: + if (_log.shouldWarn()) + _log.warn("Unhandled state on " + inboundState); + break; } } @@ -1585,6 +1660,7 @@ class EstablishmentManager { switch (outboundState.getState()) { case OB_STATE_UNKNOWN: // fall thru case OB_STATE_INTRODUCED: + case OB_STATE_NEEDS_TOKEN: // SSU2 only if (expired) processExpired(outboundState); else @@ -1592,6 +1668,9 @@ class EstablishmentManager { break; case OB_STATE_REQUEST_SENT: + case OB_STATE_TOKEN_REQUEST_SENT: // SSU2 only + case OB_STATE_RETRY_RECEIVED: // SSU2 only + case OB_STATE_REQUEST_SENT_NEW_TOKEN: // SSU2 only // no response yet (or it was invalid), lets retry long rtime = outboundState.getRequestSentTime(); if (expired || (rtime > 0 && rtime + OB_MESSAGE_TIMEOUT <= now)) @@ -1635,6 +1714,11 @@ class EstablishmentManager { case OB_STATE_VALIDATION_FAILED: processExpired(outboundState); break; + + default: + if (_log.shouldWarn()) + _log.warn("Unhandled state on " + outboundState); + break; } } @@ -1695,6 +1779,7 @@ class EstablishmentManager { * @since 0.9.2 */ private void processExpired(InboundEstablishState inboundState) { + _inboundStates.remove(inboundState.getRemoteHostId()); OutNetMessage msg; while ((msg = inboundState.getNextQueuedMessage()) != null) { _transport.failed(msg, "Expired during failed establish"); @@ -1794,6 +1879,8 @@ class EstablishmentManager { doPass(); } catch (RuntimeException re) { _log.log(Log.CRIT, "Error in the establisher", re); + // don't loop too fast + try { Thread.sleep(1000); } catch (InterruptedException ie) {} } } _inboundStates.clear(); diff --git a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java index a60b673e11a61067ba221de851133439ea6cbfbf..a45099c6dee0594c9c0a1ed2d2da2c9f905247bd 100644 --- a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java +++ b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java @@ -32,8 +32,8 @@ class InboundEstablishState { protected final Log _log; // SessionRequest message private byte _receivedX[]; - private byte _bobIP[]; - private final int _bobPort; + protected byte _bobIP[]; + protected final int _bobPort; private final DHSessionKeyBuilder _keyBuilder; // SessionCreated message private byte _sentY[]; diff --git a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java index cc49bb57723daa03798a605f9d37505ab2d75879..d0687eee6402806a31c2e286e33abca4ce7a1969 100644 --- a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java +++ b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java @@ -77,9 +77,6 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa //_sendHeaderEncryptKey2 set below //_rcvHeaderEncryptKey2 set below _introductionRequested = false; // todo - //_bobIP = TODO - //if (_log.shouldLog(Log.DEBUG)) - // _log.debug("Receive sessionRequest, BobIP = " + Addresses.toString(_bobIP)); int off = pkt.getOffset(); int len = pkt.getLength(); byte data[] = pkt.getData(); @@ -107,12 +104,18 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa } else if (type == SESSION_REQUEST_FLAG_BYTE && (token == 0 || (ENFORCE_TOKEN && !_transport.getEstablisher().isInboundTokenValid(_remoteHostId, token)))) { + if (_log.shouldInfo()) + _log.info("Invalid token " + token + " in session request from: " + _aliceSocketAddress); _currentState = InboundState.IB_STATE_REQUEST_BAD_TOKEN_RECEIVED; _sendHeaderEncryptKey2 = introKey; + // Generate token for the retry. + // We do NOT register it with the EstablishmentManager, it must be used immediately. do { token = ctx.random().nextLong(); } while (token == 0); _token = token; + // do NOT bother to init the handshake state and decrypt the payload + _timeReceived = _establishBegin; } else { // fast MSB check for key < 2^255 if ((data[off + LONG_HEADER_SIZE + KEY_LEN - 1] & 0x80) != 0) @@ -251,7 +254,11 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa } public void gotAddress(byte[] ip, int port) { - throw new IllegalStateException("Address in Handshake"); + if (_log.shouldDebug()) + _log.debug("Got Address: " + Addresses.toString(ip, port)); + _bobIP = ip; + // final, see super + //_bobPort = port; } public void gotIntroKey(byte[] key) { @@ -356,8 +363,7 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa } _createdSentCount++; _nextSend = _lastSend + delay; - if ( (_currentState == InboundState.IB_STATE_UNKNOWN) || (_currentState == InboundState.IB_STATE_REQUEST_RECEIVED) ) - _currentState = InboundState.IB_STATE_CREATED_SENT; + _currentState = InboundState.IB_STATE_CREATED_SENT; } @@ -368,6 +374,9 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa throw new IllegalStateException("Bad state for Retry Sent: " + _currentState); _currentState = InboundState.IB_STATE_RETRY_SENT; _lastSend = _context.clock().now(); + // Won't really be transmitted, they have 3 sec to respond or + // EstablishmentManager.handleInbound() will fail the connection + _nextSend = _lastSend + RETRANSMIT_DELAY; } /** @@ -539,17 +548,7 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa byte data[] = pkt.getData(); int off = pkt.getOffset(); System.arraycopy(_sessCrForReTX, 0, data, off, _sessCrForReTX.length); - InetAddress to; - try { - to = InetAddress.getByAddress(_aliceIP); - } catch (UnknownHostException uhe) { - if (_log.shouldLog(Log.ERROR)) - _log.error("How did we think this was a valid IP? " + _remoteHostId); - packet.release(); - return null; - } - pkt.setAddress(to); - pkt.setPort(_alicePort); + pkt.setSocketAddress(_aliceSocketAddress); packet.setMessageType(PacketBuilder2.TYPE_CONF); packet.setPriority(PacketBuilder2.PRIORITY_HIGH); createdPacketSent(); diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java index b77fbff12915bcb7c29389938e0ba5b839c5780a..5f4b1730b7beeabb66376e8937da81162db60f5d 100644 --- a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java +++ b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java @@ -35,8 +35,8 @@ class OutboundEstablishState { private DHSessionKeyBuilder _keyBuilder; // SessionCreated message private byte _receivedY[]; - private byte _aliceIP[]; - private int _alicePort; + protected byte _aliceIP[]; + protected int _alicePort; private long _receivedRelayTag; private long _receivedSignedOnTime; private SessionKey _sessionKey; @@ -94,6 +94,11 @@ class OutboundEstablishState { /** SessionConfirmed failed validation */ OB_STATE_VALIDATION_FAILED, + /** + * SSU2: We don't have a token + * @since 0.9.54 + */ + OB_STATE_NEEDS_TOKEN, /** * SSU2: We have sent a token request * @since 0.9.54 diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java index aa2d0fc492b937e25c809baad12857eb17a2e7dc..ea2ecaf48a0925fe4768d2927f1c2550dc0cc97d 100644 --- a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java +++ b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java @@ -123,6 +123,8 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl _routerAddress = ra; if (_token != 0) createNewState(ra); + else + _currentState = OutboundState.OB_STATE_NEEDS_TOKEN; byte[] ik = introKey.getData(); _sendHeaderEncryptKey1 = ik; @@ -196,7 +198,9 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl public void gotAddress(byte[] ip, int port) { if (_log.shouldDebug()) - _log.debug("Got ADDRESS block: " + Addresses.toString(ip, port)); + _log.debug("Got Address: " + Addresses.toString(ip, port)); + _aliceIP = ip; + _alicePort = port; } public void gotIntroKey(byte[] key) { @@ -245,8 +249,15 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl // end payload callbacks ///////////////////////////////////////////////////////// - // SSU 1 unsupported things + // SSU 1 overrides + @Override + public synchronized boolean validateSessionCreated() { + // All validation is in receiveSessionCreated() + boolean rv = _currentState == OutboundState.OB_STATE_CREATED_RECEIVED || + _currentState == OutboundState.OB_STATE_CONFIRMED_COMPLETELY; + return rv; + } // SSU 2 things @@ -268,6 +279,9 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl public byte[] getSendHeaderEncryptKey1() { return _sendHeaderEncryptKey1; } public byte[] getRcvHeaderEncryptKey1() { return _rcvHeaderEncryptKey1; } public byte[] getSendHeaderEncryptKey2() { return _sendHeaderEncryptKey2; } + /** + * @return null before Session Request is sent (i.e. we sent a Token Request first) + */ public byte[] getRcvHeaderEncryptKey2() { return _rcvHeaderEncryptKey2; } public byte[] getRcvRetryHeaderEncryptKey2() { return _rcvRetryHeaderEncryptKey2; } public InetSocketAddress getSentAddress() { return _bobSocketAddress; } @@ -278,9 +292,22 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl public synchronized void receiveRetry(UDPPacket packet) throws GeneralSecurityException { ////// TODO state check DatagramPacket pkt = packet.getPacket(); + SocketAddress from = pkt.getSocketAddress(); + if (!from.equals(_bobSocketAddress)) + throw new GeneralSecurityException("Address mismatch: req: " + _bobSocketAddress + " conf: " + from); int off = pkt.getOffset(); int len = pkt.getLength(); byte data[] = pkt.getData(); + long rid = DataHelper.fromLong8(data, off); + if (rid != _rcvConnID) + throw new GeneralSecurityException("Conn ID mismatch: 1: " + _rcvConnID + " 2: " + rid); + long sid = DataHelper.fromLong8(data, off + SRC_CONN_ID_OFFSET); + if (sid != _sendConnID) + throw new GeneralSecurityException("Conn ID mismatch: 1: " + _sendConnID + " 2: " + sid); + long token = DataHelper.fromLong8(data, off + TOKEN_OFFSET); + if (token == 0) + throw new GeneralSecurityException("Bad token 0 in retry"); + _token = token; _timeReceived = 0; try { // decrypt in-place @@ -302,7 +329,8 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl if (skew > MAX_SKEW || skew < 0 - MAX_SKEW) throw new GeneralSecurityException("Skew exceeded in Session/Token Request: " + skew); createNewState(_routerAddress); - ////// TODO state change + _currentState = OutboundState.OB_STATE_RETRY_RECEIVED; + packetReceived(); } public synchronized void receiveSessionCreated(UDPPacket packet) throws GeneralSecurityException { @@ -320,6 +348,13 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl int off = pkt.getOffset(); int len = pkt.getLength(); byte data[] = pkt.getData(); + long rid = DataHelper.fromLong8(data, off); + if (rid != _rcvConnID) + throw new GeneralSecurityException("Conn ID mismatch: 1: " + _rcvConnID + " 2: " + rid); + long sid = DataHelper.fromLong8(data, off + SRC_CONN_ID_OFFSET); + if (sid != _sendConnID) + throw new GeneralSecurityException("Conn ID mismatch: 1: " + _sendConnID + " 2: " + sid); + _handshakeState.mixHash(data, off, LONG_HEADER_SIZE); if (_log.shouldDebug()) _log.debug("State after mixHash 2: " + _handshakeState); @@ -344,11 +379,7 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl _sessReqForReTX = null; _sendHeaderEncryptKey2 = SSU2Util.hkdf(_context, _handshakeState.getChainingKey(), "SessionConfirmed"); - if (_currentState == OutboundState.OB_STATE_UNKNOWN || - _currentState == OutboundState.OB_STATE_REQUEST_SENT || - _currentState == OutboundState.OB_STATE_INTRODUCED || - _currentState == OutboundState.OB_STATE_PENDING_INTRO) - _currentState = OutboundState.OB_STATE_CREATED_RECEIVED; + _currentState = OutboundState.OB_STATE_CREATED_RECEIVED; if (_requestSentCount == 1) { _rtt = (int) (_context.clock().now() - _requestSentTime); @@ -361,10 +392,10 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl * and save them for retransmission */ public synchronized void tokenRequestSent(DatagramPacket packet) { - if (_currentState == OutboundState.OB_STATE_UNKNOWN) + OutboundState old = _currentState; + requestSent(); + if (old == OutboundState.OB_STATE_NEEDS_TOKEN) _currentState = OutboundState.OB_STATE_TOKEN_REQUEST_SENT; - else if (_currentState == OutboundState.OB_STATE_RETRY_RECEIVED) - _currentState = OutboundState.OB_STATE_REQUEST_SENT_NEW_TOKEN; // don't bother saving for retx, just make a new one every time } @@ -383,7 +414,10 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl } if (_rcvHeaderEncryptKey2 == null) _rcvHeaderEncryptKey2 = SSU2Util.hkdf(_context, _handshakeState.getChainingKey(), "SessCreateHeader"); + OutboundState old = _currentState; requestSent(); + if (old == OutboundState.OB_STATE_RETRY_RECEIVED) + _currentState = OutboundState.OB_STATE_REQUEST_SENT_NEW_TOKEN; } /** @@ -461,17 +495,7 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl byte data[] = pkt.getData(); int off = pkt.getOffset(); System.arraycopy(_sessReqForReTX, 0, data, off, _sessReqForReTX.length); - InetAddress to; - try { - to = InetAddress.getByAddress(_bobIP); - } catch (UnknownHostException uhe) { - if (_log.shouldLog(Log.ERROR)) - _log.error("How did we think this was a valid IP? " + _remoteHostId); - packet.release(); - return null; - } - pkt.setAddress(to); - pkt.setPort(_bobPort); + pkt.setSocketAddress(_bobSocketAddress); packet.setMessageType(PacketBuilder2.TYPE_SREQ); packet.setPriority(PacketBuilder2.PRIORITY_HIGH); requestSent(); diff --git a/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java b/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java index 6be94e5a49caf8ed9ac12daa45164a7f6bf2aa58..4977c9d7d9917692e9eaea55b0597d5623629ec5 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java @@ -207,7 +207,8 @@ class PacketBuilder2 { off += sz; sizeWritten += sz; } - Block block = getPadding(sizeWritten, peer.getMTU()); + // FIXME + Block block = getPadding(sizeWritten, currentMTU); if (block != null) { blocks.add(block); int sz = block.getTotalLength(); @@ -216,13 +217,13 @@ class PacketBuilder2 { } SSU2Payload.writePayload(data, SHORT_HEADER_SIZE, blocks); pkt.setLength(off); - if (_log.shouldDebug()) - _log.debug("Packet " + pktNum + " before encryption:\n" + HexDump.dump(data, 0, off)); + //if (_log.shouldDebug()) + // _log.debug("Packet " + pktNum + " before encryption:\n" + HexDump.dump(data, 0, off)); encryptDataPacket(packet, peer.getSendCipher(), pktNum, peer.getSendHeaderEncryptKey1(), peer.getSendHeaderEncryptKey2()); setTo(packet, peer.getRemoteIPAddress(), peer.getRemotePort()); - if (_log.shouldDebug()) - _log.debug("Packet " + pktNum + " after encryption:\n" + HexDump.dump(data, 0, pkt.getLength())); + //if (_log.shouldDebug()) + // _log.debug("Packet " + pktNum + " after encryption:\n" + HexDump.dump(data, 0, pkt.getLength())); // FIXME ticket #2675 // the packet could have been built before the current mtu got lowered, so @@ -294,7 +295,6 @@ class PacketBuilder2 { pkt.setLength(LONG_HEADER_SIZE); byte[] introKey = state.getSendHeaderEncryptKey1(); encryptTokenRequest(packet, introKey, n, introKey, introKey); - state.requestSent(); pkt.setSocketAddress(state.getSentAddress()); packet.setMessageType(TYPE_SREQ); packet.setPriority(PRIORITY_HIGH); @@ -316,7 +316,6 @@ class PacketBuilder2 { pkt.setLength(LONG_HEADER_SIZE); byte[] introKey = state.getSendHeaderEncryptKey1(); encryptSessionRequest(packet, state.getHandshakeState(), introKey, introKey, state.needIntroduction()); - state.requestSent(); pkt.setSocketAddress(state.getSentAddress()); packet.setMessageType(TYPE_SREQ); packet.setPriority(PRIORITY_HIGH); @@ -451,7 +450,8 @@ class PacketBuilder2 { pkt.setLength(SHORT_HEADER_SIZE); SSU2Payload.RIBlock block = new SSU2Payload.RIBlock(ourInfo, 0, len, false, gzip, 0, numFragments); - encryptSessionConfirmed(packet, state.getHandshakeState(), state.getMTU(), + boolean isIPv6 = state.getSentIP().length == 16; + encryptSessionConfirmed(packet, state.getHandshakeState(), state.getMTU(), isIPv6, state.getSendHeaderEncryptKey1(), state.getSendHeaderEncryptKey2(), block, state.getNextToken()); pkt.setSocketAddress(state.getSentAddress()); packet.setMessageType(TYPE_CONF); @@ -875,22 +875,24 @@ class PacketBuilder2 { * @param packet containing only 16 byte header */ private void encryptSessionConfirmed(UDPPacket packet, HandshakeState state, int mtu, - byte[] hdrKey1, byte[] hdrKey2, + boolean isIPv6, byte[] hdrKey1, byte[] hdrKey2, SSU2Payload.RIBlock riblock, long token) { DatagramPacket pkt = packet.getPacket(); byte data[] = pkt.getData(); int off = pkt.getOffset(); + mtu -= UDP_HEADER_SIZE; + mtu -= isIPv6 ? IPV6_HEADER_SIZE : IP_HEADER_SIZE; try { List<Block> blocks = new ArrayList<Block>(3); int len = riblock.getTotalLength(); blocks.add(riblock); - if (token > 0) { - // TODO only if room + // only if room + if (token > 0 && mtu - len >= 15) { Block block = new SSU2Payload.NewTokenBlock(token, _context.clock().now() + EstablishmentManager.IB_TOKEN_EXPIRATION); len += block.getTotalLength(); blocks.add(block); } - Block block = getPadding(len, mtu - 80); + Block block = getPadding(len, mtu - (SHORT_HEADER_SIZE + KEY_LEN + MAC_LEN + MAC_LEN)); // 80 if (block != null) { len += block.getTotalLength(); blocks.add(block); @@ -904,6 +906,8 @@ class PacketBuilder2 { _log.debug("State after mixHash 3: " + state); state.writeMessage(data, off + SHORT_HEADER_SIZE, data, off + SHORT_HEADER_SIZE + KEY_LEN + MAC_LEN, len); pkt.setLength(pkt.getLength() + KEY_LEN + MAC_LEN + len + MAC_LEN); + if (_log.shouldDebug()) + _log.debug("Session confirmed packet length is: " + pkt.getLength()); } catch (RuntimeException re) { if (!_log.shouldWarn()) _log.error("Bad msg 3 out", re); diff --git a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java index 0d2f02e92437f26b813fbf147311312ee4dcc703..084796181d086191d346924dbd32296f9046e290 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java @@ -211,7 +211,7 @@ class PacketHandler { handlePacket(_reader, packet); } catch (RuntimeException e) { if (_log.shouldLog(Log.ERROR)) - _log.error("Crazy error handling a packet: " + packet, e); + _log.error("Internal error handling " + packet, e); } // back to the cache with thee! @@ -779,6 +779,8 @@ class PacketHandler { */ private void receiveSSU2Packet(UDPPacket packet, PeerState2 state) { // header and body decryption is done by PeerState2 + // This bypasses InboundMessageStates completely. + // All handling of fragments and acks is done in PeerState2. state.receivePacket(packet); } @@ -830,8 +832,9 @@ class PacketHandler { // Session Request (after Retry) or Session Confirmed // or retransmitted Session Request or Token Rquest k2 = state.getRcvHeaderEncryptKey2(); - if (state.getState() == InboundEstablishState.InboundState.IB_STATE_RETRY_SENT) { - // Session Request + if (k2 == null) { + // Session Request after Retry + k2 = k1; header = SSU2Header.trialDecryptHandshakeHeader(packet, k1, k2); if (header == null || header.getType() != SSU2Util.SESSION_REQUEST_FLAG_BYTE || @@ -841,6 +844,13 @@ class PacketHandler { _log.warn("Failed decrypt Session Request after Retry: " + header); return false; } + if (header.getSrcConnID() != state.getSendConnID()) { + if (_log.shouldWarn()) + _log.warn("Bad Source Conn id " + header); + // TODO could be a retransmitted Session Request, + // tell establisher? + return false; + } type = SSU2Util.SESSION_REQUEST_FLAG_BYTE; } else { // Session Confirmed or retransmitted Session Request or Token Request @@ -862,13 +872,6 @@ class PacketHandler { _log.warn("Bad Dest Conn id " + header); return false; } - if (header.getSrcConnID() != state.getSendConnID()) { - if (_log.shouldWarn()) - _log.warn("Bad Source Conn id " + header); - // TODO could be a retransmitted Session Request, - // tell establisher? - return false; - } } // all good @@ -912,15 +915,21 @@ class PacketHandler { // decrypt header byte[] k1 = state.getRcvHeaderEncryptKey1(); byte[] k2 = state.getRcvHeaderEncryptKey2(); - SSU2Header.Header header = SSU2Header.trialDecryptHandshakeHeader(packet, k1, k2); - if (header != null) { - // dest conn ID decrypts the same for both Session Created - // and Retry, so we can bail out now if it doesn't match - if (header.getDestConnID() != state.getRcvConnID()) { - if (_log.shouldWarn()) - _log.warn("Bad Dest Conn id " + header); - return false; + SSU2Header.Header header; + if (k2 != null) { + header = SSU2Header.trialDecryptHandshakeHeader(packet, k1, k2); + if (header != null) { + // dest conn ID decrypts the same for both Session Created + // and Retry, so we can bail out now if it doesn't match + if (header.getDestConnID() != state.getRcvConnID()) { + if (_log.shouldWarn()) + _log.warn("Bad Dest Conn id " + header); + return false; + } } + } else { + // we have only sent a Token Request + header = null; } int type; if (header == null || 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 8c7f7b35970c98fb1d114390f09c98b073c05286..c863aa7b5511d630cda8004ea06de97253e34549 100644 --- a/router/java/src/net/i2p/router/transport/udp/PeerState2.java +++ b/router/java/src/net/i2p/router/transport/udp/PeerState2.java @@ -185,8 +185,8 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback byte[] data = dpacket.getData(); int off = dpacket.getOffset(); int len = dpacket.getLength(); - if (_log.shouldDebug()) - _log.debug("Packet before header decryption:\n" + HexDump.dump(data, off, len)); + //if (_log.shouldDebug()) + // _log.debug("Packet before header decryption:\n" + HexDump.dump(data, off, len)); try { if (len < MIN_DATA_LEN) { if (_log.shouldWarn()) @@ -199,11 +199,6 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback _log.warn("bad data header on " + this); return; } - if (header.getDestConnID() != _rcvConnID) { - if (_log.shouldWarn()) - _log.warn("bad Dest Conn id " + header.getDestConnID() + " on " + this); - return; - } if (header.getType() != DATA_FLAG_BYTE) { if (_log.shouldWarn()) _log.warn("bad data pkt type " + (header.getType() & 0xff) + " on " + this); @@ -217,16 +212,21 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback // we didn't know the session has disconnected yet. return; } + if (header.getDestConnID() != _rcvConnID) { + if (_log.shouldWarn()) + _log.warn("bad Dest Conn id " + header.getDestConnID() + " on " + this); + return; + } long n = header.getPacketNumber(); SSU2Header.acceptTrialDecrypt(packet, header); - if (_log.shouldDebug()) - _log.debug("Packet " + n + " after header decryption:\n" + HexDump.dump(data, off, len)); + //if (_log.shouldDebug()) + // _log.debug("Packet " + n + " after header decryption:\n" + HexDump.dump(data, off, len)); synchronized (_rcvCha) { _rcvCha.setNonce(n); // decrypt in-place _rcvCha.decryptWithAd(header.data, data, off + SHORT_HEADER_SIZE, data, off + SHORT_HEADER_SIZE, len - SHORT_HEADER_SIZE); - if (_log.shouldDebug()) - _log.debug("Packet " + n + " after full decryption:\n" + HexDump.dump(data, off, len - MAC_LEN)); + //if (_log.shouldDebug()) + // _log.debug("Packet " + n + " after full decryption:\n" + HexDump.dump(data, off, len - MAC_LEN)); if (_receivedMessages.set(n)) { if (_log.shouldWarn()) _log.warn("dup pkt rcvd " + n + " on " + this); @@ -244,8 +244,6 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback } catch (IndexOutOfBoundsException ioobe) { if (_log.shouldWarn()) _log.warn("Bad encrypted packet:\n" + HexDump.dump(data, off, len), ioobe); - } finally { - packet.release(); } } diff --git a/router/java/src/net/i2p/router/transport/udp/SSU2Header.java b/router/java/src/net/i2p/router/transport/udp/SSU2Header.java index d89836983a3a9fb4b219cefb2a63769d7b954044..67fee090fd2ceb476d71823a802f557afc902dbd 100644 --- a/router/java/src/net/i2p/router/transport/udp/SSU2Header.java +++ b/router/java/src/net/i2p/router/transport/udp/SSU2Header.java @@ -190,10 +190,12 @@ final class SSU2Header { public String toString() { if (data.length >= SESSION_HEADER_SIZE) { return "Handshake header destID " + getDestConnID() + " pkt num " + getPacketNumber() + " type " + getType() + + " version " + getVersion() + " netID " + getNetID() + " srcID " + getSrcConnID() + " token " + getToken() + " key " + Base64.encode(getEphemeralKey()); } if (data.length >= LONG_HEADER_SIZE) { return "Long header destID " + getDestConnID() + " pkt num " + getPacketNumber() + " type " + getType() + + " version " + getVersion() + " netID " + getNetID() + " srcID " + getSrcConnID() + " token " + getToken(); } return "Short header destID " + getDestConnID() + " pkt num " + getPacketNumber() + " type " + getType();