diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/HTTPResponseOutputStream.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/HTTPResponseOutputStream.java new file mode 100644 index 000000000..9d0af21e7 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/HTTPResponseOutputStream.java @@ -0,0 +1,232 @@ +package net.i2p.i2ptunnel; +/* + * free (adj.): unencumbered; not under the control of others + * Written by jrandom in 2005 and released into the public domain + * with no warranty of any kind, either expressed or implied. + * It probably won't make your computer catch on fire, or eat + * your children, but it might. Use at your own risk. + * + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.FilterOutputStream; +import java.io.OutputStream; +import java.util.Properties; +import java.util.Iterator; +import net.i2p.data.ByteArray; +import net.i2p.util.ByteCache; +import net.i2p.util.Log; + +/** + * Simple stream for delivering an HTTP response to + * the client, trivially filtered to make sure "Connection: close" + * is always in the response. + * + */ +class HTTPResponseOutputStream extends FilterOutputStream { + private static final Log _log = new Log(HTTPResponseOutputStream.class); + private ByteCache _cache; + protected ByteArray _headerBuffer; + private boolean _headerWritten; + private byte _buf1[]; + private static final int CACHE_SIZE = 4096; + + public HTTPResponseOutputStream(OutputStream raw) { + super(raw); + _cache = ByteCache.getInstance(8, CACHE_SIZE); + _headerBuffer = _cache.acquire(); + _headerWritten = false; + _buf1 = new byte[1]; + } + + public void write(int c) throws IOException { + _buf1[0] = (byte)c; + write(_buf1, 0, 1); + } + public void write(byte buf[]) throws IOException { + write(buf, 0, buf.length); + } + public void write(byte buf[], int off, int len) throws IOException { + if (_headerWritten) { + out.write(buf, off, len); + return; + } + + for (int i = 0; i < len; i++) { + ensureCapacity(); + _headerBuffer.getData()[_headerBuffer.getValid()] = buf[off+i]; + _headerBuffer.setValid(_headerBuffer.getValid()+1); + + if (headerReceived()) { + writeHeader(); + _headerWritten = true; + if (i + 1 < len) // write out the remaining + out.write(buf, off+i+1, len-i-1); + return; + } + } + } + + /** + * filter any headers (adding or removing as necessary), and tweak + * the first response line as necessary. + * + * @return response line ("200 OK", etc) + */ + protected String filterHeaders(String responseLine, Properties props) { + props.setProperty("Connection", "close"); + props.setProperty("Proxy-Connection", "close"); + return responseLine; + } + + + /** grow (and free) the buffer as necessary */ + private void ensureCapacity() { + if (_headerBuffer.getValid() + 1 >= _headerBuffer.getData().length) { + int newSize = (int)(_headerBuffer.getData().length * 1.5); + ByteArray newBuf = new ByteArray(new byte[newSize]); + System.arraycopy(_headerBuffer.getData(), 0, newBuf, 0, _headerBuffer.getValid()); + if (_headerBuffer.getData().length == CACHE_SIZE) + _cache.release(_headerBuffer); + _headerBuffer = newBuf; + } + } + + /** are the headers finished? */ + private boolean headerReceived() { + if (_headerBuffer.getValid() < 3) return false; + byte first = _headerBuffer.getData()[_headerBuffer.getValid()-3]; + byte second = _headerBuffer.getData()[_headerBuffer.getValid()-2]; + byte third = _headerBuffer.getData()[_headerBuffer.getValid()-1]; + return (isNL(second) && isNL(third)) || // \n\n + (isNL(first) && isNL(third)); // \n\r\n + } + + /** we ignore any potential \r, since we trim it on write anyway */ + private static final byte NL = '\n'; + private boolean isNL(byte b) { return (b == NL); } + + /** ok, received, now munge & write it */ + private void writeHeader() throws IOException { + Properties props = new Properties(); + String responseLine = null; + + int lastEnd = -1; + for (int i = 0; i < _headerBuffer.getValid(); i++) { + if (isNL(_headerBuffer.getData()[i])) { + if (lastEnd == -1) { + responseLine = new String(_headerBuffer.getData(), 0, i+1); // includes NL + } else { + for (int j = lastEnd+1; j < i; j++) { + if (_headerBuffer.getData()[j] == ':') { + String key = new String(_headerBuffer.getData(), lastEnd+1, j-(lastEnd+1)); + String val = new String(_headerBuffer.getData(), j+2, i-(j+2)); + props.setProperty(key, val); + break; + } + } + } + lastEnd = i; + } + } + + if (responseLine == null) + throw new IOException("No HTTP response line, with props=" + props); + + responseLine = filterHeaders(responseLine, props); + responseLine = (responseLine.trim() + "\n"); + + if (_log.shouldLog(Log.DEBUG)) { + StringBuffer msg = new StringBuffer(responseLine.length() + props.size() * 64); + msg.append("HTTP response: first line [").append(responseLine.trim()); + msg.append("] options: \n"); + + for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) { + String key = (String)iter.next(); + String val = props.getProperty(key); + msg.append('[').append(key.trim()).append("]=[").append(val.trim()).append("]\n"); + } + + _log.debug(msg.toString()); + } + + out.write(responseLine.getBytes()); + + for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) { + String key = (String)iter.next(); + String val = props.getProperty(key); + String line = key.trim() + ": " + val.trim() + "\n"; + out.write(line.getBytes()); + } + out.write("\n".getBytes()); // end of the headers + + // done, shove off + if (_headerBuffer.getData().length == CACHE_SIZE) + _cache.release(_headerBuffer); + else + _headerBuffer = null; + } + + public static void main(String args[]) { + String simple = "HTTP/1.1 200 OK\n" + + "foo: bar\n" + + "baz: bat\n" + + "\n" + + "hi ho, this is the body"; + String filtered = "HTTP/1.1 200 OK\n" + + "Connection: keep-alive\n" + + "foo: bar\n" + + "baz: bat\n" + + "\n" + + "hi ho, this is the body"; + String winfilter= "HTTP/1.1 200 OK\r\n" + + "Connection: keep-alive\r\n" + + "foo: bar\r\n" + + "baz: bat\r\n" + + "\r\n" + + "hi ho, this is the body"; + String minimal = "HTTP/1.1 200 OK\n" + + "\n" + + "hi ho, this is the body"; + String winmin = "HTTP/1.1 200 OK\r\n" + + "\r\n" + + "hi ho, this is the body"; + String invalid1 = "HTTP/1.1 200 OK\n"; + String invalid2 = "HTTP/1.1 200 OK"; + String invalid3 = "HTTP 200 OK\r\n"; + String invalid4 = "HTTP 200 OK\r"; + String large = "HTTP/1.1 200 OK\n" + + "Last-modified: Tue, 25 Nov 2003 12:05:38 GMT\n" + + "Expires: Tue, 25 Nov 2003 12:05:38 GMT\n" + + "Content-length: 32\n" + + "\n" + + "hi ho, this is the body"; + /* */ + test("Simple", simple); + test("Filtered", filtered); + test("Filtered windows", winfilter); + test("Minimal", minimal); + test("Windows", winmin); + test("Large", large); + test("Invalid (short headers)", invalid1); + test("Invalid (no headers)", invalid2); + test("Invalid (windows with short headers)", invalid3); + test("Invalid (windows no headers)", invalid4); + /* */ + } + + private static void test(String name, String orig) { + System.out.println("====Testing: " + name + "\n" + orig + "\n------------"); + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(4096); + HTTPResponseOutputStream resp = new HTTPResponseOutputStream(baos); + resp.write(orig.getBytes()); + resp.flush(); + String received = new String(baos.toByteArray()); + System.out.println(received); + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java index ed9a62a43..6f74176ae 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java @@ -416,7 +416,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable I2PSocket i2ps = createI2PSocket(dest, getDefaultOptions(opts)); byte[] data = newRequest.toString().getBytes("ISO-8859-1"); Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId); - I2PTunnelRunner runner = new I2PTunnelRunner(s, i2ps, sockLock, data, mySockets, onTimeout); + I2PTunnelRunner runner = new I2PTunnelHTTPClientRunner(s, i2ps, sockLock, data, mySockets, onTimeout); } catch (SocketException ex) { _log.info(getPrefix(requestId) + "Error trying to connect", ex); l.log(ex.getMessage()); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClientRunner.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClientRunner.java new file mode 100644 index 000000000..7688d4103 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClientRunner.java @@ -0,0 +1,41 @@ +/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java) + * (c) 2003 - 2004 mihi + */ +package net.i2p.i2ptunnel; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.FilterOutputStream; +import java.net.Socket; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; + +import net.i2p.client.streaming.I2PSocket; +import net.i2p.data.ByteArray; +import net.i2p.data.DataHelper; +import net.i2p.util.ByteCache; +import net.i2p.util.Log; + +/** + * Override the response with a stream filtering the HTTP headers + * received. Specifically, this makes sure we get Connection: close, + * so the browser knows they really shouldn't try to use persistent + * connections. The HTTP server *should* already be setting this, + * since the HTTP headers sent by the browser specify Connection: close, + * and the server should echo it. However, both broken and malicious + * servers could ignore that, potentially confusing the user. + * + */ +public class I2PTunnelHTTPClientRunner extends I2PTunnelRunner { + public I2PTunnelHTTPClientRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, List sockList, Runnable onTimeout) { + super(s, i2ps, slock, initialI2PData, sockList, onTimeout); + } + + protected OutputStream getSocketOut() throws IOException { + OutputStream raw = super.getSocketOut(); + return new HTTPResponseOutputStream(raw); + } +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java index 12d7824ea..6f906a138 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java @@ -111,10 +111,13 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL return startedOn; } + protected InputStream getSocketIn() throws IOException { return s.getInputStream(); } + protected OutputStream getSocketOut() throws IOException { return s.getOutputStream(); } + public void run() { try { - InputStream in = s.getInputStream(); - OutputStream out = s.getOutputStream(); // = new BufferedOutputStream(s.getOutputStream(), NETWORK_BUFFER_SIZE); + InputStream in = getSocketIn(); + OutputStream out = getSocketOut(); // = new BufferedOutputStream(s.getOutputStream(), NETWORK_BUFFER_SIZE); i2ps.setSocketErrorListener(this); InputStream i2pin = i2ps.getInputStream(); OutputStream i2pout = i2ps.getOutputStream(); //new BufferedOutputStream(i2ps.getOutputStream(), MAX_PACKET_SIZE); diff --git a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java index 17dc40b38..1d748c152 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java @@ -584,6 +584,7 @@ public class Connection { } } + /** how many packets have we sent and the other side has ACKed? */ public long getAckedPackets() { return _ackedPackets; } public long getCreatedOn() { return _createdOn; } public long getCloseSentOn() { return _closeSentOn; } @@ -599,6 +600,7 @@ public class Connection { public void incrementUnackedPacketsReceived() { _unackedPacketsReceived++; } public int getUnackedPacketsReceived() { return _unackedPacketsReceived; } + /** how many packets have we sent but not yet received an ACK for? */ public int getUnackedPacketsSent() { synchronized (_outboundPackets) { return _outboundPackets.size(); diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionDataReceiver.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionDataReceiver.java index 5ed0e7a6c..5bfa4b1df 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionDataReceiver.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionDataReceiver.java @@ -132,6 +132,8 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver { private PacketLocal buildPacket(Connection con, byte buf[], int off, int size, boolean forceIncrement) { if (size > Packet.MAX_PAYLOAD_SIZE) throw new IllegalArgumentException("size is too large (" + size + ")"); boolean ackOnly = isAckOnly(con, size); + boolean isFirst = (con.getAckedPackets() <= 0) && (con.getUnackedPacketsSent() <= 0); + PacketLocal packet = new PacketLocal(_context, con.getRemotePeer(), con); //ByteArray data = packet.acquirePayload(); ByteArray data = new ByteArray(new byte[size]); @@ -140,7 +142,7 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver { data.setValid(size); data.setOffset(0); packet.setPayload(data); - if (ackOnly && !forceIncrement) + if ( (ackOnly && !forceIncrement) && (!isFirst) ) packet.setSequenceNum(0); else packet.setSequenceNum(con.getNextOutboundPacketNum()); @@ -158,7 +160,8 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver { packet.setFlag(Packet.FLAG_SIGNATURE_REQUESTED, con.getOptions().getRequireFullySigned()); - if ( (!ackOnly) && (packet.getSequenceNum() <= 0) ) { + //if ( (!ackOnly) && (packet.getSequenceNum() <= 0) ) { + if (isFirst) { packet.setFlag(Packet.FLAG_SYNCHRONIZE); packet.setOptionalFrom(con.getSession().getMyDestination()); packet.setOptionalMaxSize(con.getOptions().getMaxMessageSize()); 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 a2f0916be..d6ad8d69f 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java @@ -160,6 +160,7 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat synchronized (this) { if (_ackOn > 0) break; if (_cancelledOn > 0) break; + if (!_connection.getIsConnected()) break; if (timeRemaining > 60*1000) timeRemaining = 60*1000; else if (timeRemaining <= 0) diff --git a/core/java/src/net/i2p/data/DataHelper.java b/core/java/src/net/i2p/data/DataHelper.java index e3095926b..7b525b8b5 100644 --- a/core/java/src/net/i2p/data/DataHelper.java +++ b/core/java/src/net/i2p/data/DataHelper.java @@ -865,7 +865,7 @@ public class DataHelper { ReusableGZIPInputStream in = ReusableGZIPInputStream.acquire(); in.initialize(new ByteArrayInputStream(orig, offset, length)); - ByteCache cache = ByteCache.getInstance(16, MAX_UNCOMPRESSED); + ByteCache cache = ByteCache.getInstance(8, MAX_UNCOMPRESSED); ByteArray outBuf = cache.acquire(); int written = 0; while (true) { @@ -877,6 +877,7 @@ public class DataHelper { byte rv[] = new byte[written]; System.arraycopy(outBuf.getData(), 0, rv, 0, written); cache.release(outBuf); + ReusableGZIPInputStream.release(in); return rv; } diff --git a/core/java/src/net/i2p/time/Timestamper.java b/core/java/src/net/i2p/time/Timestamper.java index ebd29c6ef..db7b4bd89 100644 --- a/core/java/src/net/i2p/time/Timestamper.java +++ b/core/java/src/net/i2p/time/Timestamper.java @@ -134,8 +134,8 @@ public class Timestamper implements Runnable { try { lastFailed = !queryTime(serverList); } catch (IllegalArgumentException iae) { - if (!lastFailed) - _log.log(Log.CRIT, "Unable to reach any of the NTP servers - network disconnected?"); + if ( (!lastFailed) && (_log.shouldLog(Log.ERROR)) ) + _log.error("Unable to reach any of the NTP servers - network disconnected?"); lastFailed = true; } } diff --git a/core/java/src/net/i2p/util/OrderedProperties.java b/core/java/src/net/i2p/util/OrderedProperties.java index d65a42396..831ec9b77 100644 --- a/core/java/src/net/i2p/util/OrderedProperties.java +++ b/core/java/src/net/i2p/util/OrderedProperties.java @@ -238,8 +238,8 @@ public class OrderedProperties extends Properties { public int compareTo(Object o) { if (o == null) return -1; - if (o instanceof StringMapEntry) return ((String) getKey()).compareTo(((StringMapEntry) o).getKey()); - if (o instanceof String) return ((String) getKey()).compareTo(o); + if (o instanceof StringMapEntry) return ((String) getKey()).compareTo((String)((StringMapEntry) o).getKey()); + if (o instanceof String) return ((String) getKey()).compareTo((String)o); return -2; } diff --git a/core/java/src/net/i2p/util/ReusableGZIPInputStream.java b/core/java/src/net/i2p/util/ReusableGZIPInputStream.java index 4e330fe55..832d242a5 100644 --- a/core/java/src/net/i2p/util/ReusableGZIPInputStream.java +++ b/core/java/src/net/i2p/util/ReusableGZIPInputStream.java @@ -15,7 +15,7 @@ import net.i2p.data.DataHelper; * */ public class ReusableGZIPInputStream extends ResettableGZIPInputStream { - private static ArrayList _available = new ArrayList(16); + private static ArrayList _available = new ArrayList(8); /** * Pull a cached instance */ @@ -36,7 +36,7 @@ public class ReusableGZIPInputStream extends ResettableGZIPInputStream { */ public static void release(ReusableGZIPInputStream released) { synchronized (_available) { - if (_available.size() < 16) + if (_available.size() < 8) _available.add(released); } } diff --git a/history.txt b/history.txt index c60663492..564b1d653 100644 --- a/history.txt +++ b/history.txt @@ -1,4 +1,13 @@ -$Id: history.txt,v 1.162 2005/03/02 22:36:52 jrandom Exp $ +$Id: history.txt,v 1.163 2005/03/04 01:09:51 jrandom Exp $ + +2005-03-04 jrandom + * Filter HTTP response headers in the eepproxy, forcing Connection: close + so that broken (/malicious) webservers can't allow persistent + connections. All HTTP compliant browsers should now always close the + socket. + * Enabled the GZIPInputStream's cache (they were'nt cached before) + * Make sure our first send is always a SYN (duh) + * Workaround for some buggy compilers 2005-03-03 jrandom * Loop while starting up the I2PTunnel instances, in case the I2CP diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index b259edd3e..87ab848e7 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -15,9 +15,9 @@ import net.i2p.CoreVersion; * */ public class RouterVersion { - public final static String ID = "$Revision: 1.157 $ $Date: 2005/03/02 22:36:53 $"; + public final static String ID = "$Revision: 1.158 $ $Date: 2005/03/04 01:09:20 $"; public final static String VERSION = "0.5.0.1"; - public final static long BUILD = 9; + public final static long BUILD = 10; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION); System.out.println("Router ID: " + RouterVersion.ID); diff --git a/router/java/src/net/i2p/router/tunnel/InboundGatewayReceiver.java b/router/java/src/net/i2p/router/tunnel/InboundGatewayReceiver.java index b961d6b88..d0be132f4 100644 --- a/router/java/src/net/i2p/router/tunnel/InboundGatewayReceiver.java +++ b/router/java/src/net/i2p/router/tunnel/InboundGatewayReceiver.java @@ -27,7 +27,7 @@ public class InboundGatewayReceiver implements TunnelGateway.Receiver { if (_target == null) { ReceiveJob j = null; if (!alreadySearched) - j = new ReceiveJob(encrypted); + j = new ReceiveJob(_context, encrypted); _context.netDb().lookupRouterInfo(_config.getSendTo(), j, j, 5*1000); return; } @@ -47,8 +47,8 @@ public class InboundGatewayReceiver implements TunnelGateway.Receiver { private class ReceiveJob extends JobImpl { private byte[] _encrypted; - public ReceiveJob(byte data[]) { - super(_context); + public ReceiveJob(RouterContext ctx, byte data[]) { + super(ctx); _encrypted = data; } public String getName() { return "lookup first hop"; } diff --git a/router/java/src/net/i2p/router/tunnel/OutboundReceiver.java b/router/java/src/net/i2p/router/tunnel/OutboundReceiver.java index e84b57059..08d8733ab 100644 --- a/router/java/src/net/i2p/router/tunnel/OutboundReceiver.java +++ b/router/java/src/net/i2p/router/tunnel/OutboundReceiver.java @@ -42,7 +42,7 @@ class OutboundReceiver implements TunnelGateway.Receiver { if (_log.shouldLog(Log.DEBUG)) _log.debug("lookup of " + _config.getPeer(1).toBase64().substring(0,4) + " required for " + msg); - _context.netDb().lookupRouterInfo(_config.getPeer(1), new SendJob(msg), new FailedJob(), 10*1000); + _context.netDb().lookupRouterInfo(_config.getPeer(1), new SendJob(_context, msg), new FailedJob(_context), 10*1000); } } @@ -60,8 +60,8 @@ class OutboundReceiver implements TunnelGateway.Receiver { private class SendJob extends JobImpl { private TunnelDataMessage _msg; - public SendJob(TunnelDataMessage msg) { - super(_context); + public SendJob(RouterContext ctx, TunnelDataMessage msg) { + super(ctx); _msg = msg; } public String getName() { return "forward a tunnel message"; } @@ -78,8 +78,8 @@ class OutboundReceiver implements TunnelGateway.Receiver { } private class FailedJob extends JobImpl { - public FailedJob() { - super(_context); + public FailedJob(RouterContext ctx) { + super(ctx); } public String getName() { return "failed looking for our outbound gateway"; } public void runJob() { diff --git a/router/java/src/net/i2p/router/tunnel/TunnelParticipant.java b/router/java/src/net/i2p/router/tunnel/TunnelParticipant.java index 6f36fed4d..dc2812760 100644 --- a/router/java/src/net/i2p/router/tunnel/TunnelParticipant.java +++ b/router/java/src/net/i2p/router/tunnel/TunnelParticipant.java @@ -77,7 +77,7 @@ public class TunnelParticipant { if (_log.shouldLog(Log.WARN)) _log.warn("Lookup the nextHop (" + _config.getSendTo().toBase64().substring(0,4) + " for " + msg); - _context.netDb().lookupRouterInfo(_config.getSendTo(), new SendJob(msg), new TimeoutJob(msg), 10*1000); + _context.netDb().lookupRouterInfo(_config.getSendTo(), new SendJob(_context, msg), new TimeoutJob(_context, msg), 10*1000); } } else { _inboundEndpointProcessor.getConfig().incrementProcessedMessages(); @@ -112,8 +112,8 @@ public class TunnelParticipant { private class SendJob extends JobImpl { private TunnelDataMessage _msg; - public SendJob(TunnelDataMessage msg) { - super(_context); + public SendJob(RouterContext ctx, TunnelDataMessage msg) { + super(ctx); _msg = msg; } public String getName() { return "forward a tunnel message"; } @@ -132,8 +132,8 @@ public class TunnelParticipant { private class TimeoutJob extends JobImpl { private TunnelDataMessage _msg; - public TimeoutJob(TunnelDataMessage msg) { - super(_context); + public TimeoutJob(RouterContext ctx, TunnelDataMessage msg) { + super(ctx); _msg = msg; } public String getName() { return "timeout looking for next hop info"; }