/** * We found a non-SYN packet that was queued in the syn queue, check to see if it has a home now, * else drop it ... */ private void reReceivePacket(Packet packet) { Connection con = _manager.getConnectionByOutboundId(packet.getReceiveStreamId()); if (con != null) { // Send it through the packet handler again if (_log.shouldLog(Log.WARN)) _log.warn("Found con for queued non-syn packet: " + packet); // false -> don't requeue, fixes a race where a SYN gets dropped // between here and PacketHandler, causing the packet to loop forever.... _manager.getPacketHandler().receivePacketDirect(packet, false); } else { // goodbye if (_log.shouldLog(Log.WARN)) _log.warn("Did not find con for queued non-syn packet, dropping: " + packet); packet.releasePayload(); } }
/** * This sends a reset back to the place this packet came from. If the packet has no 'optional * from' or valid signature, this does nothing. This is not associated with a connection, so no * con stats are updated. */ private void sendReset(Packet packet) { Destination from = packet.getOptionalFrom(); if (from == null) return; boolean ok = packet.verifySignature(_context, from, null); if (!ok) { if (_log.shouldLog(Log.WARN)) _log.warn("Can't send reset after recv spoofed packet: " + packet); return; } PacketLocal reply = new PacketLocal(_context, from); reply.setFlag(Packet.FLAG_RESET); reply.setFlag(Packet.FLAG_SIGNATURE_INCLUDED); reply.setSendStreamId(packet.getReceiveStreamId()); reply.setReceiveStreamId(packet.getSendStreamId()); reply.setOptionalFrom(_manager.getSession().getMyDestination()); // this just sends the packet - no retries or whatnot _manager.getPacketQueue().enqueue(reply); }
private void sendReset(Packet packet) { boolean ok = packet.verifySignature(_context, packet.getOptionalFrom(), null); if (!ok) { if (_log.shouldLog(Log.WARN)) _log.warn("Received a spoofed SYN packet: they said they were " + packet.getOptionalFrom()); return; } PacketLocal reply = new PacketLocal(_context, packet.getOptionalFrom()); reply.setFlag(Packet.FLAG_RESET); reply.setFlag(Packet.FLAG_SIGNATURE_INCLUDED); reply.setAckThrough(packet.getSequenceNum()); reply.setSendStreamId(packet.getReceiveStreamId()); reply.setReceiveStreamId(0); reply.setOptionalFrom(_manager.getSession().getMyDestination()); if (_log.shouldLog(Log.DEBUG)) _log.debug("Sending RST: " + reply + " because of " + packet); // this just sends the packet - no retries or whatnot _manager.getPacketQueue().enqueue(reply); }
/** * We found a non-SYN packet that was queued in the syn queue, check to see if it has a home now, * else drop it ... */ private void reReceivePacket(Packet packet) { Connection con = _manager.getConnectionByOutboundId(packet.getReceiveStreamId()); if (con != null) { // Send it through the packet handler again if (_log.shouldLog(Log.WARN)) _log.warn("Found con for queued non-syn packet: " + packet); // false -> don't requeue, fixes a race where a SYN gets dropped // between here and PacketHandler, causing the packet to loop forever.... _manager.getPacketHandler().receivePacketDirect(packet, false); } else { // log it here, just before we kill it - dest will be unknown if (I2PSocketManagerFull.pcapWriter != null && _context.getBooleanProperty(I2PSocketManagerFull.PROP_PCAP)) packet.logTCPDump(null); // goodbye if (_log.shouldLog(Log.WARN)) _log.warn("Did not find con for queued non-syn packet, dropping: " + packet); packet.releasePayload(); } }
private void receivePong(Packet packet) { _manager.receivePong(packet.getReceiveStreamId()); }
private void receiveUnknownCon(Packet packet, long sendId, boolean queueIfNoConn) { if (packet.isFlagSet(Packet.FLAG_ECHO)) { if (packet.getSendStreamId() > 0) { if (_manager.answerPings()) receivePing(packet); else if (_log.shouldLog(Log.WARN)) _log.warn("Dropping Echo packet on unknown con: " + packet); } else if (packet.getReceiveStreamId() > 0) { receivePong(packet); } else { if (_log.shouldLog(Log.WARN)) _log.warn("Echo packet received with no stream IDs: " + packet); } packet.releasePayload(); } else { if (_log.shouldLog(Log.WARN) && !packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) _log.warn("Packet received on an unknown stream (and not an ECHO or SYN): " + packet); if (sendId <= 0) { Connection con = _manager.getConnectionByOutboundId(packet.getReceiveStreamId()); if (con != null) { if ((con.getHighestAckedThrough() <= 5) && (packet.getSequenceNum() <= 5)) { if (_log.shouldLog(Log.WARN)) _log.warn( "Received additional packet w/o SendStreamID after the syn on " + con + ": " + packet); receiveKnownCon(con, packet); return; } else { if (_log.shouldLog(Log.WARN)) _log.warn( "hrmph, received while ack of syn was in flight on " + con + ": " + packet + " acked: " + con.getAckedPackets()); // allow unlimited packets without a SendStreamID for now receiveKnownCon(con, packet); return; } } } if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) { // logTCPDump() will be called in ConnectionManager.receiveConnection(), // which is called by ConnectionHandler.receiveNewSyn(), // after we have a new conn, which makes the logging better. _manager.getConnectionHandler().receiveNewSyn(packet); } else if (queueIfNoConn) { // don't call logTCPDump() here, wait for it to find a conn // We can get here on the 2nd+ packet if the 1st (SYN) packet // is still on the _synQueue in the ConnectionHandler, and // ConnectionManager.receiveConnection() hasn't run yet to put // the StreamID on the getConnectionByOutboundId list. // Then the 2nd packet gets discarded and has to be retransmitted. // // We fix this by putting this packet on the syn queue too! // Then ConnectionHandler.accept() will check the connection list // and call receivePacket() above instead of receiveConnection(). if (_log.shouldLog(Log.WARN)) { _log.warn("Packet belongs to no other cons, putting on the syn queue: " + packet); } if (_log.shouldLog(Log.DEBUG)) { StringBuilder buf = new StringBuilder(128); for (Connection con : _manager.listConnections()) { buf.append(con.toString()).append(" "); } _log.debug( "connections: " + buf.toString() + " sendId: " + (sendId > 0 ? Packet.toId(sendId) : " unknown")); } // packet.releasePayload(); _manager.getConnectionHandler().receiveNewSyn(packet); } else { // log it here, just before we kill it - dest will be unknown if (I2PSocketManagerFull.pcapWriter != null && _context.getBooleanProperty(I2PSocketManagerFull.PROP_PCAP)) packet.logTCPDump(null); // don't queue again (infinite loop!) sendReset(packet); packet.releasePayload(); } } }
private void receiveKnownCon(Connection con, Packet packet) { // is this ok here or does it need to be below each packetHandler().receivePacket() ? if (I2PSocketManagerFull.pcapWriter != null && _context.getBooleanProperty(I2PSocketManagerFull.PROP_PCAP)) packet.logTCPDump(con); if (packet.isFlagSet(Packet.FLAG_ECHO)) { if (packet.getSendStreamId() > 0) { if (con.getOptions().getAnswerPings()) receivePing(packet); else if (_log.shouldLog(Log.WARN)) _log.warn("Dropping Echo packet on existing con: " + packet); } else if (packet.getReceiveStreamId() > 0) { receivePong(packet); } else { if (_log.shouldLog(Log.WARN)) _log.warn("Echo packet received with no stream IDs: " + packet); } packet.releasePayload(); return; } // the packet is pointed at a stream ID we're receiving on if (isValidMatch(con.getSendStreamId(), packet.getReceiveStreamId())) { // the packet's receive stream ID also matches what we expect // if (_log.shouldLog(Log.DEBUG)) // _log.debug("receive valid: " + packet); try { con.getPacketHandler().receivePacket(packet, con); } catch (I2PException ie) { if (_log.shouldLog(Log.WARN)) _log.warn("Received forged packet for " + con, ie); } } else { if (packet.isFlagSet(Packet.FLAG_RESET)) { // refused if (_log.shouldLog(Log.DEBUG)) _log.debug("receive reset: " + packet); try { con.getPacketHandler().receivePacket(packet, con); } catch (I2PException ie) { if (_log.shouldLog(Log.WARN)) _log.warn("Received forged reset for " + con, ie); } } else { if ((con.getSendStreamId() <= 0) || (con.getSendStreamId() == packet.getReceiveStreamId()) || (packet.getSequenceNum() <= ConnectionOptions.MIN_WINDOW_SIZE)) { // its in flight from the first batch long oldId = con.getSendStreamId(); if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) { if (oldId <= 0) { // con fully established, w00t con.setSendStreamId(packet.getReceiveStreamId()); } else if (oldId == packet.getReceiveStreamId()) { // ok, as expected... } else { if (_log.shouldLog(Log.WARN)) _log.warn("Received a syn with the wrong IDs, con=" + con + " packet=" + packet); sendReset(packet); packet.releasePayload(); return; } } try { con.getPacketHandler().receivePacket(packet, con); } catch (I2PException ie) { if (_log.shouldLog(Log.ERROR)) _log.error("Received forged packet for " + con + "/" + oldId + ": " + packet, ie); con.setSendStreamId(oldId); } } else if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) { if (_log.shouldLog(Log.WARN)) _log.warn("Receive a syn packet with the wrong IDs, sending reset: " + packet); sendReset(packet); packet.releasePayload(); } else { if (!con.getResetSent()) { // someone is sending us a packet on the wrong stream // It isn't a SYN so it isn't likely to have a FROM to send a reset back to if (_log.shouldLog(Log.ERROR)) { StringBuilder buf = new StringBuilder(512); buf.append("Received a packet on the wrong stream: "); buf.append(packet); buf.append("\nthis connection:\n"); buf.append(con); buf.append("\nall connections:"); for (Connection cur : _manager.listConnections()) { buf.append('\n').append(cur); } _log.error(buf.toString(), new Exception("Wrong stream")); } } packet.releasePayload(); } } } }
/** * Receive an incoming connection (built from a received SYN) Non-SYN packets with a zero * SendStreamID may also be queued here so that they don't get thrown away while the SYN packet * before it is queued. * * @param timeoutMs max amount of time to wait for a connection (if less than 1ms, wait * indefinitely) * @return connection received, or null if there was a timeout or the handler was shut down */ public Connection accept(long timeoutMs) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Accept(" + timeoutMs + ") called"); long expiration = timeoutMs + _context.clock().now(); while (true) { if ((timeoutMs > 0) && (expiration < _context.clock().now())) return null; if (!_active) { // fail all the ones we had queued up while (true) { Packet packet = _synQueue.poll(); // fails immediately if empty if (packet == null || packet.getOptionalDelay() == PoisonPacket.POISON_MAX_DELAY_REQUEST) break; sendReset(packet); } return null; } Packet syn = null; while (_active && syn == null) { if (_log.shouldLog(Log.DEBUG)) _log.debug( "Accept(" + timeoutMs + "): active=" + _active + " queue: " + _synQueue.size()); if (timeoutMs <= 0) { try { syn = _synQueue.take(); // waits forever } catch (InterruptedException ie) { } // { break;} } else { long remaining = expiration - _context.clock().now(); // (dont think this applies anymore for LinkedBlockingQueue) // BUGFIX // The specified amount of real time has elapsed, more or less. // If timeout is zero, however, then real time is not taken into consideration // and the thread simply waits until notified. if (remaining < 1) break; try { syn = _synQueue.poll(remaining, TimeUnit.MILLISECONDS); // waits the specified time max } catch (InterruptedException ie) { } break; } } if (syn != null) { if (syn.getOptionalDelay() == PoisonPacket.POISON_MAX_DELAY_REQUEST) return null; // deal with forged / invalid syn packets in _manager.receiveConnection() // Handle both SYN and non-SYN packets in the queue if (syn.isFlagSet(Packet.FLAG_SYNCHRONIZE)) { // We are single-threaded here, so this is // a good place to check for dup SYNs and drop them Destination from = syn.getOptionalFrom(); if (from == null) { if (_log.shouldLog(Log.WARN)) _log.warn("Dropping SYN packet with no FROM: " + syn); // drop it continue; } Connection oldcon = _manager.getConnectionByOutboundId(syn.getReceiveStreamId()); if (oldcon != null) { // His ID not guaranteed to be unique to us, but probably is... // only drop it on a destination match too if (from.equals(oldcon.getRemotePeer())) { if (_log.shouldLog(Log.WARN)) _log.warn("Dropping dup SYN: " + syn); continue; } } Connection con = _manager.receiveConnection(syn); if (con != null) return con; } else { reReceivePacket(syn); // ... and keep looping } } // keep looping... } }