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