diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java index a0f7bac5522406bd364e7b75dd46bb2073afacb1..275da2725c43cda35c4af865a791d65825ea09bb 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java @@ -195,7 +195,9 @@ class ConnectionHandler { _manager.getPacketHandler().receivePacketDirect(packet, false); } else { // log it here, just before we kill it - dest will be unknown - ((PacketLocal)packet).logTCPDump(true); + if (I2PSocketManagerFull.pcapWriter != null && + _context.getBooleanProperty(I2PSocketManagerFull.PROP_PCAP)) + packet.logTCPDump(null); // goodbye if (_log.shouldLog(Log.WARN)) diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java index adce3dc99cdfa846063e29be86bb9f6622665a3e..c058c74b4b76beb3894895b5c0f41b7c8f56562b 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java @@ -267,8 +267,9 @@ class ConnectionManager { con.setReceiveStreamId(receiveId); // finally, we know enough that we can log the packet with the conn filled in - ((PacketLocal)synPacket).setConnection(con); - ((PacketLocal)synPacket).logTCPDump(true); + if (I2PSocketManagerFull.pcapWriter != null && + _context.getBooleanProperty(I2PSocketManagerFull.PROP_PCAP)) + synPacket.logTCPDump(con); try { // This validates the packet, and sets the con's SendStreamID and RemotePeer con.getPacketHandler().receivePacket(synPacket, con); diff --git a/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketManagerFull.java b/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketManagerFull.java index ce6a65bc1e4a4a7a138cc180a1f7d5d0e97ad7a8..ec0cd3781494ad85b3112dc938ea9c7c04cf622f 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketManagerFull.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketManagerFull.java @@ -315,6 +315,8 @@ public class I2PSocketManagerFull implements I2PSocketManager { } catch (I2PSessionException ise) { _log.warn("Unable to destroy the session", ise); } + if (pcapWriter != null) + pcapWriter.flush(); } } @@ -351,7 +353,7 @@ public class I2PSocketManagerFull implements I2PSocketManager { private static final String PCAP_FILE = "streaming.pcap"; private static void debugInit(I2PAppContext ctx) { - if (!Boolean.valueOf(ctx.getProperty(PROP_PCAP)).booleanValue()) + if (!ctx.getBooleanProperty(PROP_PCAP)) return; synchronized(_pcapInitLock) { if (!_pcapInitialized) { diff --git a/apps/streaming/java/src/net/i2p/client/streaming/MessageHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/MessageHandler.java index 1f3dd66c421f0ebf8aa241cb1bf9983c958dd6a5..4aba07d31429ae246e7e0de160152d5a18bc70dc 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/MessageHandler.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/MessageHandler.java @@ -59,9 +59,7 @@ class MessageHandler implements I2PSessionMuxedListener { return; } if (data == null) return; - //Packet packet = new Packet(); - // for tcpdump - Packet packet = new PacketLocal(_context, null); + Packet packet = new Packet(); try { packet.readPacket(data, 0, data.length); packet.setRemotePort(fromPort); diff --git a/apps/streaming/java/src/net/i2p/client/streaming/Packet.java b/apps/streaming/java/src/net/i2p/client/streaming/Packet.java index a79f67cb751ec11b40d1ac28479d38be6cc33887..e589b6273a06d8a7f5a85d9d0ff86f6307f23978 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/Packet.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/Packet.java @@ -1,5 +1,6 @@ package net.i2p.client.streaming; +import java.io.IOException; import java.util.Arrays; import net.i2p.I2PAppContext; @@ -16,7 +17,8 @@ import net.i2p.util.Log; * This contains solely the data that goes out on the wire, * including the local and remote port which is embedded in * the I2CP overhead, not in the packet itself. - * For local state saved for outbound packets, see PacketLocal. + * This is the class used for inbound packets. + * For local state saved for outbound packets, see the PacketLocal extension. * * <p> * @@ -709,4 +711,14 @@ class Packet { if (isFlagSet(FLAG_SYNCHRONIZE)) buf.append(" SYN"); return buf.toString(); } + + /** Generate a pcap/tcpdump-compatible format, + * so we can use standard debugging tools. + */ + public void logTCPDump(Connection con) { + try { + I2PSocketManagerFull.pcapWriter.write(this, con); + } catch (IOException ioe) { + } + } } diff --git a/apps/streaming/java/src/net/i2p/client/streaming/PacketHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/PacketHandler.java index d54e253bb6fa40ca6d528b85460d251d5ba9e00d..6cdf3dc81aec4cee8bc669015d6cec840e810d8f 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/PacketHandler.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/PacketHandler.java @@ -131,8 +131,9 @@ class PacketHandler { private void receiveKnownCon(Connection con, Packet packet) { // is this ok here or does it need to be below each packetHandler().receivePacket() ? - ((PacketLocal)packet).setConnection(con); - ((PacketLocal)packet).logTCPDump(true); + 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()) @@ -318,7 +319,9 @@ class PacketHandler { _manager.getConnectionHandler().receiveNewSyn(packet); } else { // log it here, just before we kill it - dest will be unknown - ((PacketLocal)packet).logTCPDump(true); + if (I2PSocketManagerFull.pcapWriter != null && + _context.getBooleanProperty(I2PSocketManagerFull.PROP_PCAP)) + packet.logTCPDump(null); // don't queue again (infinite loop!) sendReset(packet); packet.releasePayload(); diff --git a/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java b/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java index b5df7236e60f7a1ada65fdf37b1c88840761c640..97666db6f47b73edfdfba6e37e4547db5b7099fb 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java @@ -10,6 +10,8 @@ import net.i2p.util.Log; import net.i2p.util.SimpleTimer2; /** + * This is the class used for outbound packets. + * * coordinate local attributes about a packet - send time, ack time, number of * retries, etc. */ @@ -143,8 +145,6 @@ class PacketLocal extends Packet implements MessageOutputStream.WriteStatus { /** @return null if not bound */ public Connection getConnection() { return _connection; } - /** used to set the rcvd conn after the fact for incoming syn replies */ - public void setConnection(Connection con) { _connection = con; } /** * Will force a fast restransmit on the 3rd call (FAST_RETRANSMIT_THRESHOLD) @@ -263,16 +263,11 @@ class PacketLocal extends Packet implements MessageOutputStream.WriteStatus { /** Generate a pcap/tcpdump-compatible format, * so we can use standard debugging tools. */ - public void logTCPDump(boolean isInbound) { - if (_log.shouldLog(Log.INFO)) - _log.info(toString()); - if (I2PSocketManagerFull.pcapWriter != null && - Boolean.valueOf(_context.getProperty(I2PSocketManagerFull.PROP_PCAP)).booleanValue()) { + public void logTCPDump() { try { - I2PSocketManagerFull.pcapWriter.write(this, isInbound); + I2PSocketManagerFull.pcapWriter.write(this); } catch (IOException ioe) { _log.warn("pcap write ioe: " + ioe); } - } } } diff --git a/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java b/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java index c415e8482797e0c148465158fe3275ff19c2f0d8..4764c9abfa7fa89c12a6c5b400c8168bb0bb997e 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java @@ -169,7 +169,9 @@ class PacketQueue { Connection c = packet.getConnection(); String suffix = (c != null ? "wsize " + c.getOptions().getWindowSize() + " rto " + c.getOptions().getRTO() : null); _connectionManager.getPacketHandler().displayPacket(packet, "SEND", suffix); - ((PacketLocal)packet).logTCPDump(false); + if (I2PSocketManagerFull.pcapWriter != null && + _context.getBooleanProperty(I2PSocketManagerFull.PROP_PCAP)) + ((PacketLocal)packet).logTCPDump(); } if ( (packet.getSequenceNum() == 0) && (!packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) ) { diff --git a/apps/streaming/java/src/net/i2p/client/streaming/PcapWriter.java b/apps/streaming/java/src/net/i2p/client/streaming/PcapWriter.java index e838ad03107d6b527bdda8a166a18f4c35af161a..3bc119d817671d4ad241d7cba9d0113f437ed514 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/PcapWriter.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/PcapWriter.java @@ -1,8 +1,10 @@ package net.i2p.client.streaming; +import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import net.i2p.I2PAppContext; import net.i2p.data.DataFormatException; @@ -72,8 +74,8 @@ public class PcapWriter { private static final byte OPTION_NACK = (byte) 0xac; - private FileOutputStream _fos; - private I2PAppContext _context; + private final OutputStream _fos; + private final I2PAppContext _context; public PcapWriter(I2PAppContext ctx, String file) throws IOException { _context = ctx; @@ -81,37 +83,52 @@ public class PcapWriter { //if (f.exists()) { // _fos = new FileOutputStream(f, true); //} else { - _fos = new FileOutputStream(f); + _fos = new BufferedOutputStream(new FileOutputStream(f), 64*1024); _fos.write(FILE_HEADER); //} } public void close() { - FileOutputStream fos = _fos; - if (fos != null) { try { - fos.close(); + _fos.close(); } catch (IOException ioe) {} - _fos = null; + } + + public void flush() { + try { + _fos.flush(); + } catch (IOException ioe) {} + } + + /** + * For outbound packets + */ + public void write(PacketLocal pkt) throws IOException { + try { + wrt(pkt, pkt.getConnection(), false); + } catch (DataFormatException dfe) { + dfe.printStackTrace(); + throw new IOException(dfe.toString()); } } - public void write(PacketLocal pkt, boolean isInbound) throws IOException { + /** + * For inbound packets + * @param con may be null + */ + public void write(Packet pkt, Connection con) throws IOException { try { - wrt(pkt, isInbound); + wrt(pkt, con, true); } catch (DataFormatException dfe) { dfe.printStackTrace(); throw new IOException(dfe.toString()); } - // remove me - _fos.flush(); } - private synchronized void wrt(PacketLocal pkt, boolean isInbound) throws IOException, DataFormatException { - FileOutputStream fos = _fos; - if (fos == null) - throw new IOException("Not open or already closed"); - Connection con = pkt.getConnection(); + /** + * @param con may be null + */ + private synchronized void wrt(Packet pkt, Connection con, boolean isInbound) throws IOException, DataFormatException { int includeLen = Math.min(MAX_PAYLOAD_BYTES, pkt.getPayloadSize()); // option block @@ -142,23 +159,23 @@ public class PcapWriter { // PCAP Header long now; if (isInbound) - now = pkt.getCreatedOn(); + now = _context.clock().now(); else - now = pkt.getLastSend(); - DataHelper.writeLong(fos, 4, now / 1000); - DataHelper.writeLong(fos, 4, 1000 * (now % 1000)); - DataHelper.writeLong(fos, 4, 54 + optLen + includeLen); // 14 MAC + 20 IP + 20 TCP - DataHelper.writeLong(fos, 4, 58 + optLen + pkt.getPayloadSize()); // 54 + MAC checksum + now = ((PacketLocal)pkt).getLastSend(); + DataHelper.writeLong(_fos, 4, now / 1000); + DataHelper.writeLong(_fos, 4, 1000 * (now % 1000)); + DataHelper.writeLong(_fos, 4, 54 + optLen + includeLen); // 14 MAC + 20 IP + 20 TCP + DataHelper.writeLong(_fos, 4, 58 + optLen + pkt.getPayloadSize()); // 54 + MAC checksum // MAC Header 14 bytes - fos.write(MAC_HEADER); + _fos.write(MAC_HEADER); // IP 20 bytes total // IP Header 12 bytes int length = 20 + 20 + optLen + pkt.getPayloadSize(); - fos.write(IP_HEADER_1); - DataHelper.writeLong(fos, 2, length); // total IP length - fos.write(IP_HEADER_2); + _fos.write(IP_HEADER_1); + DataHelper.writeLong(_fos, 2, length); // total IP length + _fos.write(IP_HEADER_2); // src and dst IP 8 bytes // make our side always start with 127.0.x.x @@ -199,17 +216,17 @@ public class PcapWriter { checksum = update(checksum, IP_HEADER_2); checksum = update(checksum, srcAddr, 4); checksum = update(checksum, dstAddr, 4); - DataHelper.writeLong(fos, 2, checksum ^ 0xffff); + DataHelper.writeLong(_fos, 2, checksum ^ 0xffff); // IPs - fos.write(srcAddr, 0, 4); - fos.write(dstAddr, 0, 4); + _fos.write(srcAddr, 0, 4); + _fos.write(dstAddr, 0, 4); // TCP header 20 bytes total // src and dst port 4 bytes // the rcv ID is the source, and the send ID is the dest. - DataHelper.writeLong(fos, 2, pkt.getReceiveStreamId() & 0xffff); - DataHelper.writeLong(fos, 2, pkt.getSendStreamId() & 0xffff); + DataHelper.writeLong(_fos, 2, pkt.getReceiveStreamId() & 0xffff); + DataHelper.writeLong(_fos, 2, pkt.getSendStreamId() & 0xffff); // seq and acks 8 bytes long seq; @@ -226,8 +243,8 @@ public class PcapWriter { else acked = getLowestAckedThrough(pkt, con); } - DataHelper.writeLong(fos, 4, pkt.getSequenceNum()); - DataHelper.writeLong(fos, 4, acked); + DataHelper.writeLong(_fos, 4, pkt.getSequenceNum()); + DataHelper.writeLong(_fos, 4, acked); // offset and flags 2 bytes int flags = 0; @@ -241,8 +258,8 @@ public class PcapWriter { flags |= 0x10; // offset byte int osb = (5 + (optLen / 4)) << 4; - DataHelper.writeLong(fos, 1, osb); // 5 + optLen/4 32-byte words - DataHelper.writeLong(fos, 1, flags); + DataHelper.writeLong(_fos, 1, osb); // 5 + optLen/4 32-byte words + DataHelper.writeLong(_fos, 1, flags); // window size 2 bytes long window = ConnectionOptions.INITIAL_WINDOW_SIZE; @@ -268,18 +285,20 @@ public class PcapWriter { // for now we don't spoof window scaling if (window > 65535) window = 65535; - DataHelper.writeLong(fos, 2, window); + DataHelper.writeLong(_fos, 2, window); // checksum and urgent pointer 4 bytes - DataHelper.writeLong(fos, 4, 0); + DataHelper.writeLong(_fos, 4, 0); // TCP option block if (optLen > 0) - fos.write(options, 0, optLen); + _fos.write(options, 0, optLen); // some data if (includeLen > 0) - fos.write(pkt.getPayload().getData(), 0, includeLen); + _fos.write(pkt.getPayload().getData(), 0, includeLen); + if (pkt.isFlagSet(Packet.FLAG_CLOSE)) + _fos.flush(); } /** @@ -294,7 +313,7 @@ public class PcapWriter { * * To do: Add the SACK option to the TCP header. */ - private static long getLowestAckedThrough(PacketLocal pkt, Connection con) { + private static long getLowestAckedThrough(Packet pkt, Connection con) { long nacks[] = pkt.getNacks(); long lowest = pkt.getAckThrough(); // can return -1 but we increment below if (nacks != null) { @@ -311,14 +330,16 @@ public class PcapWriter { } private static class Options { - byte[] _b; - int _len; + private final byte[] _b; + private int _len; + public Options() { _b = new byte[MAX_OPTION_LEN]; } /** 40 bytes long, caller must use size() to get actual size */ public byte[] getData() { return _b; } + /** rounded to next 4 bytes */ public int size() { return ((_len + 3) / 4) * 4; } diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index ca8312125f12c7e8b74ce460fc63a3090267a208..a411b009986dc5f1f6384384ad98aeea42885422 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -21,7 +21,7 @@ public class RouterVersion { public final static long BUILD = 0; /** for example "-test" */ - public final static String EXTRA = "-pcap"; + public final static String EXTRA = ""; public final static String FULL_VERSION = VERSION + "-" + BUILD + EXTRA; public static void main(String args[]) { System.out.println("I2P Router version: " + FULL_VERSION);