forked from I2P_Developers/i2p.i2p
Compare commits
69 Commits
i2p_0_4_2
...
i2p_0_4_2_
Author | SHA1 | Date | |
---|---|---|---|
4b2a734cda | |||
97ae8f78a0 | |||
834665c3ba | |||
d969dd2d8d | |||
3cb727561c | |||
cbc89376d3 | |||
66aa29e3d4 | |||
5c72aca5ee | |||
8824815d6d | |||
ad72e5cbdf | |||
b2f183fc17 | |||
9e16bc203a | |||
83c6eac017 | |||
d5b277a536 | |||
77ce6c33e3 | |||
60f8d349cf | |||
f539c3df70 | |||
fe1cf1758c | |||
8c71c26487 | |||
2ce39d1fd4 | |||
88a994b712 | |||
24c8cc1a0c | |||
caf684394c | |||
4b74510450 | |||
0ddcfc423e | |||
b4ac56e204 | |||
3b19ac3942 | |||
af52cad4ea | |||
d88396c1e2 | |||
4c5f7b9451 | |||
e601cedbb8 | |||
fa12dc867f | |||
acfb6c4578 | |||
e52d637092 | |||
2fba055696 | |||
88bb176f3b | |||
499eeb275b | |||
61a8d679bb | |||
2bbde91625 | |||
9ce098ee06 | |||
927ae57d24 | |||
d65c2d3539 | |||
2d9d8f32dc | |||
1a30cd5f4a | |||
f54687f398 | |||
9f4b4c5de1 | |||
33bfa94229 | |||
61e5f190a6 | |||
a4946272d0 | |||
8abd99d134 | |||
97e8ab7c5b | |||
cb930a7ab5 | |||
610f1f7dd4 | |||
516d0b4db8 | |||
df61ae5c6f | |||
9f6584b55e | |||
e4b41f5bb0 | |||
8d0cea93e9 | |||
d294d07919 | |||
153eea2bd5 | |||
571e3c5c13 | |||
a2d268f3d6 | |||
02d456d7a0 | |||
b3626ad86f | |||
9b6eab451f | |||
72be9b5f04 | |||
35e94a7f65 | |||
8e02586cc9 | |||
0b5a640896 |
@ -33,12 +33,12 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
private static final Log _log = new Log(I2PTunnelClientBase.class);
|
||||
protected Logging l;
|
||||
|
||||
private static final long DEFAULT_CONNECT_TIMEOUT = 60 * 1000;
|
||||
static final long DEFAULT_CONNECT_TIMEOUT = 60 * 1000;
|
||||
|
||||
private static volatile long __clientId = 0;
|
||||
protected long _clientId;
|
||||
protected Object sockLock = new Object(); // Guards sockMgr and mySockets
|
||||
private I2PSocketManager sockMgr;
|
||||
protected I2PSocketManager sockMgr;
|
||||
protected List mySockets = new ArrayList();
|
||||
|
||||
protected Destination dest = null;
|
||||
@ -184,13 +184,26 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
* create the default options (using the default timeout, etc)
|
||||
*
|
||||
*/
|
||||
private I2PSocketOptions getDefaultOptions() {
|
||||
protected I2PSocketOptions getDefaultOptions() {
|
||||
Properties defaultOpts = getTunnel().getClientOptions();
|
||||
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
|
||||
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
|
||||
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
|
||||
return opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* create the default options (using the default timeout, etc)
|
||||
*
|
||||
*/
|
||||
protected I2PSocketOptions getDefaultOptions(Properties overrides) {
|
||||
Properties defaultOpts = getTunnel().getClientOptions();
|
||||
defaultOpts.putAll(overrides);
|
||||
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
|
||||
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
|
||||
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
|
||||
return opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new I2PSocket towards to the specified destination,
|
||||
|
@ -14,10 +14,12 @@ import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.I2PSocketOptions;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Clock;
|
||||
@ -142,14 +144,49 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
return proxy;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int DEFAULT_READ_TIMEOUT = 60*1000;
|
||||
|
||||
|
||||
/**
|
||||
* create the default options (using the default timeout, etc)
|
||||
*
|
||||
*/
|
||||
protected I2PSocketOptions getDefaultOptions() {
|
||||
Properties defaultOpts = getTunnel().getClientOptions();
|
||||
if (!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT))
|
||||
defaultOpts.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, ""+DEFAULT_READ_TIMEOUT);
|
||||
if (!defaultOpts.contains("i2p.streaming.inactivityTimeout"))
|
||||
defaultOpts.setProperty("i2p.streaming.inactivityTimeout", ""+DEFAULT_READ_TIMEOUT);
|
||||
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
|
||||
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
|
||||
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
|
||||
return opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* create the default options (using the default timeout, etc)
|
||||
*
|
||||
*/
|
||||
protected I2PSocketOptions getDefaultOptions(Properties overrides) {
|
||||
Properties defaultOpts = getTunnel().getClientOptions();
|
||||
defaultOpts.putAll(overrides);
|
||||
if (!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT))
|
||||
defaultOpts.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, ""+DEFAULT_READ_TIMEOUT);
|
||||
if (!defaultOpts.contains("i2p.streaming.inactivityTimeout"))
|
||||
defaultOpts.setProperty("i2p.streaming.inactivityTimeout", ""+DEFAULT_READ_TIMEOUT);
|
||||
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
|
||||
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
|
||||
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
|
||||
return opts;
|
||||
}
|
||||
|
||||
private static long __requestId = 0;
|
||||
protected void clientConnectionRun(Socket s) {
|
||||
OutputStream out = null;
|
||||
String targetRequest = null;
|
||||
boolean usingWWWProxy = false;
|
||||
String currentProxy = null;
|
||||
InactivityTimeoutThread timeoutThread = null;
|
||||
long requestId = ++__requestId;
|
||||
try {
|
||||
out = s.getOutputStream();
|
||||
@ -295,7 +332,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(getPrefix(requestId) + "Setting host = " + host);
|
||||
} else if (line.startsWith("User-Agent: ")) {
|
||||
line = "User-Agent: MYOB/6.66 (AN/ON)";
|
||||
// always stripped, added back at the end
|
||||
line = null;
|
||||
continue;
|
||||
} else if (line.startsWith("Accept")) {
|
||||
// strip the accept-blah headers, as they vary dramatically from
|
||||
// browser to browser
|
||||
line = null;
|
||||
continue;
|
||||
} else if (line.startsWith("Referer: ")) {
|
||||
// Shouldn't we be more specific, like accepting in-site referers ?
|
||||
//line = "Referer: i2p";
|
||||
@ -313,6 +357,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
|
||||
if (line.length() == 0) {
|
||||
newRequest.append("User-Agent: MYOB/6.66 (AN/ON)\r\n");
|
||||
newRequest.append("Connection: close\r\n\r\n");
|
||||
break;
|
||||
} else {
|
||||
@ -354,25 +399,27 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
return;
|
||||
}
|
||||
String remoteID;
|
||||
I2PSocket i2ps = createI2PSocket(dest);
|
||||
|
||||
Properties opts = new Properties();
|
||||
opts.setProperty("i2p.streaming.inactivityTimeout", ""+120*1000);
|
||||
// 1 == disconnect. see ConnectionOptions in the new streaming lib, which i
|
||||
// dont want to hard link to here
|
||||
opts.setProperty("i2p.streaming.inactivityTimeoutAction", ""+1);
|
||||
I2PSocket i2ps = createI2PSocket(dest, getDefaultOptions(opts));
|
||||
byte[] data = newRequest.toString().getBytes("ISO-8859-1");
|
||||
I2PTunnelRunner runner = new I2PTunnelRunner(s, i2ps, sockLock, data, mySockets);
|
||||
timeoutThread = new InactivityTimeoutThread(runner, out, targetRequest, usingWWWProxy, currentProxy, s, requestId);
|
||||
timeoutThread.start();
|
||||
Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
I2PTunnelRunner runner = new I2PTunnelRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
|
||||
} catch (SocketException ex) {
|
||||
if (timeoutThread != null) timeoutThread.disable();
|
||||
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
closeSocket(s);
|
||||
} catch (IOException ex) {
|
||||
if (timeoutThread != null) timeoutThread.disable();
|
||||
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
closeSocket(s);
|
||||
} catch (I2PException ex) {
|
||||
if (timeoutThread != null) timeoutThread.disable();
|
||||
_log.info("getPrefix(requestId) + Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
@ -380,91 +427,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
}
|
||||
|
||||
private static final long INACTIVITY_TIMEOUT = 120 * 1000;
|
||||
private static volatile long __timeoutId = 0;
|
||||
|
||||
private class InactivityTimeoutThread extends I2PThread {
|
||||
|
||||
private Socket s;
|
||||
private I2PTunnelRunner _runner;
|
||||
private OutputStream _out;
|
||||
private String _targetRequest;
|
||||
private boolean _useWWWProxy;
|
||||
private String _currentProxy;
|
||||
private long _requestId;
|
||||
private boolean _disabled;
|
||||
private Object _disableLock = new Object();
|
||||
|
||||
public InactivityTimeoutThread(I2PTunnelRunner runner, OutputStream out, String targetRequest,
|
||||
boolean useWWWProxy, String currentProxy, Socket s, long requestId) {
|
||||
this.s = s;
|
||||
_runner = runner;
|
||||
_out = out;
|
||||
_targetRequest = targetRequest;
|
||||
_useWWWProxy = useWWWProxy;
|
||||
_currentProxy = currentProxy;
|
||||
_disabled = false;
|
||||
_requestId = requestId;
|
||||
long timeoutId = ++__timeoutId;
|
||||
setName("InactivityThread " + getPrefix(requestId) + timeoutId);
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
_disabled = true;
|
||||
synchronized (_disableLock) {
|
||||
_disableLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while (!_disabled) {
|
||||
if (_runner.isFinished()) {
|
||||
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix(_requestId) + "HTTP client request completed prior to timeout");
|
||||
return;
|
||||
}
|
||||
if (_runner.getLastActivityOn() < Clock.getInstance().now() - INACTIVITY_TIMEOUT) {
|
||||
if (_runner.getStartedOn() < Clock.getInstance().now() - INACTIVITY_TIMEOUT) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getPrefix(_requestId) + "HTTP client request timed out (lastActivity: "
|
||||
+ new Date(_runner.getLastActivityOn()) + ", startedOn: "
|
||||
+ new Date(_runner.getStartedOn()) + ")");
|
||||
timeout();
|
||||
return;
|
||||
} else {
|
||||
// runner hasn't been going to long enough
|
||||
}
|
||||
} else {
|
||||
// there has been activity in the period
|
||||
}
|
||||
synchronized (_disableLock) {
|
||||
try {
|
||||
_disableLock.wait(INACTIVITY_TIMEOUT);
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void timeout() {
|
||||
_log.info(getPrefix(_requestId) + "Inactivity timeout reached");
|
||||
l.log("Inactivity timeout reached");
|
||||
if (_out != null) {
|
||||
try {
|
||||
if (_runner.getLastActivityOn() > 0) {
|
||||
// some data has been sent, so don't 404 it
|
||||
} else {
|
||||
writeErrorMessage(ERR_TIMEOUT, _out, _targetRequest, _useWWWProxy, _currentProxy);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_log.warn(getPrefix(_requestId) + "Error writing out the 'timeout' message", ioe);
|
||||
}
|
||||
} else {
|
||||
_log.warn(getPrefix(_requestId) + "Client disconnected before we could say we timed out");
|
||||
}
|
||||
closeSocket(s);
|
||||
}
|
||||
}
|
||||
|
||||
private final static String getHostName(String host) {
|
||||
if (host == null) return null;
|
||||
try {
|
||||
@ -476,6 +438,30 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
}
|
||||
|
||||
private class OnTimeout implements Runnable {
|
||||
private Socket _socket;
|
||||
private OutputStream _out;
|
||||
private String _target;
|
||||
private boolean _usingProxy;
|
||||
private String _wwwProxy;
|
||||
private long _requestId;
|
||||
public OnTimeout(Socket s, OutputStream out, String target, boolean usingProxy, String wwwProxy, long id) {
|
||||
_socket = s;
|
||||
_out = out;
|
||||
_target = target;
|
||||
_usingProxy = usingProxy;
|
||||
_wwwProxy = wwwProxy;
|
||||
_requestId = id;
|
||||
}
|
||||
public void run() {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Timeout occured requesting " + _target);
|
||||
handleHTTPClientException(new RuntimeException("Timeout"), _out,
|
||||
_target, _usingProxy, _wwwProxy, _requestId);
|
||||
closeSocket(_socket);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeErrorMessage(byte[] errMessage, OutputStream out, String targetRequest,
|
||||
boolean usingWWWProxy, String wwwProxy) throws IOException {
|
||||
if (out != null) {
|
||||
|
@ -46,15 +46,23 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
/** when the runner started up */
|
||||
private long startedOn;
|
||||
private List sockList;
|
||||
/** if we die before receiving any data, run this job */
|
||||
private Runnable onTimeout;
|
||||
private long totalSent;
|
||||
private long totalReceived;
|
||||
|
||||
private volatile long __forwarderId;
|
||||
|
||||
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialData, List sockList) {
|
||||
this(s, i2ps, slock, initialData, sockList, null);
|
||||
}
|
||||
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialData, List sockList, Runnable onTimeout) {
|
||||
this.sockList = sockList;
|
||||
this.s = s;
|
||||
this.i2ps = i2ps;
|
||||
this.slock = slock;
|
||||
this.initialData = initialData;
|
||||
this.onTimeout = onTimeout;
|
||||
lastActivityOn = -1;
|
||||
startedOn = Clock.getInstance().now();
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
@ -97,7 +105,6 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
}
|
||||
|
||||
public void run() {
|
||||
boolean closedCleanly = false;
|
||||
try {
|
||||
InputStream in = s.getInputStream();
|
||||
OutputStream out = s.getOutputStream(); // = new BufferedOutputStream(s.getOutputStream(), NETWORK_BUFFER_SIZE);
|
||||
@ -110,19 +117,33 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
//i2pout.flush();
|
||||
}
|
||||
}
|
||||
Thread t1 = new StreamForwarder(in, i2pout, "toI2P");
|
||||
Thread t2 = new StreamForwarder(i2pin, out, "fromI2P");
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Initial data " + (initialData != null ? initialData.length : 0)
|
||||
+ " written, starting forwarders");
|
||||
Thread t1 = new StreamForwarder(in, i2pout, true);
|
||||
Thread t2 = new StreamForwarder(i2pin, out, false);
|
||||
synchronized (finishLock) {
|
||||
while (!finished) {
|
||||
finishLock.wait();
|
||||
}
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("At least one forwarder completed, closing and joining");
|
||||
|
||||
// this task is useful for the httpclient
|
||||
if (onTimeout != null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("runner has a timeout job, totalReceived = " + totalReceived
|
||||
+ " totalSent = " + totalSent + " job = " + onTimeout);
|
||||
if ( (totalSent <= 0) && (totalReceived <= 0) )
|
||||
onTimeout.run();
|
||||
}
|
||||
|
||||
// now one connection is dead - kill the other as well.
|
||||
s.close();
|
||||
i2ps.close();
|
||||
t1.join();
|
||||
t2.join();
|
||||
closedCleanly = true;
|
||||
t1.join(30*1000);
|
||||
t2.join(30*1000);
|
||||
} catch (InterruptedException ex) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Interrupted", ex);
|
||||
@ -135,21 +156,20 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
} finally {
|
||||
removeRef();
|
||||
try {
|
||||
if ( (s != null) && (!closedCleanly) )
|
||||
if (s != null)
|
||||
s.close();
|
||||
} catch (IOException ex) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Could not close java socket", ex);
|
||||
}
|
||||
try {
|
||||
if (i2ps != null) {
|
||||
if (!closedCleanly)
|
||||
i2ps.close();
|
||||
i2ps.setSocketErrorListener(null);
|
||||
if (i2ps != null) {
|
||||
try {
|
||||
i2ps.close();
|
||||
} catch (IOException ex) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Could not close I2PSocket", ex);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Could not close I2PSocket", ex);
|
||||
i2ps.setSocketErrorListener(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -176,12 +196,14 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
InputStream in;
|
||||
OutputStream out;
|
||||
String direction;
|
||||
private boolean _toI2P;
|
||||
private ByteCache _cache;
|
||||
|
||||
private StreamForwarder(InputStream in, OutputStream out, String dir) {
|
||||
private StreamForwarder(InputStream in, OutputStream out, boolean toI2P) {
|
||||
this.in = in;
|
||||
this.out = out;
|
||||
direction = dir;
|
||||
_toI2P = toI2P;
|
||||
direction = (toI2P ? "toI2P" : "fromI2P");
|
||||
_cache = ByteCache.getInstance(256, NETWORK_BUFFER_SIZE);
|
||||
setName("StreamForwarder " + _runnerId + "." + (++__forwarderId));
|
||||
start();
|
||||
@ -202,6 +224,10 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
int len;
|
||||
while ((len = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, len);
|
||||
if (_toI2P)
|
||||
totalSent += len;
|
||||
else
|
||||
totalReceived += len;
|
||||
|
||||
if (len > 0) updateActivity();
|
||||
|
||||
@ -221,7 +247,7 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
out.flush(); // make sure the data get though
|
||||
}
|
||||
}
|
||||
out.flush();
|
||||
//out.flush(); // close() flushes
|
||||
} catch (SocketException ex) {
|
||||
// this *will* occur when the other threads closes the socket
|
||||
synchronized (finishLock) {
|
||||
@ -237,8 +263,8 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
+ ex.getMessage() + "\")");
|
||||
} catch (IOException ex) {
|
||||
if (!finished) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error(direction + ": Error forwarding", ex);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(direction + ": Error forwarding", ex);
|
||||
}
|
||||
//else
|
||||
// _log.warn("You may ignore this", ex);
|
||||
@ -248,11 +274,16 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
+ from + " and " + to);
|
||||
}
|
||||
try {
|
||||
out.close();
|
||||
in.close();
|
||||
} catch (IOException ex) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(direction + ": Error closing streams", ex);
|
||||
_log.warn(direction + ": Error closing input stream", ex);
|
||||
}
|
||||
try {
|
||||
out.flush();
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(direction + ": Error flushing to close", ioe);
|
||||
}
|
||||
synchronized (finishLock) {
|
||||
finished = true;
|
||||
|
@ -233,6 +233,9 @@ public class TunnelController implements Logging {
|
||||
String host = getI2CPHost();
|
||||
if ( (host != null) && (host.length() > 0) )
|
||||
_tunnel.host = host;
|
||||
// woohah, special casing for people with ipv6/etc
|
||||
if ("localhost".equals(_tunnel.host))
|
||||
_tunnel.host = "127.0.0.1";
|
||||
String port = getI2CPPort();
|
||||
if ( (port != null) && (port.length() > 0) )
|
||||
_tunnel.port = port;
|
||||
|
@ -103,7 +103,7 @@ class WebEditPageFormGenerator {
|
||||
if ( (controller != null) && (controller.getTargetHost() != null) )
|
||||
buf.append("value=\"").append(controller.getTargetHost()).append("\" ");
|
||||
else
|
||||
buf.append("value=\"localhost\" ");
|
||||
buf.append("value=\"127.0.0.1\" ");
|
||||
buf.append(" /><br />\n");
|
||||
|
||||
buf.append("<b>Target port:</b> <input type=\"text\" size=\"4\" name=\"targetPort\" ");
|
||||
@ -285,7 +285,7 @@ class WebEditPageFormGenerator {
|
||||
if ( (controller != null) && (controller.getI2CPHost() != null) )
|
||||
buf.append(controller.getI2CPHost());
|
||||
else
|
||||
buf.append("localhost");
|
||||
buf.append("127.0.0.1");
|
||||
buf.append("\" /><br />\n");
|
||||
buf.append("<b>I2CP port:</b> ");
|
||||
buf.append("<input type=\"text\" name=\"clientPort\" size=\"20\" value=\"");
|
||||
|
@ -100,8 +100,10 @@ public class I2PSocketManagerFactory {
|
||||
//p.setProperty("tunnels.depthInbound", "0");
|
||||
}
|
||||
|
||||
opts.setProperty(I2PClient.PROP_TCP_HOST, i2cpHost);
|
||||
opts.setProperty(I2PClient.PROP_TCP_PORT, "" + i2cpPort);
|
||||
if (i2cpHost != null)
|
||||
opts.setProperty(I2PClient.PROP_TCP_HOST, i2cpHost);
|
||||
if (i2cpPort > 0)
|
||||
opts.setProperty(I2PClient.PROP_TCP_PORT, "" + i2cpPort);
|
||||
|
||||
try {
|
||||
I2PSession session = client.createSession(myPrivateKeyStream, opts);
|
||||
|
@ -0,0 +1,218 @@
|
||||
package net.i2p.client.streaming;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.I2PClientFactory;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.I2PThread;
|
||||
|
||||
/**
|
||||
* Sit around on a destination, receiving lots of data and sending lots of
|
||||
* data to whomever talks to us.
|
||||
*
|
||||
* Usage: TestSwarm myKeyFile [peerDestFile ]*
|
||||
*
|
||||
*/
|
||||
public class TestSwarm {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private String _destFile;
|
||||
private String _peerDestFiles[];
|
||||
private String _conOptions;
|
||||
private I2PSocketManager _manager;
|
||||
private boolean _dead;
|
||||
|
||||
public static void main(String args[]) {
|
||||
if (args.length < 1) {
|
||||
System.err.println("Usage: TestSwarm myDestFile [peerDestFile ]*");
|
||||
return;
|
||||
}
|
||||
I2PAppContext ctx = new I2PAppContext();
|
||||
String files[] = new String[args.length - 1];
|
||||
System.arraycopy(args, 1, files, 0, files.length);
|
||||
TestSwarm swarm = new TestSwarm(ctx, args[0], files);
|
||||
swarm.startup();
|
||||
}
|
||||
|
||||
public TestSwarm(I2PAppContext ctx, String destFile, String peerDestFiles[]) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(TestSwarm.class);
|
||||
_dead = false;
|
||||
_destFile = destFile;
|
||||
_peerDestFiles = peerDestFiles;
|
||||
_conOptions = "";
|
||||
}
|
||||
|
||||
public void startup() {
|
||||
_log.debug("Starting up");
|
||||
File keys = new File(_destFile);
|
||||
if (!keys.exists()) {
|
||||
try {
|
||||
I2PClientFactory.createClient().createDestination(new FileOutputStream(keys));
|
||||
} catch (Exception e) {
|
||||
_log.error("Error creating a new destination on " + keys, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
_manager = I2PSocketManagerFactory.createManager(new FileInputStream(_destFile), null, -1, null);
|
||||
} catch (Exception e) {
|
||||
_log.error("Error creatign the manager", e);
|
||||
return;
|
||||
}
|
||||
|
||||
I2PThread listener = new I2PThread(new Listener(), "Listener");
|
||||
listener.start();
|
||||
|
||||
connectWithPeers();
|
||||
}
|
||||
|
||||
|
||||
private void connectWithPeers() {
|
||||
if (_peerDestFiles != null) {
|
||||
for (int i = 0; i < _peerDestFiles.length; i++) {
|
||||
try {
|
||||
FileInputStream fin = new FileInputStream(_peerDestFiles[i]);
|
||||
Destination dest = new Destination();
|
||||
dest.readBytes(fin);
|
||||
|
||||
I2PThread flooder = new I2PThread(new Flooder(dest), "Flooder+" + dest.calculateHash().toBase64().substring(0,4));
|
||||
flooder.start();
|
||||
} catch (Exception e) {
|
||||
_log.error("Unable to read the peer from " + _peerDestFiles[i], e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Listener implements Runnable {
|
||||
public void run() {
|
||||
try {
|
||||
I2PServerSocket ss = _manager.getServerSocket();
|
||||
I2PSocket s = null;
|
||||
while ( (s = ss.accept()) != null) {
|
||||
I2PThread flooder = new I2PThread(new Flooder(s), "Flooder-" + s.getPeerDestination().calculateHash().toBase64().substring(0,4));
|
||||
flooder.start();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
_log.error("Error listening", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static volatile long __conId = 0;
|
||||
private class Flooder implements Runnable {
|
||||
private Destination _remoteDestination;
|
||||
private I2PSocket _socket;
|
||||
private boolean _closed;
|
||||
private long _started;
|
||||
private long _totalSent;
|
||||
private long _totalReceived;
|
||||
private long _lastReceived;
|
||||
private long _lastReceivedOn;
|
||||
private long _connectionId;
|
||||
|
||||
public Flooder(Destination dest) {
|
||||
_socket = null;
|
||||
_remoteDestination = dest;
|
||||
_connectionId = ++__conId;
|
||||
_closed = false;
|
||||
_lastReceived = -1;
|
||||
_lastReceivedOn = _context.clock().now();
|
||||
_context.statManager().createRateStat("swarm." + _connectionId + ".totalReceived", "Data size received", "swarm", new long[] { 30*1000, 60*1000, 5*60*1000 });
|
||||
_context.statManager().createRateStat("swarm." + _connectionId + ".totalSent", "Data size sent", "swarm", new long[] { 30*1000, 60*1000, 5*60*1000 });
|
||||
_context.statManager().createRateStat("swarm." + _connectionId + ".started", "When we start", "swarm", new long[] { 5*60*1000 });
|
||||
_context.statManager().createRateStat("swarm." + _connectionId + ".lifetime", "How long we talk to a peer", "swarm", new long[] { 5*60*1000 });
|
||||
}
|
||||
|
||||
public Flooder(I2PSocket socket) {
|
||||
_socket = socket;
|
||||
_remoteDestination = socket.getPeerDestination();
|
||||
_connectionId = ++__conId;
|
||||
_closed = false;
|
||||
_lastReceived = -1;
|
||||
_lastReceivedOn = _context.clock().now();
|
||||
_context.statManager().createRateStat("swarm." + _connectionId + ".totalReceived", "Data size received", "swarm", new long[] { 30*1000, 60*1000, 5*60*1000 });
|
||||
_context.statManager().createRateStat("swarm." + _connectionId + ".totalSent", "Data size sent", "swarm", new long[] { 30*1000, 60*1000, 5*60*1000 });
|
||||
_context.statManager().createRateStat("swarm." + _connectionId + ".started", "When we start", "swarm", new long[] { 5*60*1000 });
|
||||
_context.statManager().createRateStat("swarm." + _connectionId + ".lifetime", "How long we talk to a peer", "swarm", new long[] { 5*60*1000 });
|
||||
}
|
||||
|
||||
public long getConnectionId() { return _connectionId; }
|
||||
public Destination getDestination() { return _remoteDestination; }
|
||||
|
||||
public void run() {
|
||||
_started = _context.clock().now();
|
||||
_context.statManager().addRateData("swarm." + _connectionId + ".started", 1, 0);
|
||||
byte data[] = new byte[32*1024];
|
||||
long value = 0;
|
||||
long lastSend = _context.clock().now();
|
||||
if (_socket == null) {
|
||||
try {
|
||||
_socket = _manager.connect(_remoteDestination);
|
||||
} catch (Exception e) {
|
||||
_log.error("Error connecting to " + _remoteDestination.calculateHash().toBase64().substring(0,4));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
I2PThread floodListener = new I2PThread(new FloodListener(), "FloodListener" + _connectionId);
|
||||
floodListener.start();
|
||||
|
||||
try {
|
||||
OutputStream out = _socket.getOutputStream();
|
||||
while (!_closed) {
|
||||
out.write(data);
|
||||
// out.flush();
|
||||
_totalSent += data.length;
|
||||
_context.statManager().addRateData("swarm." + _connectionId + ".totalSent", _totalSent, 0);
|
||||
//try { Thread.sleep(100); } catch (InterruptedException ie) {}
|
||||
long now = _context.clock().now();
|
||||
_log.debug("Sending " + _connectionId + " after " + (now-lastSend));
|
||||
lastSend = now;
|
||||
try { Thread.sleep(20); } catch (InterruptedException ie) {}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
_log.error("Error sending", e);
|
||||
}
|
||||
}
|
||||
|
||||
private class FloodListener implements Runnable {
|
||||
public void run() {
|
||||
long lastRead = System.currentTimeMillis();
|
||||
long now = lastRead;
|
||||
try {
|
||||
InputStream in = _socket.getInputStream();
|
||||
byte buf[] = new byte[32*1024];
|
||||
int read = 0;
|
||||
while ( (read = in.read(buf)) != -1) {
|
||||
now = System.currentTimeMillis();
|
||||
_totalReceived += read;
|
||||
_context.statManager().addRateData("swarm." + getConnectionId() + ".totalReceived", _totalReceived, 0);
|
||||
_log.debug("Receiving " + _connectionId + " with " + read + " after " + (now-lastRead));
|
||||
lastRead = now;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
_log.error("Error listening to the flood", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -120,10 +120,15 @@ public class ConfigNetHelper {
|
||||
private static String getBurstFactor(int numSeconds, String name) {
|
||||
StringBuffer buf = new StringBuffer(256);
|
||||
buf.append("<select name=\"").append(name).append("\">\n");
|
||||
boolean found = false;
|
||||
for (int i = 10; i <= 60; i += 10) {
|
||||
buf.append("<option value=\"").append(i).append("\" ");
|
||||
if ( (i == numSeconds) || (i == 60) )
|
||||
if (i == numSeconds) {
|
||||
buf.append("selected ");
|
||||
found = true;
|
||||
} else if ( (i == 60) && (!found) ) {
|
||||
buf.append("selected ");
|
||||
}
|
||||
buf.append(">");
|
||||
buf.append(i).append(" seconds</option>\n");
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Writer;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.router.RouterContext;
|
||||
|
||||
/**
|
||||
* uuuugly. dump the peer profile data if given a peer.
|
||||
*
|
||||
*/
|
||||
public class StatHelper {
|
||||
private String _peer;
|
||||
private Writer _writer;
|
||||
|
||||
public void setPeer(String peer) { _peer = peer; }
|
||||
public void setWriter(Writer writer) { _writer = writer; }
|
||||
|
||||
public String getProfile() {
|
||||
RouterContext ctx = (RouterContext)net.i2p.router.RouterContext.listContexts().get(0);
|
||||
Set peers = ctx.profileOrganizer().selectAllPeers();
|
||||
for (Iterator iter = peers.iterator(); iter.hasNext(); ) {
|
||||
Hash peer = (Hash)iter.next();
|
||||
if (peer.toBase64().startsWith(_peer)) {
|
||||
try {
|
||||
WriterOutputStream wos = new WriterOutputStream(_writer);
|
||||
ctx.profileOrganizer().exportProfile(peer, wos);
|
||||
wos.flush();
|
||||
_writer.flush();
|
||||
return "";
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* Treat a writer as an output stream. Quick 'n dirty, none
|
||||
* of that "intarnasheeonaleyzayshun" stuff. So we can treat
|
||||
* the jsp's PrintWriter as an OutputStream
|
||||
*/
|
||||
public class WriterOutputStream extends OutputStream {
|
||||
private Writer _writer;
|
||||
|
||||
public WriterOutputStream(Writer writer) { _writer = writer; }
|
||||
public void write(int b) throws IOException { _writer.write(b); }
|
||||
}
|
@ -66,9 +66,9 @@
|
||||
I2P will attempt to guess your IP address by having whomever it talks to tell it what
|
||||
address they think you are. If and only if you have no working TCP connections and you
|
||||
have not overridden the IP address, your router will believe them. If that doesn't sound
|
||||
ok to you, thats fine - go to the <a href="/configadvanced.jsp">advanced config</a> page
|
||||
ok to you, thats fine - go to the <a href="configadvanced.jsp">advanced config</a> page
|
||||
and add "i2np.tcp.hostname=yourHostname", then go to the
|
||||
<a href="/configservice.jsp">service</a> page and do a graceful restart. We used to make
|
||||
<a href="configservice.jsp">service</a> page and do a graceful restart. We used to make
|
||||
people enter a hostname/IP address on this page, but too many people got it wrong ;)</p>
|
||||
|
||||
<p>The other advanced network option has to do with reseeding - you should never need to
|
||||
|
5
apps/routerconsole/jsp/dumpprofile.jsp
Normal file
5
apps/routerconsole/jsp/dumpprofile.jsp
Normal file
@ -0,0 +1,5 @@
|
||||
<%@page contentType="text/plain"
|
||||
%><jsp:useBean id="helper" class="net.i2p.router.web.StatHelper"
|
||||
/><jsp:setProperty name="helper" property="peer" value="<%=request.getParameter("peer")%>"
|
||||
/><jsp:setProperty name="helper" property="writer" value="<%=out%>"
|
||||
/><jsp:getProperty name="helper" property="profile" />
|
@ -33,7 +33,7 @@ by their binary code license. This product includes software developed by the A
|
||||
(http://www.apache.org/). </p>
|
||||
|
||||
<p>Another application you can see on this webpage is <a href="http://www.i2p.net/i2ptunnel">I2PTunnel</a>
|
||||
(your <a href="/i2ptunnel/" target="_blank">web interface</a>) - a GPL'ed application written by mihi that
|
||||
(your <a href="i2ptunnel/" target="_blank">web interface</a>) - a GPL'ed application written by mihi that
|
||||
lets you tunnel normal TCP/IP traffic over I2P (such as the eepproxy and the irc proxy).</p>
|
||||
|
||||
<p>The router by default also includes human's public domain <a href="http://www.i2p.net/sam">SAM</a> bridge,
|
||||
|
@ -23,7 +23,7 @@
|
||||
if (helper.getActivePeers() <= 0) {
|
||||
%><b><a href="config.jsp">check your NAT/firewall</a></b><br /><%
|
||||
}
|
||||
if (helper.getActiveProfiles() <= 4) { // 4 is the min fallback
|
||||
if (helper.getActiveProfiles() <= 10) { // 10 is the min fallback
|
||||
if ("true".equals(System.getProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "false"))) {
|
||||
out.print(" <i>reseeding</i>");
|
||||
} else {
|
||||
|
@ -21,9 +21,9 @@ SRCDIR = src
|
||||
# Programs
|
||||
#
|
||||
|
||||
AR = C:\Dev-Cpp\bin\ar
|
||||
CC = C:\Dev-Cpp\bin\gcc
|
||||
RM = C:\Dev-Cpp\bin\rm
|
||||
AR = C:\MinGW\bin\ar
|
||||
CC = C:\MinGW\bin\gcc
|
||||
RM = C:\MinGW\bin\rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
|
@ -1 +1 @@
|
||||
See the `docs' directory for documentation and license.
|
||||
See the `docs' directory for the documentation and license.
|
||||
|
@ -1,7 +0,0 @@
|
||||
If you would like to make a donation to the author of this library, you can use
|
||||
the following methods:
|
||||
|
||||
* E-Gold account number 1043280
|
||||
* Paypal email mpc@innographx.com
|
||||
|
||||
If you want to use some other method, just ask.
|
32
apps/sam/c/doc/install.txt
Normal file
32
apps/sam/c/doc/install.txt
Normal file
@ -0,0 +1,32 @@
|
||||
=====================
|
||||
How to Install LibSAM
|
||||
=====================
|
||||
|
||||
1) Be sure you have GNU Make installed.
|
||||
|
||||
2) Find the Makefile for your operating system in the LibSAM root. For example,
|
||||
if you are on FreeBSD, you'd use Makefile.freebsd.
|
||||
|
||||
3) Run gmake with the -f option to build LibSAM. For example, on FreeBSD, you'd
|
||||
run "gmake -f Makefile.freebsd". On Linux, GNU Make is just called 'make', so
|
||||
you'd run "make -f Makefile.linux".
|
||||
|
||||
4) If that worked, you can now try to compile some of the example programs.
|
||||
They are compiled in the same way as LibSAM, but the Makefile names are
|
||||
different.
|
||||
|
||||
I2P-Ping should compile on any Unix system using the default Makefile. It won't
|
||||
work on Win32, however, because Win32 doesn't have the getopt() function.
|
||||
|
||||
The Warhammer example should run on any OS. Use the Makefile.posix for Unix
|
||||
sytems or the Makefile.mingw for Win32.
|
||||
|
||||
*** If you have trouble ***
|
||||
|
||||
If you have trouble compiling LibSAM, try to edit the Makefiles to fix the
|
||||
problem. The "Makefile.common" is shared by all systems, and you shouldn't have
|
||||
to touch it. Just copy the Makefile of the OS most similar to your own and
|
||||
start hacking on it. Send me a patch if you get it working.
|
||||
|
||||
I realise this build system is horrible, and in the future I will probably
|
||||
replace it entirely.
|
@ -1,10 +1,10 @@
|
||||
I need to do these things:
|
||||
|
||||
* SAM raw support
|
||||
* SAM raw support (partially complete)
|
||||
* Write an instruction manual
|
||||
* Make dest a dynamic string
|
||||
* Change SAM parser to use a hashmap
|
||||
* Switch to GNU Autoconf (?)
|
||||
* Improve build system
|
||||
|
||||
Anyone can help with these things:
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
/* vi:set ts=4: */
|
||||
|
||||
v1.30
|
||||
* Added session to sam_namingback()
|
||||
* Removed stdint.h dependency
|
||||
* Improved WIRETAP to do more logging
|
||||
* Added "pinger.sh" shell script example for using i2p-ping
|
||||
* Added SAM_BAD_STYLE error
|
||||
|
@ -60,7 +60,8 @@ static void databack(sam_sess_t *session, sam_sid_t stream_id, void *data,
|
||||
size_t size);
|
||||
static void diedback(sam_sess_t *session);
|
||||
static void logback(char *s);
|
||||
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result);
|
||||
static void namingback(sam_sess_t *session, char *name, sam_pubkey_t pubkey,
|
||||
samerr_t result);
|
||||
static void statusback(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result);
|
||||
|
||||
@ -78,7 +79,7 @@ int main(int argc, char *argv[])
|
||||
int count = INT_MAX; /* number of times to ping */
|
||||
int pongcount = -1;
|
||||
char *samhost = "localhost";
|
||||
uint16_t samport = 7656;
|
||||
unsigned short samport = 7656;
|
||||
|
||||
while ((ch = getopt(argc, argv, "ac:h:mp:qv")) != -1) {
|
||||
switch (ch) {
|
||||
@ -103,7 +104,7 @@ int main(int argc, char *argv[])
|
||||
quiet = true;
|
||||
break;
|
||||
case 'v': /* version */
|
||||
puts("$Id: i2p-ping.c,v 1.4 2004/09/22 20:05:40 jrandom Exp $");
|
||||
puts("$Id: i2p-ping.c,v 1.6 2004/12/02 17:54:23 mpc Exp $");
|
||||
puts("Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>");
|
||||
break;
|
||||
case '?':
|
||||
@ -140,7 +141,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
pongcount = 0;
|
||||
for (int j = 0; j < argc; j++) {
|
||||
if (strlen(argv[j]) == 516) {
|
||||
if (strlen(argv[j]) == SAM_PUBKEY_LEN - 1) {
|
||||
memcpy(dest, argv[j], SAM_PUBKEY_LEN);
|
||||
gotdest = true;
|
||||
} else
|
||||
@ -242,7 +243,8 @@ static void logback(char *s)
|
||||
* This is really hackish, but we know that we are only doing one lookup, so
|
||||
* what the hell
|
||||
*/
|
||||
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result)
|
||||
static void namingback(sam_sess_t *session, char *name, sam_pubkey_t pubkey,
|
||||
samerr_t result)
|
||||
{
|
||||
if (result != SAM_OK) {
|
||||
fprintf(stderr, "Naming lookup failed: %s\n", sam_strerror(result));
|
||||
|
@ -6,8 +6,8 @@
|
||||
# Programs
|
||||
#
|
||||
|
||||
CC = C:\Dev-Cpp\bin\gcc
|
||||
RM = C:\Dev-Cpp\bin\rm
|
||||
CC = C:\MinGW\bin\gcc
|
||||
RM = C:\MinGW\bin\rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
|
@ -47,7 +47,8 @@ static void dgramback(sam_sess_t *session, sam_pubkey_t dest, void *data,
|
||||
size_t size);
|
||||
static void diedback(sam_sess_t *session);
|
||||
static void logback(char *s);
|
||||
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result);
|
||||
static void namingback(sam_sess_t *session, char *name, sam_pubkey_t pubkey,
|
||||
samerr_t result);
|
||||
|
||||
/*
|
||||
* Just some ugly global variables. Don't do this in your program.
|
||||
@ -88,12 +89,12 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether they've supplied a name or a base 64 destination
|
||||
* Check whether they've supplied a hostname or a base 64 destination
|
||||
*
|
||||
* Note that this is a hack. Jrandom says that once certificates are added,
|
||||
* the length could be different depending on the certificate's size.
|
||||
*/
|
||||
if (strlen(argv[1]) == 516) {
|
||||
if (strlen(argv[1]) == SAM_PUBKEY_LEN - 1) {
|
||||
memcpy(dest, argv[1], SAM_PUBKEY_LEN);
|
||||
gotdest = true;
|
||||
} else {
|
||||
@ -155,7 +156,6 @@ static void dgramback(sam_sess_t *session, sam_pubkey_t dest, void *data,
|
||||
static void diedback(sam_sess_t *session)
|
||||
{
|
||||
fprintf(stderr, "Lost SAM connection!\n");
|
||||
/* high quality code would do a sam_session_free() here */
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -172,11 +172,11 @@ static void logback(char *s)
|
||||
* This is really hackish, but we know that we are only doing one lookup, so
|
||||
* what the hell
|
||||
*/
|
||||
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result)
|
||||
static void namingback(sam_sess_t *session, char *name, sam_pubkey_t pubkey,
|
||||
samerr_t result)
|
||||
{
|
||||
if (result != SAM_OK) {
|
||||
fprintf(stderr, "Naming lookup failed: %s\n", sam_strerror(result));
|
||||
/* high quality code would do a sam_session_free() here */
|
||||
exit(1);
|
||||
}
|
||||
memcpy(dest, pubkey, SAM_PUBKEY_LEN);
|
||||
|
@ -28,36 +28,19 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef PLATFORM_H
|
||||
#define PLATFORM_H
|
||||
#ifndef LIBSAM_PLATFORM_H
|
||||
#define LIBSAM_PLATFORM_H
|
||||
|
||||
/*
|
||||
* Operating system
|
||||
*/
|
||||
#define FREEBSD 0 // FreeBSD
|
||||
#define MINGW 1 // Windows native (Mingw)
|
||||
#define CYGWIN 1 // Cygwin
|
||||
#define LINUX 2 // Linux
|
||||
#define CYGWIN 3 // Cygwin
|
||||
|
||||
#if OS == MINGW
|
||||
#define INET_ADDRSTRLEN 16
|
||||
#define NO_GETHOSTBYNAME2
|
||||
#define NO_INET_ATON /* implies NO_INET_PTON */
|
||||
#define NO_INET_NTOP
|
||||
#define NO_SSIZE_T
|
||||
#define NO_STRL
|
||||
#define NO_Z_FORMAT
|
||||
#define WINSOCK
|
||||
#endif
|
||||
|
||||
#if OS == LINUX
|
||||
#define NO_GETHOSTBYNAME2
|
||||
#define NO_STRL
|
||||
#define NO_Z_FORMAT
|
||||
#endif
|
||||
#define MINGW 3 // Windows native (Mingw)
|
||||
#define MSVC 4 // Windows native (Visual C++ 2003)
|
||||
|
||||
#if OS == CYGWIN
|
||||
#define FAST32_IS_LONG
|
||||
#define INET_ADDRSTRLEN 16
|
||||
#define NO_GETHOSTBYNAME2
|
||||
#define NO_INET_NTOP
|
||||
@ -68,13 +51,29 @@
|
||||
#define NO_Z_FORMAT
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Standard C99 includes - if your compiler doesn't have these, it's time to
|
||||
* upgrade
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#if OS == LINUX
|
||||
#define NO_GETHOSTBYNAME2
|
||||
#define NO_STRL
|
||||
#define NO_Z_FORMAT
|
||||
#endif
|
||||
|
||||
#if OS == MINGW
|
||||
#define INET_ADDRSTRLEN 16
|
||||
#define NO_GETHOSTBYNAME2
|
||||
#define NO_INET_ATON // implies NO_INET_PTON
|
||||
#define NO_INET_NTOP
|
||||
#define NO_SSIZE_T
|
||||
#define NO_STRL
|
||||
#define NO_Z_FORMAT
|
||||
#define WINSOCK
|
||||
#endif
|
||||
|
||||
#if OS == MSVC // FIXME: doesn't work
|
||||
#define NO_STDBOOL_H
|
||||
#define NO_SSIZE_T
|
||||
#define NO_STRL
|
||||
#define WINSOCK
|
||||
#endif
|
||||
|
||||
/*
|
||||
* System includes
|
||||
@ -116,6 +115,13 @@
|
||||
typedef signed long ssize_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* I'm too lazy to type "unsigned"
|
||||
*/
|
||||
typedef unsigned char byte;
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned short ushort;
|
||||
|
||||
/*
|
||||
* Prints out the file name, line number, and function name before log message
|
||||
*/
|
||||
@ -136,4 +142,4 @@
|
||||
#include <ctype.h>
|
||||
#endif
|
||||
|
||||
#endif /* PLATFORM_H */
|
||||
#endif /* LIBSAM_PLATFORM_H */
|
||||
|
@ -28,19 +28,26 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef SAM_H
|
||||
#define SAM_H
|
||||
#ifndef LIBSAM_SAM_H
|
||||
#define LIBSAM_SAM_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#ifdef NO_STDBOOL_H
|
||||
typedef int bool;
|
||||
#define true 0
|
||||
#define false 1
|
||||
#else
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
#include <stddef.h> // size_t
|
||||
|
||||
|
||||
/*
|
||||
* Lengths
|
||||
*/
|
||||
|
||||
/* The maximum length a SAM command can be */
|
||||
#define SAM_CMD_LEN 128
|
||||
/* The maximum size of a single datagram packet */
|
||||
@ -49,29 +56,21 @@ extern "C" {
|
||||
#define SAM_LOGMSG_LEN 256
|
||||
/* The longest `name' arg for the naming lookup callback */
|
||||
#define SAM_NAME_LEN 256
|
||||
/* The max size of a single stream packet */
|
||||
/* The maximum size of a single stream packet */
|
||||
#define SAM_STREAM_PAYLOAD_MAX (32 * 1024)
|
||||
/* The length of a base 64 public key - it's actually 516, but +1 for '\0' */
|
||||
#define SAM_PUBKEY_LEN 517
|
||||
/* A public key SAM command's length */
|
||||
/* The maximum length of a SAM command with a public key */
|
||||
#define SAM_PKCMD_LEN (SAM_PUBKEY_LEN + SAM_CMD_LEN)
|
||||
/* The maximum size of a single raw packet */
|
||||
#define SAM_RAW_PAYLOAD_MAX (32 * 1024)
|
||||
/* The maximum length a SAM non-data reply can be */
|
||||
#define SAM_REPLY_LEN 1024
|
||||
|
||||
/*
|
||||
* Shorten some standard variable types
|
||||
*/
|
||||
typedef signed char schar_t;
|
||||
typedef unsigned char uchar_t;
|
||||
typedef unsigned int uint_t;
|
||||
typedef unsigned long ulong_t;
|
||||
typedef unsigned short ushort_t;
|
||||
|
||||
#ifdef WINSOCK
|
||||
typedef SOCKET socket_t;
|
||||
#else
|
||||
typedef int socket_t;
|
||||
#endif
|
||||
/*
|
||||
* Some LibSAM variable types
|
||||
*/
|
||||
|
||||
typedef enum {SAM_STREAM, SAM_DGRAM, SAM_RAW} sam_conn_t; /* SAM connection */
|
||||
|
||||
@ -82,12 +81,13 @@ typedef struct {
|
||||
size_t size;
|
||||
} sam_sendq_t; /* sending queue to encourage large stream packet sizes */
|
||||
|
||||
typedef int_fast32_t sam_sid_t; /* stream id number */
|
||||
typedef long sam_sid_t; /* stream id number */
|
||||
|
||||
typedef struct {
|
||||
socket_t sock; /* the socket used for communications with SAM */
|
||||
int sock; /* the socket used for communications with SAM */
|
||||
bool connected; /* whether the socket is connected */
|
||||
sam_sid_t prev_id; /* the last stream id number we used */
|
||||
void *child; /* whatever you want it to be */
|
||||
} sam_sess_t; /* a SAM session */
|
||||
|
||||
typedef enum { /* see sam_strerror() for detailed descriptions of these */
|
||||
@ -102,59 +102,65 @@ typedef enum { /* see sam_strerror() for detailed descriptions of these */
|
||||
SAM_TOO_BIG
|
||||
} samerr_t;
|
||||
|
||||
|
||||
/*
|
||||
* Public functions
|
||||
*/
|
||||
|
||||
/* SAM controls - sessions */
|
||||
extern sam_sess_t *sam_session_init(sam_sess_t *session);
|
||||
extern void sam_session_free(sam_sess_t **session);
|
||||
sam_sess_t *sam_session_init(sam_sess_t *session);
|
||||
void sam_session_free(sam_sess_t **session);
|
||||
/* SAM controls - connection */
|
||||
extern bool sam_close(sam_sess_t *session);
|
||||
extern samerr_t sam_connect(sam_sess_t *session, const char *samhost,
|
||||
uint16_t samport, const char *destname, sam_conn_t style,
|
||||
uint_t tunneldepth);
|
||||
bool sam_close(sam_sess_t *session);
|
||||
samerr_t sam_connect(sam_sess_t *session, const char *samhost,
|
||||
unsigned short samport, const char *destname, sam_conn_t style,
|
||||
unsigned int tunneldepth);
|
||||
/* SAM controls - utilities */
|
||||
extern void sam_naming_lookup(sam_sess_t *session, const char *name);
|
||||
extern bool sam_read_buffer(sam_sess_t *session);
|
||||
extern const char *sam_strerror(samerr_t code);
|
||||
void sam_naming_lookup(sam_sess_t *session, const char *name);
|
||||
bool sam_read_buffer(sam_sess_t *session);
|
||||
const char *sam_strerror(samerr_t code);
|
||||
/* SAM controls - callbacks */
|
||||
extern void (*sam_diedback)(sam_sess_t *session);
|
||||
extern void (*sam_logback)(char *str);
|
||||
extern void (*sam_namingback)(char *name, sam_pubkey_t pubkey,
|
||||
samerr_t result);
|
||||
void (*sam_diedback)(sam_sess_t *session);
|
||||
void (*sam_logback)(char *str);
|
||||
void (*sam_namingback)(sam_sess_t *session, char *name,
|
||||
sam_pubkey_t pubkey, samerr_t result);
|
||||
|
||||
/* Stream commands */
|
||||
extern void sam_stream_close(sam_sess_t *session, sam_sid_t stream_id);
|
||||
extern sam_sid_t sam_stream_connect(sam_sess_t *session,
|
||||
const sam_pubkey_t dest);
|
||||
extern samerr_t sam_stream_send(sam_sess_t *session, sam_sid_t stream_id,
|
||||
const void *data, size_t size);
|
||||
void sam_stream_close(sam_sess_t *session, sam_sid_t stream_id);
|
||||
sam_sid_t sam_stream_connect(sam_sess_t *session, const sam_pubkey_t dest);
|
||||
samerr_t sam_stream_send(sam_sess_t *session, sam_sid_t stream_id,
|
||||
const void *data, size_t size);
|
||||
/* Stream commands - callbacks */
|
||||
extern void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t reason);
|
||||
extern void (*sam_connectback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_pubkey_t dest);
|
||||
extern void (*sam_databack)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
void *data, size_t size);
|
||||
extern void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result);
|
||||
void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t reason);
|
||||
void (*sam_connectback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_pubkey_t dest);
|
||||
void (*sam_databack)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
void *data, size_t size);
|
||||
void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result);
|
||||
|
||||
/* Stream send queue (experimental) */
|
||||
extern void sam_sendq_add(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_sendq_t **sendq, const void *data, size_t dsize);
|
||||
extern void sam_sendq_flush(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_sendq_t **sendq);
|
||||
void sam_sendq_add(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_sendq_t **sendq, const void *data, size_t dsize);
|
||||
void sam_sendq_flush(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_sendq_t **sendq);
|
||||
|
||||
/* Datagram commands */
|
||||
extern samerr_t sam_dgram_send(sam_sess_t *session, const sam_pubkey_t dest,
|
||||
const void *data, size_t size);
|
||||
|
||||
samerr_t sam_dgram_send(sam_sess_t *session, const sam_pubkey_t dest,
|
||||
const void *data, size_t size);
|
||||
/* Datagram commands - callbacks */
|
||||
extern void (*sam_dgramback)(sam_sess_t *session, sam_pubkey_t dest,
|
||||
void *data, size_t size);
|
||||
void (*sam_dgramback)(sam_sess_t *session, sam_pubkey_t dest, void *data,
|
||||
size_t size);
|
||||
|
||||
/* Raw commands */
|
||||
samerr_t sam_raw_send(sam_sess_t *session, const sam_pubkey_t dest,
|
||||
const void *data, size_t size);
|
||||
/* Raw commands - callbacks */
|
||||
void (*sam_rawback)(sam_sess_t *session, void *data, size_t size);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* SAM_H */
|
||||
#endif /* LIBSAM_SAM_H */
|
||||
|
@ -33,8 +33,8 @@
|
||||
* snprintf.c)
|
||||
*/
|
||||
|
||||
#ifndef SNPRINTF_H
|
||||
#define SNPRINTF_H
|
||||
#ifndef LIBSAM_SNPRINTF_H
|
||||
#define LIBSAM_SNPRINTF_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -46,4 +46,4 @@ int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* SNPRINTF_H */
|
||||
#endif /* LIBSAM_SNPRINTF_H */
|
||||
|
@ -32,8 +32,8 @@
|
||||
* Note: The strl.c file retains its original license (at the top of strl.c)
|
||||
*/
|
||||
|
||||
#ifndef STRL_H
|
||||
#define STRL_H
|
||||
#ifndef LIBSAM_STRL_H
|
||||
#define LIBSAM_STRL_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -44,4 +44,4 @@ extern size_t strlcpy(char *dst, const char *src, size_t siz);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* STRL_H */
|
||||
#endif /* LIBSAM_STRL_H */
|
||||
|
@ -28,8 +28,8 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "platform.h"
|
||||
#include "sam.h"
|
||||
#include "platform.h"
|
||||
|
||||
static bool sam_hello(sam_sess_t *session);
|
||||
static void sam_log(const char *format, ...);
|
||||
@ -40,9 +40,9 @@ static bool sam_readable(sam_sess_t *session);
|
||||
static sam_sendq_t *sam_sendq_create();
|
||||
static samerr_t sam_session_create(sam_sess_t *session,
|
||||
const char *destname, sam_conn_t style,
|
||||
uint_t tunneldepth);
|
||||
uint tunneldepth);
|
||||
static bool sam_socket_connect(sam_sess_t *session, const char *host,
|
||||
uint16_t port);
|
||||
ushort port);
|
||||
static bool sam_socket_resolve(const char *hostname, char *ipaddr);
|
||||
#ifdef WINSOCK
|
||||
static samerr_t sam_winsock_cleanup();
|
||||
@ -55,28 +55,41 @@ static ssize_t sam_write(sam_sess_t *session, const void *buf, size_t n);
|
||||
* Callback functions
|
||||
* Note: if you add a new callback be sure to check for non-NULL in sam_connect
|
||||
*/
|
||||
|
||||
/* a peer closed the connection */
|
||||
void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id, samerr_t reason)
|
||||
= NULL;
|
||||
|
||||
/* a peer connected to us */
|
||||
void (*sam_connectback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_pubkey_t dest) = NULL;
|
||||
|
||||
/* a peer sent some stream data (`data' MUST be freed) */
|
||||
void (*sam_databack)(sam_sess_t *session, sam_sid_t stream_id, void *data,
|
||||
size_t size) = NULL;
|
||||
|
||||
/* a peer sent some datagram data (`data' MUST be freed) */
|
||||
void (*sam_dgramback)(sam_sess_t *session, sam_pubkey_t dest, void *data,
|
||||
size_t size) = NULL;
|
||||
|
||||
/* we lost the connection to the SAM host */
|
||||
void (*sam_diedback)(sam_sess_t *session) = NULL;
|
||||
|
||||
/* logging callback */
|
||||
void (*sam_logback)(char *str) = NULL;
|
||||
|
||||
/* naming lookup reply - `pubkey' will be NULL if `result' isn't SAM_OK */
|
||||
void (*sam_namingback)(char *name, sam_pubkey_t pubkey, samerr_t result) = NULL;
|
||||
void (*sam_namingback)(sam_sess_t *session, char *name, sam_pubkey_t pubkey,
|
||||
samerr_t result) = NULL;
|
||||
|
||||
/* our connection to a peer has completed */
|
||||
void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result) = NULL;
|
||||
|
||||
/* a peer sent some raw data (`data' MUST be freed) */
|
||||
void (*sam_rawback)(sam_sess_t *session, void *data, size_t size) = NULL;
|
||||
|
||||
|
||||
/*
|
||||
* Closes the connection to the SAM host
|
||||
*
|
||||
@ -134,8 +147,8 @@ bool sam_close(sam_sess_t *session)
|
||||
*
|
||||
* Returns: SAM error code. If SAM_OK, `session' will be ready for use.
|
||||
*/
|
||||
samerr_t sam_connect(sam_sess_t *session, const char *samhost, uint16_t samport,
|
||||
const char *destname, sam_conn_t style, uint_t tunneldepth)
|
||||
samerr_t sam_connect(sam_sess_t *session, const char *samhost, ushort samport,
|
||||
const char *destname, sam_conn_t style, uint tunneldepth)
|
||||
{
|
||||
assert(session != NULL);
|
||||
samerr_t rc;
|
||||
@ -155,7 +168,11 @@ samerr_t sam_connect(sam_sess_t *session, const char *samhost, uint16_t samport,
|
||||
return SAM_CALLBACKS_UNSET;
|
||||
}
|
||||
} else if (style == SAM_RAW) {
|
||||
abort(); /* not implemented yet */
|
||||
if (sam_diedback == NULL || sam_logback == NULL
|
||||
|| sam_namingback == NULL || sam_rawback == NULL) {
|
||||
SAMLOGS("Please set callback functions before connecting");
|
||||
return SAM_CALLBACKS_UNSET;
|
||||
}
|
||||
} else {
|
||||
SAMLOGS("Unknown connection style");
|
||||
return SAM_BAD_STYLE;
|
||||
@ -295,6 +312,7 @@ static void sam_parse(sam_sess_t *session, char *s)
|
||||
#define SAM_NAMING_REPLY_OK "NAMING REPLY RESULT=OK"
|
||||
#define SAM_NAMING_REPLY_IK "NAMING REPLY RESULT=INVALID_KEY"
|
||||
#define SAM_NAMING_REPLY_KNF "NAMING REPLY RESULT=KEY_NOT_FOUND"
|
||||
#define SAM_RAW_RECEIVED_REPLY "RAW RECEIVED"
|
||||
#define SAM_STREAM_CLOSED_REPLY "STREAM CLOSED"
|
||||
#define SAM_STREAM_CONNECTED_REPLY "STREAM CONNECTED"
|
||||
#define SAM_STREAM_RECEIVED_REPLY "STREAM RECEIVED"
|
||||
@ -305,6 +323,10 @@ static void sam_parse(sam_sess_t *session, char *s)
|
||||
#define SAM_STREAM_STATUS_REPLY_IK "STREAM STATUS RESULT=INVALID_KEY"
|
||||
#define SAM_STREAM_STATUS_REPLY_TO "STREAM STATUS RESULT=TIMEOUT"
|
||||
|
||||
/*
|
||||
* TODO: add raw parsing
|
||||
*/
|
||||
|
||||
if (strncmp(s, SAM_DGRAM_RECEIVED_REPLY,
|
||||
strlen(SAM_DGRAM_RECEIVED_REPLY)) == 0) {
|
||||
char *p;
|
||||
@ -365,7 +387,7 @@ static void sam_parse(sam_sess_t *session, char *s)
|
||||
q++;
|
||||
strlcpy(name, p, sizeof name);
|
||||
strlcpy(pubkey, q, sizeof pubkey);
|
||||
sam_namingback(name, pubkey, SAM_OK);
|
||||
sam_namingback(session, name, pubkey, SAM_OK);
|
||||
|
||||
} else if (strncmp(s, SAM_NAMING_REPLY_IK,
|
||||
strlen(SAM_NAMING_REPLY_IK)) == 0) {
|
||||
@ -373,7 +395,7 @@ static void sam_parse(sam_sess_t *session, char *s)
|
||||
if (q != NULL)
|
||||
*q = '\0';
|
||||
strlcpy(name, p, sizeof name);
|
||||
sam_namingback(name, NULL, SAM_INVALID_KEY);
|
||||
sam_namingback(session, name, NULL, SAM_INVALID_KEY);
|
||||
|
||||
} else if (strncmp(s, SAM_NAMING_REPLY_KNF,
|
||||
strlen(SAM_NAMING_REPLY_KNF)) == 0) {
|
||||
@ -381,14 +403,14 @@ static void sam_parse(sam_sess_t *session, char *s)
|
||||
if (q != NULL)
|
||||
*q = '\0';
|
||||
strlcpy(name, p, sizeof name);
|
||||
sam_namingback(name, NULL, SAM_KEY_NOT_FOUND);
|
||||
sam_namingback(session, name, NULL, SAM_KEY_NOT_FOUND);
|
||||
|
||||
} else {
|
||||
q = strchr(p, ' '); /* ' 'MES.. (optional) */
|
||||
if (q != NULL)
|
||||
*q = '\0';
|
||||
strlcpy(name, p, sizeof name);
|
||||
sam_namingback(name, NULL, SAM_UNKNOWN);
|
||||
sam_namingback(session, name, NULL, SAM_UNKNOWN);
|
||||
}
|
||||
|
||||
return;
|
||||
@ -518,6 +540,42 @@ static void sam_parse(sam_sess_t *session, char *s)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends data to a destination in a raw packet
|
||||
*
|
||||
* dest - base 64 destination of who we're sending to
|
||||
* data - the data we're sending
|
||||
* size - the size of the data
|
||||
*
|
||||
* Returns: SAM_OK on success
|
||||
*/
|
||||
samerr_t sam_raw_send(sam_sess_t *session, const sam_pubkey_t dest,
|
||||
const void *data, size_t size)
|
||||
{
|
||||
assert(session != NULL);
|
||||
char cmd[SAM_PKCMD_LEN];
|
||||
|
||||
if (size < 1 || size > SAM_RAW_PAYLOAD_MAX) {
|
||||
#ifdef NO_Z_FORMAT
|
||||
SAMLOG("Invalid data send size (%u bytes)", size);
|
||||
#else
|
||||
SAMLOG("Invalid data send size (%zu bytes)", size);
|
||||
#endif
|
||||
return SAM_TOO_BIG;
|
||||
}
|
||||
#ifdef NO_Z_FORMAT
|
||||
snprintf(cmd, sizeof cmd, "RAW SEND DESTINATION=%s SIZE=%u\n",
|
||||
dest, size);
|
||||
#else
|
||||
snprintf(cmd, sizeof cmd, "RAW SEND DESTINATION=%s SIZE=%zu\n",
|
||||
dest, size);
|
||||
#endif
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
sam_write(session, data, size);
|
||||
|
||||
return SAM_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads and callbacks everything in the SAM network buffer until it is clear
|
||||
*
|
||||
@ -666,10 +724,10 @@ static ssize_t sam_read2(sam_sess_t *session, void *buf, size_t n)
|
||||
p = buf;
|
||||
printf("*RR* ");
|
||||
for (size_t x = 0; x < n; x++) {
|
||||
if (isprint(((uchar_t*)p)[x]))
|
||||
printf("%c,", ((uchar_t*)p)[x]);
|
||||
if (isprint(((byte*)p)[x]))
|
||||
printf("%c,", ((byte*)p)[x]);
|
||||
else
|
||||
printf("%03d,", ((uint8_t*)p)[x]);
|
||||
printf("%03d,", ((byte*)p)[x]);
|
||||
}
|
||||
printf("\n");
|
||||
printf("*RR* (read2() read %d bytes)\n", n);
|
||||
@ -820,7 +878,7 @@ void sam_sendq_flush(sam_sess_t *session, sam_sid_t stream_id,
|
||||
* Returns: SAM error code
|
||||
*/
|
||||
static samerr_t sam_session_create(sam_sess_t *session, const char *destname,
|
||||
sam_conn_t style, uint_t tunneldepth)
|
||||
sam_conn_t style, uint tunneldepth)
|
||||
{
|
||||
assert(session != NULL);
|
||||
#define SAM_SESSTATUS_REPLY_OK "SESSION STATUS RESULT=OK"
|
||||
@ -879,6 +937,7 @@ sam_sess_t *sam_session_init(sam_sess_t *session)
|
||||
SAMLOGS("Out of memory");
|
||||
abort();
|
||||
}
|
||||
session->child = NULL;
|
||||
}
|
||||
session->connected = false;
|
||||
session->prev_id = 0;
|
||||
@ -906,7 +965,7 @@ void sam_session_free(sam_sess_t **session)
|
||||
*
|
||||
* Returns: true on sucess, false on error, with errno set
|
||||
*/
|
||||
bool sam_socket_connect(sam_sess_t *session, const char *host, uint16_t port)
|
||||
bool sam_socket_connect(sam_sess_t *session, const char *host, ushort port)
|
||||
{
|
||||
assert(session != NULL);
|
||||
struct sockaddr_in hostaddr;
|
||||
@ -1023,11 +1082,7 @@ void sam_stream_close(sam_sess_t *session, sam_sid_t stream_id)
|
||||
assert(session != NULL);
|
||||
char cmd[SAM_CMD_LEN];
|
||||
|
||||
#ifdef FAST32_IS_LONG
|
||||
snprintf(cmd, sizeof cmd, "STREAM CLOSE ID=%ld\n", stream_id);
|
||||
#else
|
||||
snprintf(cmd, sizeof cmd, "STREAM CLOSE ID=%d\n", stream_id);
|
||||
#endif
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
|
||||
return;
|
||||
@ -1046,13 +1101,8 @@ sam_sid_t sam_stream_connect(sam_sess_t *session, const sam_pubkey_t dest)
|
||||
char cmd[SAM_PKCMD_LEN];
|
||||
|
||||
session->prev_id++; /* increment the id for the connection */
|
||||
#ifdef FAST32_IS_LONG
|
||||
snprintf(cmd, sizeof cmd, "STREAM CONNECT ID=%ld DESTINATION=%s\n",
|
||||
session->prev_id, dest);
|
||||
#else
|
||||
snprintf(cmd, sizeof cmd, "STREAM CONNECT ID=%d DESTINATION=%s\n",
|
||||
session->prev_id, dest);
|
||||
#endif
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
|
||||
return session->prev_id;
|
||||
@ -1084,15 +1134,9 @@ samerr_t sam_stream_send(sam_sess_t *session, sam_sid_t stream_id,
|
||||
return SAM_TOO_BIG;
|
||||
}
|
||||
#ifdef NO_Z_FORMAT
|
||||
#ifdef FAST32_IS_LONG
|
||||
snprintf(cmd, sizeof cmd, "STREAM SEND ID=%ld SIZE=%u\n",
|
||||
stream_id, size);
|
||||
#else
|
||||
snprintf(cmd, sizeof cmd, "STREAM SEND ID=%d SIZE=%u\n",
|
||||
stream_id, size);
|
||||
#endif
|
||||
snprintf(cmd, sizeof cmd, "STREAM SEND ID=%ld SIZE=%u\n", stream_id, size);
|
||||
#else
|
||||
snprintf(cmd, sizeof cmd, "STREAM SEND ID=%d SIZE=%zu\n",
|
||||
snprintf(cmd, sizeof cmd, "STREAM SEND ID=%ld SIZE=%zu\n",
|
||||
stream_id, size);
|
||||
#endif
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
@ -1342,7 +1386,7 @@ static ssize_t sam_write(sam_sess_t *session, const void *buf, size_t n)
|
||||
return -1;
|
||||
}
|
||||
#if SAM_WIRETAP
|
||||
const uchar_t *cp = buf;
|
||||
const byte *cp = buf;
|
||||
printf("*WW* ");
|
||||
for (size_t x = 0; x < n; x++) {
|
||||
if (isprint(cp[x]))
|
||||
|
@ -10,7 +10,7 @@
|
||||
<mkdir dir="./build" />
|
||||
<mkdir dir="./build/obj" />
|
||||
<javac
|
||||
srcdir="./src"
|
||||
srcdir="./src:./test"
|
||||
debug="true" deprecation="on" source="1.3" target="1.3"
|
||||
destdir="./build/obj"
|
||||
classpath="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar" />
|
||||
|
@ -220,6 +220,15 @@ public class SAMBridge implements Runnable {
|
||||
}
|
||||
SAMBridge bridge = new SAMBridge(host, port, opts, keyfile);
|
||||
I2PThread t = new I2PThread(bridge, "SAMListener");
|
||||
if (Boolean.valueOf(System.getProperty("sam.shutdownOnOOM", "false")).booleanValue()) {
|
||||
t.addOOMEventListener(new I2PThread.OOMEventListener() {
|
||||
public void outOfMemory(OutOfMemoryError err) {
|
||||
err.printStackTrace();
|
||||
System.err.println("OOMed, die die die");
|
||||
System.exit(-1);
|
||||
}
|
||||
});
|
||||
}
|
||||
t.start();
|
||||
}
|
||||
|
||||
|
@ -119,6 +119,8 @@ public abstract class SAMHandler implements Runnable {
|
||||
* @return True is the string was successfully written, false otherwise
|
||||
*/
|
||||
protected final boolean writeString(String str) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Sending the client: [" + str + "]");
|
||||
try {
|
||||
writeBytes(str.getBytes("ISO-8859-1"));
|
||||
} catch (IOException e) {
|
||||
|
@ -17,6 +17,7 @@ import java.net.Socket;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
@ -36,14 +37,11 @@ public class SAMHandlerFactory {
|
||||
* @return A SAM protocol handler, or null if the client closed before the handshake
|
||||
*/
|
||||
public static SAMHandler createSAMHandler(Socket s, Properties i2cpProps) throws SAMException {
|
||||
BufferedReader br;
|
||||
String line;
|
||||
StringTokenizer tok;
|
||||
|
||||
try {
|
||||
br = new BufferedReader(new InputStreamReader(s.getInputStream(),
|
||||
"ISO-8859-1"));
|
||||
line = br.readLine();
|
||||
line = DataHelper.readLine(s.getInputStream());
|
||||
if (line == null) {
|
||||
_log.debug("Connection closed by client");
|
||||
return null;
|
||||
|
@ -15,8 +15,10 @@ import java.io.InterruptedIOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ConnectException;
|
||||
import java.net.NoRouteToHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
@ -28,8 +30,11 @@ import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.client.streaming.I2PSocketManagerFactory;
|
||||
import net.i2p.client.streaming.I2PSocketOptions;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.ByteCache;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@ -51,7 +56,10 @@ public class SAMStreamSession {
|
||||
private I2PSocketManager socketMgr = null;
|
||||
|
||||
private Object handlersMapLock = new Object();
|
||||
/** stream id (Long) to SAMStreamSessionSocketReader */
|
||||
private HashMap handlersMap = new HashMap();
|
||||
/** stream id (Long) to StreamSender */
|
||||
private HashMap sendersMap = new HashMap();
|
||||
|
||||
private Object idLock = new Object();
|
||||
private int lastNegativeId = 0;
|
||||
@ -59,6 +67,14 @@ public class SAMStreamSession {
|
||||
// Can we create outgoing connections?
|
||||
private boolean canCreate = false;
|
||||
|
||||
/**
|
||||
* should we flush every time we get a STREAM SEND, or leave that up to
|
||||
* the streaming lib to decide?
|
||||
*/
|
||||
private boolean forceFlush = false;
|
||||
public static String PROP_FORCE_FLUSH = "sam.forceFlush";
|
||||
public static String DEFAULT_FORCE_FLUSH = "false";
|
||||
|
||||
/**
|
||||
* Create a new SAM STREAM session.
|
||||
*
|
||||
@ -107,9 +123,6 @@ public class SAMStreamSession {
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new SAMException("Invalid I2CP port specified [" + port + "]");
|
||||
}
|
||||
// streams MUST be mode=guaranteed (though i think the socket manager
|
||||
// enforces this anyway...
|
||||
allprops.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED);
|
||||
|
||||
_log.debug("Creating I2PSocketManager...");
|
||||
socketMgr = I2PSocketManagerFactory.createManager(destStream,
|
||||
@ -120,6 +133,8 @@ public class SAMStreamSession {
|
||||
throw new SAMException("Error creating I2PSocketManager");
|
||||
}
|
||||
|
||||
forceFlush = Boolean.valueOf(allprops.getProperty(PROP_FORCE_FLUSH, DEFAULT_FORCE_FLUSH)).booleanValue();
|
||||
|
||||
boolean canReceive = false;
|
||||
if (dir.equals("BOTH")) {
|
||||
canCreate = true;
|
||||
@ -197,18 +212,25 @@ public class SAMStreamSession {
|
||||
*
|
||||
* @param data Bytes to be sent
|
||||
*
|
||||
* @return True if the data was sent, false otherwise
|
||||
* @return True if the data was queued for sending, false otherwise
|
||||
*/
|
||||
public boolean sendBytes(int id, byte[] data) {
|
||||
Destination d = new Destination();
|
||||
SAMStreamSessionSocketHandler handler = getSocketHandler(id);
|
||||
public boolean sendBytes(int id, InputStream in, int size) throws IOException {
|
||||
StreamSender sender = getSender(id);
|
||||
|
||||
if (handler == null) {
|
||||
_log.error("Trying to send bytes through inexistent handler " +id);
|
||||
if (sender == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Trying to send bytes through nonexistent handler " +id);
|
||||
// even though it failed, we need to read those bytes!
|
||||
for (int i = 0; i < size; i++) {
|
||||
int c = in.read();
|
||||
if (c == -1)
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return handler.sendBytes(data);
|
||||
sender.sendBytes(in, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -248,13 +270,15 @@ public class SAMStreamSession {
|
||||
* @return An id associated to the socket handler
|
||||
*/
|
||||
private int createSocketHandler(I2PSocket s, int id) {
|
||||
SAMStreamSessionSocketHandler handler;
|
||||
SAMStreamSessionSocketReader reader = null;
|
||||
StreamSender sender = null;
|
||||
if (id == 0) {
|
||||
id = createUniqueId();
|
||||
}
|
||||
|
||||
try {
|
||||
handler = new SAMStreamSessionSocketHandler(s, id);
|
||||
reader = new SAMStreamSessionSocketReader(s, id);
|
||||
sender = new StreamSender(s, id);
|
||||
} catch (IOException e) {
|
||||
_log.error("IOException when creating SAM STREAM session socket handler", e);
|
||||
recv.stopStreamReceiving();
|
||||
@ -262,10 +286,13 @@ public class SAMStreamSession {
|
||||
}
|
||||
|
||||
synchronized (handlersMapLock) {
|
||||
handlersMap.put(new Integer(id), handler);
|
||||
handlersMap.put(new Integer(id), reader);
|
||||
sendersMap.put(new Integer(id), sender);
|
||||
}
|
||||
|
||||
I2PThread t = new I2PThread(handler, "SAMStreamSessionSocketHandler");
|
||||
I2PThread t = new I2PThread(reader, "SAMReader" + id);
|
||||
t.start();
|
||||
t = new I2PThread(sender, "SAMSender" + id);
|
||||
t.start();
|
||||
|
||||
return id;
|
||||
@ -283,9 +310,14 @@ public class SAMStreamSession {
|
||||
*
|
||||
* @param id Handler id
|
||||
*/
|
||||
private SAMStreamSessionSocketHandler getSocketHandler(int id) {
|
||||
private SAMStreamSessionSocketReader getSocketReader(int id) {
|
||||
synchronized (handlersMapLock) {
|
||||
return (SAMStreamSessionSocketHandler)handlersMap.get(new Integer(id));
|
||||
return (SAMStreamSessionSocketReader)handlersMap.get(new Integer(id));
|
||||
}
|
||||
}
|
||||
private StreamSender getSender(int id) {
|
||||
synchronized (handlersMapLock) {
|
||||
return (StreamSender)sendersMap.get(new Integer(id));
|
||||
}
|
||||
}
|
||||
|
||||
@ -306,19 +338,19 @@ public class SAMStreamSession {
|
||||
* @param id Handler id to be removed
|
||||
*/
|
||||
private void removeSocketHandler(int id) {
|
||||
SAMStreamSessionSocketHandler removed;
|
||||
SAMStreamSessionSocketReader reader = null;
|
||||
StreamSender sender = null;
|
||||
|
||||
synchronized (handlersMapLock) {
|
||||
removed = (SAMStreamSessionSocketHandler)handlersMap.remove(new Integer(id));
|
||||
reader = (SAMStreamSessionSocketReader)handlersMap.remove(new Integer(id));
|
||||
sender = (StreamSender)sendersMap.remove(new Integer(id));
|
||||
}
|
||||
|
||||
if (removed == null) {
|
||||
_log.error("BUG! Trying to remove inexistent SAM STREAM session socket handler " + id);
|
||||
recv.stopStreamReceiving();
|
||||
} else {
|
||||
removed.stopRunning();
|
||||
_log.debug("Removed SAM STREAM session socket handler " + id);
|
||||
}
|
||||
if (reader != null)
|
||||
reader.stopRunning();
|
||||
if (sender != null)
|
||||
sender.stopRunning();
|
||||
_log.debug("Removed SAM STREAM session socket handler " + id);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -337,9 +369,11 @@ public class SAMStreamSession {
|
||||
|
||||
while (iter.hasNext()) {
|
||||
id = (Integer)iter.next();
|
||||
((SAMStreamSessionSocketHandler)handlersMap.get(id)).stopRunning();
|
||||
((SAMStreamSessionSocketReader)handlersMap.get(id)).stopRunning();
|
||||
((StreamSender)sendersMap.get(id)).stopRunning();
|
||||
}
|
||||
handlersMap.clear();
|
||||
sendersMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@ -391,6 +425,8 @@ public class SAMStreamSession {
|
||||
while (stillRunning) {
|
||||
try {
|
||||
i2ps = serverSocket.accept();
|
||||
if (i2ps == null)
|
||||
break;
|
||||
|
||||
_log.debug("New incoming connection");
|
||||
|
||||
@ -417,6 +453,8 @@ public class SAMStreamSession {
|
||||
} catch (I2PException e) {
|
||||
_log.debug("Caught I2PException", e);
|
||||
}
|
||||
|
||||
close();
|
||||
|
||||
_log.debug("Shutting down SAM STREAM session server");
|
||||
}
|
||||
@ -429,10 +467,9 @@ public class SAMStreamSession {
|
||||
*
|
||||
* @author human
|
||||
*/
|
||||
public class SAMStreamSessionSocketHandler implements Runnable {
|
||||
public class SAMStreamSessionSocketReader implements Runnable {
|
||||
|
||||
private I2PSocket i2pSocket = null;
|
||||
private OutputStream i2pSocketOS = null;
|
||||
|
||||
private Object runningLock = new Object();
|
||||
private boolean stillRunning = true;
|
||||
@ -440,43 +477,20 @@ public class SAMStreamSession {
|
||||
private int id;
|
||||
|
||||
/**
|
||||
* Create a new SAM STREAM session socket handler
|
||||
* Create a new SAM STREAM session socket reader
|
||||
*
|
||||
* @param s Socket to be handled
|
||||
* @param id Unique id assigned to the handler
|
||||
*/
|
||||
public SAMStreamSessionSocketHandler(I2PSocket s, int id) throws IOException {
|
||||
public SAMStreamSessionSocketReader(I2PSocket s, int id) throws IOException {
|
||||
_log.debug("Instantiating new SAM STREAM session socket handler");
|
||||
|
||||
i2pSocket = s;
|
||||
i2pSocketOS = s.getOutputStream();
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send bytes through the SAM STREAM session socket handler
|
||||
*
|
||||
* @param data Data to be sent
|
||||
*
|
||||
* @return True if data has been sent without errors, false otherwise
|
||||
*/
|
||||
public boolean sendBytes(byte[] data) {
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug("Handler " + id + ": sending " + data.length
|
||||
+ " bytes");
|
||||
}
|
||||
try {
|
||||
i2pSocketOS.write(data);
|
||||
} catch (IOException e) {
|
||||
_log.error("Error sending data through I2P socket", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a SAM STREAM session socket handler
|
||||
* Stop a SAM STREAM session socket reader
|
||||
*
|
||||
*/
|
||||
public void stopRunning() {
|
||||
@ -535,4 +549,99 @@ public class SAMStreamSession {
|
||||
_log.debug("Shutting down SAM STREAM session socket handler " +id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lets us push data through the stream without blocking, (even after exceeding
|
||||
* the I2PSocket's buffer)
|
||||
*/
|
||||
private class StreamSender implements Runnable {
|
||||
private List _data;
|
||||
private int _id;
|
||||
private ByteCache _cache;
|
||||
private OutputStream _out = null;
|
||||
private boolean _stillRunning;
|
||||
|
||||
public StreamSender(I2PSocket s, int id) throws IOException {
|
||||
_data = new ArrayList(1);
|
||||
_id = id;
|
||||
_cache = ByteCache.getInstance(4, 32*1024);
|
||||
_out = s.getOutputStream();
|
||||
_stillRunning = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send bytes through the SAM STREAM session socket sender
|
||||
*
|
||||
* @param data Data to be sent
|
||||
*
|
||||
* @throws IOException if the client didnt provide enough data
|
||||
*/
|
||||
public void sendBytes(InputStream in, int size) throws IOException {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Handler " + _id + ": sending " + size + " bytes");
|
||||
|
||||
ByteArray ba = _cache.acquire();
|
||||
int read = DataHelper.read(in, ba.getData(), 0, size);
|
||||
if (read != size)
|
||||
throw new IOException("Insufficient data from the SAM client (" + read + "/" + size + ")");
|
||||
|
||||
ba.setValid(read);
|
||||
synchronized (_data) {
|
||||
_data.add(ba);
|
||||
_data.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a SAM STREAM session socket sender
|
||||
*
|
||||
*/
|
||||
public void stopRunning() {
|
||||
_log.debug("stopRunning() invoked on socket sender " + _id);
|
||||
_stillRunning = false;
|
||||
synchronized (_data) {
|
||||
_data.clear();
|
||||
_data.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
ByteArray data = null;
|
||||
while (_stillRunning) {
|
||||
data = null;
|
||||
try {
|
||||
synchronized (_data) {
|
||||
if (_data.size() > 0)
|
||||
data = (ByteArray)_data.remove(0);
|
||||
else
|
||||
_data.wait(5000);
|
||||
}
|
||||
|
||||
if (data != null) {
|
||||
try {
|
||||
_out.write(data.getData(), 0, data.getValid());
|
||||
if (forceFlush) {
|
||||
// i dont like doing this, but it clears the buffer issues
|
||||
_out.flush();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
// ok, the stream failed, but the SAM client didn't
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Stream failed", ioe);
|
||||
|
||||
removeSocketHandler(_id);
|
||||
stopRunning();
|
||||
|
||||
} finally {
|
||||
_cache.release(data);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
synchronized (_data) {
|
||||
_data.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@ -44,6 +45,9 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
private SAMDatagramSession datagramSession = null;
|
||||
private SAMStreamSession streamSession = null;
|
||||
|
||||
private long _id;
|
||||
private static volatile long __id = 0;
|
||||
|
||||
/**
|
||||
* Create a new SAM version 1 handler. This constructor expects
|
||||
* that the SAM HELLO message has been still answered (and
|
||||
@ -68,6 +72,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
*/
|
||||
public SAMv1Handler(Socket s, int verMajor, int verMinor, Properties i2cpProps) throws SAMException, IOException {
|
||||
super(s, verMajor, verMinor, i2cpProps);
|
||||
_id = ++__id;
|
||||
_log.debug("SAM version 1 handler instantiated");
|
||||
|
||||
if ((this.verMajor != 1) || (this.verMinor != 0)) {
|
||||
@ -76,13 +81,14 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
}
|
||||
|
||||
public void handle() {
|
||||
String msg, domain, opcode;
|
||||
String msg = null;
|
||||
String domain = null;
|
||||
String opcode = null;
|
||||
boolean canContinue = false;
|
||||
ByteArrayOutputStream buf = new ByteArrayOutputStream(IN_BUFSIZE);
|
||||
StringTokenizer tok;
|
||||
Properties props;
|
||||
|
||||
this.thread.setName("SAMv1Handler");
|
||||
this.thread.setName("SAMv1Handler " + _id);
|
||||
_log.debug("SAM handling started");
|
||||
|
||||
try {
|
||||
@ -95,22 +101,15 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
break;
|
||||
}
|
||||
|
||||
while ((b = in.read()) != -1) {
|
||||
if (b == '\n') {
|
||||
break;
|
||||
}
|
||||
buf.write(b);
|
||||
}
|
||||
if (b == -1) {
|
||||
msg = DataHelper.readLine(in);
|
||||
if (msg == null) {
|
||||
_log.debug("Connection closed by client");
|
||||
break;
|
||||
}
|
||||
|
||||
msg = buf.toString("ISO-8859-1").trim();
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug("New message received: [" + msg + "]");
|
||||
}
|
||||
buf.reset();
|
||||
|
||||
tok = new StringTokenizer(msg, " ");
|
||||
if (tok.countTokens() < 2) {
|
||||
@ -150,14 +149,11 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
_log.error("Caught UnsupportedEncodingException ("
|
||||
+ e.getMessage() + ")", e);
|
||||
} catch (IOException e) {
|
||||
_log.debug("Caught IOException ("
|
||||
+ e.getMessage() + ")", e);
|
||||
+ e.getMessage() + ") for message [" + msg + "]", e);
|
||||
} catch (Exception e) {
|
||||
_log.error("Unexpected exception", e);
|
||||
_log.error("Unexpected exception for message [" + msg + "]", e);
|
||||
} finally {
|
||||
_log.debug("Stopping handler");
|
||||
try {
|
||||
@ -550,13 +546,9 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
}
|
||||
|
||||
try {
|
||||
DataInputStream in = new DataInputStream(getClientSocketInputStream());
|
||||
byte[] data = new byte[size];
|
||||
|
||||
in.readFully(data);
|
||||
|
||||
if (!streamSession.sendBytes(id, data)) {
|
||||
_log.error("STREAM SEND failed");
|
||||
if (!streamSession.sendBytes(id, getClientSocketInputStream(), size)) { // data)) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("STREAM SEND [" + size + "] failed");
|
||||
boolean rv = writeString("STREAM CLOSED RESULT=CANT_REACH_PEER ID=" + id + " MESSAGE=\"Send of " + size + " bytes failed\"\n");
|
||||
streamSession.closeConnection(id);
|
||||
return rv;
|
||||
@ -564,11 +556,11 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
|
||||
return true;
|
||||
} catch (EOFException e) {
|
||||
_log.debug("Too few bytes with RAW SEND message (expected: "
|
||||
_log.debug("Too few bytes with STREAM SEND message (expected: "
|
||||
+ size);
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
_log.debug("Caught IOException while parsing RAW SEND message",
|
||||
_log.debug("Caught IOException while parsing STREAM SEND message",
|
||||
e);
|
||||
return false;
|
||||
}
|
||||
@ -707,7 +699,8 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
try {
|
||||
closeClientSocket();
|
||||
} catch (IOException e) {
|
||||
_log.error("Error closing socket: " + e.getMessage());
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error closing socket", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -742,7 +735,8 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
try {
|
||||
closeClientSocket();
|
||||
} catch (IOException e) {
|
||||
_log.error("Error closing socket: " + e.getMessage());
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error closing socket", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -801,7 +795,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
}
|
||||
|
||||
public void stopStreamReceiving() {
|
||||
_log.debug("stopStreamReceiving() invoked");
|
||||
_log.debug("stopStreamReceiving() invoked", new Exception("stopped"));
|
||||
|
||||
if (streamSession == null) {
|
||||
_log.error("BUG! Got stream receiving stop, but session is null!");
|
||||
@ -811,7 +805,8 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
try {
|
||||
closeClientSocket();
|
||||
} catch (IOException e) {
|
||||
_log.error("Error closing socket: " + e.getMessage());
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error closing socket", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,18 @@
|
||||
package net.i2p.sam.client;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Basic noop client event listener
|
||||
*/
|
||||
public class SAMClientEventListenerImpl implements SAMReader.SAMClientEventListener {
|
||||
public void destReplyReceived(String publicKey, String privateKey) {}
|
||||
public void helloReplyReceived(boolean ok) {}
|
||||
public void namingReplyReceived(String name, String result, String value, String message) {}
|
||||
public void sessionStatusReceived(String result, String destination, String message) {}
|
||||
public void streamClosedReceived(String result, int id, String message) {}
|
||||
public void streamConnectedReceived(String remoteDestination, int id) {}
|
||||
public void streamDataReceived(int id, byte[] data, int offset, int length) {}
|
||||
public void streamStatusReceived(String result, int id, String message) {}
|
||||
public void unknownMessageReceived(String major, String minor, Properties params) {}
|
||||
}
|
127
apps/sam/java/src/net/i2p/sam/client/SAMEventHandler.java
Normal file
127
apps/sam/java/src/net/i2p/sam/client/SAMEventHandler.java
Normal file
@ -0,0 +1,127 @@
|
||||
package net.i2p.sam.client;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Simple helper implementation of a the SAMClientEventListener
|
||||
*
|
||||
*/
|
||||
public class SAMEventHandler extends SAMClientEventListenerImpl {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private Boolean _helloOk;
|
||||
private Object _helloLock = new Object();
|
||||
private Boolean _sessionCreateOk;
|
||||
private Object _sessionCreateLock = new Object();
|
||||
private Object _namingReplyLock = new Object();
|
||||
private Map _namingReplies = new HashMap();
|
||||
|
||||
public SAMEventHandler(I2PAppContext ctx) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(getClass());
|
||||
}
|
||||
|
||||
public void helloReplyReceived(boolean ok) {
|
||||
synchronized (_helloLock) {
|
||||
if (ok)
|
||||
_helloOk = Boolean.TRUE;
|
||||
else
|
||||
_helloOk = Boolean.FALSE;
|
||||
_helloLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void sessionStatusReceived(String result, String destination, String msg) {
|
||||
synchronized (_sessionCreateLock) {
|
||||
if (SAMReader.SAMClientEventListener.SESSION_STATUS_OK.equals(result))
|
||||
_sessionCreateOk = Boolean.TRUE;
|
||||
else
|
||||
_sessionCreateOk = Boolean.FALSE;
|
||||
_sessionCreateLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void namingReplyReceived(String name, String result, String value, String msg) {
|
||||
synchronized (_namingReplyLock) {
|
||||
if (SAMReader.SAMClientEventListener.NAMING_REPLY_OK.equals(result))
|
||||
_namingReplies.put(name, value);
|
||||
else
|
||||
_namingReplies.put(name, result);
|
||||
_namingReplyLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void unknownMessageReceived(String major, String minor, Properties params) {
|
||||
_log.error("wrt, [" + major + "] [" + minor + "] [" + params + "]");
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// blocking lookup calls below
|
||||
//
|
||||
|
||||
/**
|
||||
* Wait for the connection to be established, returning true if everything
|
||||
* went ok
|
||||
*/
|
||||
public boolean waitForHelloReply() {
|
||||
while (true) {
|
||||
try {
|
||||
synchronized (_helloLock) {
|
||||
if (_helloOk == null)
|
||||
_helloLock.wait();
|
||||
else
|
||||
return _helloOk.booleanValue();
|
||||
}
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the session to be created, returning true if everything went ok
|
||||
*
|
||||
*/
|
||||
public boolean waitForSessionCreateReply() {
|
||||
while (true) {
|
||||
try {
|
||||
synchronized (_sessionCreateLock) {
|
||||
if (_sessionCreateOk == null)
|
||||
_sessionCreateLock.wait();
|
||||
else
|
||||
return _sessionCreateOk.booleanValue();
|
||||
}
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the destination found matching the name, or null if the key was
|
||||
* not able to be retrieved.
|
||||
*
|
||||
* @param name name to be looked for, or "ME"
|
||||
*/
|
||||
public String waitForNamingReply(String name) {
|
||||
while (true) {
|
||||
try {
|
||||
synchronized (_namingReplyLock) {
|
||||
String val = (String)_namingReplies.remove(name);
|
||||
if (val == null) {
|
||||
_namingReplyLock.wait();
|
||||
} else {
|
||||
if (SAMReader.SAMClientEventListener.NAMING_REPLY_INVALID_KEY.equals(val))
|
||||
return null;
|
||||
else if (SAMReader.SAMClientEventListener.NAMING_REPLY_KEY_NOT_FOUND.equals(val))
|
||||
return null;
|
||||
else
|
||||
return val;
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
}
|
253
apps/sam/java/src/net/i2p/sam/client/SAMReader.java
Normal file
253
apps/sam/java/src/net/i2p/sam/client/SAMReader.java
Normal file
@ -0,0 +1,253 @@
|
||||
package net.i2p.sam.client;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.I2PThread;
|
||||
|
||||
/**
|
||||
* Read from a socket, producing events for any SAM message read
|
||||
*
|
||||
*/
|
||||
public class SAMReader {
|
||||
private Log _log;
|
||||
private InputStream _inRaw;
|
||||
private SAMClientEventListener _listener;
|
||||
private boolean _live;
|
||||
|
||||
public SAMReader(I2PAppContext context, InputStream samIn, SAMClientEventListener listener) {
|
||||
_log = context.logManager().getLog(SAMReader.class);
|
||||
_inRaw = samIn;
|
||||
_listener = listener;
|
||||
}
|
||||
|
||||
public void startReading() {
|
||||
_live = true;
|
||||
I2PThread t = new I2PThread(new Runner(), "SAM reader");
|
||||
t.start();
|
||||
}
|
||||
public void stopReading() { _live = false; }
|
||||
|
||||
/**
|
||||
* Async event notification interface for SAM clients
|
||||
*
|
||||
*/
|
||||
public interface SAMClientEventListener {
|
||||
public static final String SESSION_STATUS_OK = "OK";
|
||||
public static final String SESSION_STATUS_DUPLICATE_DEST = "DUPLICATE_DEST";
|
||||
public static final String SESSION_STATUS_I2P_ERROR = "I2P_ERROR";
|
||||
public static final String SESSION_STATUS_INVALID_KEY = "INVALID_KEY";
|
||||
|
||||
public static final String STREAM_STATUS_OK = "OK";
|
||||
public static final String STREAM_STATUS_CANT_REACH_PEER = "CANT_REACH_PEER";
|
||||
public static final String STREAM_STATUS_I2P_ERROR = "I2P_ERROR";
|
||||
public static final String STREAM_STATUS_INVALID_KEY = "INVALID_KEY";
|
||||
public static final String STREAM_STATUS_TIMEOUT = "TIMEOUT";
|
||||
|
||||
public static final String STREAM_CLOSED_OK = "OK";
|
||||
public static final String STREAM_CLOSED_CANT_REACH_PEER = "CANT_REACH_PEER";
|
||||
public static final String STREAM_CLOSED_I2P_ERROR = "I2P_ERROR";
|
||||
public static final String STREAM_CLOSED_PEER_NOT_FOUND = "PEER_NOT_FOUND";
|
||||
public static final String STREAM_CLOSED_TIMEOUT = "CLOSED";
|
||||
|
||||
public static final String NAMING_REPLY_OK = "OK";
|
||||
public static final String NAMING_REPLY_INVALID_KEY = "INVALID_KEY";
|
||||
public static final String NAMING_REPLY_KEY_NOT_FOUND = "KEY_NOT_FOUND";
|
||||
|
||||
public void helloReplyReceived(boolean ok);
|
||||
public void sessionStatusReceived(String result, String destination, String message);
|
||||
public void streamStatusReceived(String result, int id, String message);
|
||||
public void streamConnectedReceived(String remoteDestination, int id);
|
||||
public void streamClosedReceived(String result, int id, String message);
|
||||
public void streamDataReceived(int id, byte data[], int offset, int length);
|
||||
public void namingReplyReceived(String name, String result, String value, String message);
|
||||
public void destReplyReceived(String publicKey, String privateKey);
|
||||
|
||||
public void unknownMessageReceived(String major, String minor, Properties params);
|
||||
}
|
||||
|
||||
private class Runner implements Runnable {
|
||||
public void run() {
|
||||
Properties params = new Properties();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(80);
|
||||
while (_live) {
|
||||
|
||||
try {
|
||||
int c = -1;
|
||||
while ((c = _inRaw.read()) != -1) {
|
||||
if (c == '\n') {
|
||||
break;
|
||||
}
|
||||
baos.write(c);
|
||||
}
|
||||
if (c == -1) {
|
||||
_log.error("Error reading from the SAM bridge");
|
||||
return;
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error reading from SAM", ioe);
|
||||
}
|
||||
|
||||
String line = new String(baos.toByteArray());
|
||||
baos.reset();
|
||||
|
||||
if (line == null) {
|
||||
_log.info("No more data from the SAM bridge");
|
||||
break;
|
||||
}
|
||||
|
||||
_log.debug("Line read from the bridge: " + line);
|
||||
|
||||
StringTokenizer tok = new StringTokenizer(line);
|
||||
|
||||
if (tok.countTokens() < 2) {
|
||||
_log.error("Invalid SAM line: [" + line + "]");
|
||||
_live = false;
|
||||
return;
|
||||
}
|
||||
|
||||
String major = tok.nextToken();
|
||||
String minor = tok.nextToken();
|
||||
|
||||
params.clear();
|
||||
while (tok.hasMoreTokens()) {
|
||||
String pair = tok.nextToken();
|
||||
int eq = pair.indexOf('=');
|
||||
if ( (eq > 0) && (eq < pair.length() - 1) ) {
|
||||
String name = pair.substring(0, eq);
|
||||
String val = pair.substring(eq+1);
|
||||
while ( (val.charAt(0) == '\"') && (val.length() > 0) )
|
||||
val = val.substring(1);
|
||||
while ( (val.length() > 0) && (val.charAt(val.length()-1) == '\"') )
|
||||
val = val.substring(0, val.length()-1);
|
||||
params.setProperty(name, val);
|
||||
}
|
||||
}
|
||||
|
||||
processEvent(major, minor, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Big ugly method parsing everything. If I cared, I'd factor this out into
|
||||
* a dozen tiny methods.
|
||||
*
|
||||
*/
|
||||
private void processEvent(String major, String minor, Properties params) {
|
||||
if ("HELLO".equals(major)) {
|
||||
if ("REPLY".equals(minor)) {
|
||||
String result = params.getProperty("RESULT");
|
||||
if ("OK".equals(result))
|
||||
_listener.helloReplyReceived(true);
|
||||
else
|
||||
_listener.helloReplyReceived(false);
|
||||
} else {
|
||||
_listener.unknownMessageReceived(major, minor, params);
|
||||
}
|
||||
} else if ("SESSION".equals(major)) {
|
||||
if ("STATUS".equals(minor)) {
|
||||
String result = params.getProperty("RESULT");
|
||||
String dest = params.getProperty("DESTINATION");
|
||||
String msg = params.getProperty("MESSAGE");
|
||||
_listener.sessionStatusReceived(result, dest, msg);
|
||||
} else {
|
||||
_listener.unknownMessageReceived(major, minor, params);
|
||||
}
|
||||
} else if ("STREAM".equals(major)) {
|
||||
if ("STATUS".equals(minor)) {
|
||||
String result = params.getProperty("RESULT");
|
||||
String id = params.getProperty("ID");
|
||||
String msg = params.getProperty("MESSAGE");
|
||||
if (id != null) {
|
||||
try {
|
||||
_listener.streamStatusReceived(result, Integer.parseInt(id), msg);
|
||||
} catch (NumberFormatException nfe) {
|
||||
_listener.unknownMessageReceived(major, minor, params);
|
||||
}
|
||||
} else {
|
||||
_listener.unknownMessageReceived(major, minor, params);
|
||||
}
|
||||
} else if ("CONNECTED".equals(minor)) {
|
||||
String dest = params.getProperty("DESTINATION");
|
||||
String id = params.getProperty("ID");
|
||||
if (id != null) {
|
||||
try {
|
||||
_listener.streamConnectedReceived(dest, Integer.parseInt(id));
|
||||
} catch (NumberFormatException nfe) {
|
||||
_listener.unknownMessageReceived(major, minor, params);
|
||||
}
|
||||
} else {
|
||||
_listener.unknownMessageReceived(major, minor, params);
|
||||
}
|
||||
} else if ("CLOSED".equals(minor)) {
|
||||
String result = params.getProperty("RESULT");
|
||||
String id = params.getProperty("ID");
|
||||
String msg = params.getProperty("MESSAGE");
|
||||
if (id != null) {
|
||||
try {
|
||||
_listener.streamClosedReceived(result, Integer.parseInt(id), msg);
|
||||
} catch (NumberFormatException nfe) {
|
||||
_listener.unknownMessageReceived(major, minor, params);
|
||||
}
|
||||
} else {
|
||||
_listener.unknownMessageReceived(major, minor, params);
|
||||
}
|
||||
} else if ("RECEIVED".equals(minor)) {
|
||||
String id = params.getProperty("ID");
|
||||
String size = params.getProperty("SIZE");
|
||||
if (id != null) {
|
||||
try {
|
||||
int idVal = Integer.parseInt(id);
|
||||
int sizeVal = Integer.parseInt(size);
|
||||
|
||||
byte data[] = new byte[sizeVal];
|
||||
int read = DataHelper.read(_inRaw, data);
|
||||
if (read != sizeVal) {
|
||||
_listener.unknownMessageReceived(major, minor, params);
|
||||
} else {
|
||||
_listener.streamDataReceived(idVal, data, 0, sizeVal);
|
||||
}
|
||||
} catch (NumberFormatException nfe) {
|
||||
_listener.unknownMessageReceived(major, minor, params);
|
||||
} catch (IOException ioe) {
|
||||
_live = false;
|
||||
_listener.unknownMessageReceived(major, minor, params);
|
||||
}
|
||||
} else {
|
||||
_listener.unknownMessageReceived(major, minor, params);
|
||||
}
|
||||
} else {
|
||||
_listener.unknownMessageReceived(major, minor, params);
|
||||
}
|
||||
} else if ("NAMING".equals(major)) {
|
||||
if ("REPLY".equals(minor)) {
|
||||
String name = params.getProperty("NAME");
|
||||
String result = params.getProperty("RESULT");
|
||||
String value = params.getProperty("VALUE");
|
||||
String msg = params.getProperty("MESSAGE");
|
||||
_listener.namingReplyReceived(name, result, value, msg);
|
||||
} else {
|
||||
_listener.unknownMessageReceived(major, minor, params);
|
||||
}
|
||||
} else if ("DEST".equals(major)) {
|
||||
if ("REPLY".equals(minor)) {
|
||||
String pub = params.getProperty("PUB");
|
||||
String priv = params.getProperty("PRIV");
|
||||
_listener.destReplyReceived(pub, priv);
|
||||
} else {
|
||||
_listener.unknownMessageReceived(major, minor, params);
|
||||
}
|
||||
} else {
|
||||
_listener.unknownMessageReceived(major, minor, params);
|
||||
}
|
||||
}
|
||||
}
|
262
apps/sam/java/src/net/i2p/sam/client/SAMStreamSend.java
Normal file
262
apps/sam/java/src/net/i2p/sam/client/SAMStreamSend.java
Normal file
@ -0,0 +1,262 @@
|
||||
package net.i2p.sam.client;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.I2PThread;
|
||||
|
||||
import net.i2p.sam.client.SAMEventHandler;
|
||||
import net.i2p.sam.client.SAMClientEventListenerImpl;
|
||||
import net.i2p.sam.client.SAMReader;
|
||||
|
||||
/**
|
||||
* Send a file to a peer
|
||||
*
|
||||
* Usage: SAMStreamSend samHost samPort peerDestFile dataFile
|
||||
*
|
||||
*/
|
||||
public class SAMStreamSend {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private String _samHost;
|
||||
private String _samPort;
|
||||
private String _destFile;
|
||||
private String _dataFile;
|
||||
private String _conOptions;
|
||||
private Socket _samSocket;
|
||||
private OutputStream _samOut;
|
||||
private InputStream _samIn;
|
||||
private SAMReader _reader;
|
||||
private boolean _dead;
|
||||
private SAMEventHandler _eventHandler;
|
||||
/** Connection id (Integer) to peer (Flooder) */
|
||||
private Map _remotePeers;
|
||||
|
||||
public static void main(String args[]) {
|
||||
if (args.length < 4) {
|
||||
System.err.println("Usage: SAMStreamSend samHost samPort peerDestFile dataFile");
|
||||
return;
|
||||
}
|
||||
I2PAppContext ctx = new I2PAppContext();
|
||||
String files[] = new String[args.length - 3];
|
||||
SAMStreamSend sender = new SAMStreamSend(ctx, args[0], args[1], args[2], args[3]);
|
||||
sender.startup();
|
||||
}
|
||||
|
||||
public SAMStreamSend(I2PAppContext ctx, String samHost, String samPort, String destFile, String dataFile) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(SAMStreamSend.class);
|
||||
_dead = false;
|
||||
_samHost = samHost;
|
||||
_samPort = samPort;
|
||||
_destFile = destFile;
|
||||
_dataFile = dataFile;;
|
||||
_conOptions = "";
|
||||
_eventHandler = new SendEventHandler(_context);
|
||||
_remotePeers = new HashMap();
|
||||
}
|
||||
|
||||
public void startup() {
|
||||
_log.debug("Starting up");
|
||||
boolean ok = connect();
|
||||
_log.debug("Connected: " + ok);
|
||||
if (ok) {
|
||||
_reader = new SAMReader(_context, _samIn, _eventHandler);
|
||||
_reader.startReading();
|
||||
_log.debug("Reader created");
|
||||
String ourDest = handshake();
|
||||
_log.debug("Handshake complete. we are " + ourDest);
|
||||
if (ourDest != null) {
|
||||
send();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SendEventHandler extends SAMEventHandler {
|
||||
public SendEventHandler(I2PAppContext ctx) { super(ctx); }
|
||||
public void streamClosedReceived(String result, int id, String message) {
|
||||
Sender sender = null;
|
||||
synchronized (_remotePeers) {
|
||||
sender = (Sender)_remotePeers.remove(new Integer(id));
|
||||
}
|
||||
if (sender != null) {
|
||||
sender.closed();
|
||||
_log.debug("Connection " + sender.getConnectionId() + " closed to " + sender.getDestination());
|
||||
} else {
|
||||
_log.error("wtf, not connected to " + id + " but we were just closed?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean connect() {
|
||||
try {
|
||||
_samSocket = new Socket(_samHost, Integer.parseInt(_samPort));
|
||||
_samOut = _samSocket.getOutputStream();
|
||||
_samIn = _samSocket.getInputStream();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
_log.error("Unable to connect to SAM at " + _samHost + ":" + _samPort, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private String handshake() {
|
||||
synchronized (_samOut) {
|
||||
try {
|
||||
_samOut.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
|
||||
_samOut.flush();
|
||||
_log.debug("Hello sent");
|
||||
boolean ok = _eventHandler.waitForHelloReply();
|
||||
_log.debug("Hello reply found: " + ok);
|
||||
if (!ok)
|
||||
throw new IOException("wtf, hello failed?");
|
||||
String req = "SESSION CREATE STYLE=STREAM DESTINATION=TRANSIENT " + _conOptions + "\n";
|
||||
_samOut.write(req.getBytes());
|
||||
_samOut.flush();
|
||||
_log.debug("Session create sent");
|
||||
ok = _eventHandler.waitForSessionCreateReply();
|
||||
_log.debug("Session create reply found: " + ok);
|
||||
|
||||
req = "NAMING LOOKUP NAME=ME\n";
|
||||
_samOut.write(req.getBytes());
|
||||
_samOut.flush();
|
||||
_log.debug("Naming lookup sent");
|
||||
String destination = _eventHandler.waitForNamingReply("ME");
|
||||
_log.debug("Naming lookup reply found: " + destination);
|
||||
if (destination == null) {
|
||||
_log.error("No naming lookup reply found!");
|
||||
return null;
|
||||
} else {
|
||||
_log.info("We are " + destination);
|
||||
}
|
||||
return destination;
|
||||
} catch (Exception e) {
|
||||
_log.error("Error handshaking", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void send() {
|
||||
Sender sender = new Sender();
|
||||
boolean ok = sender.openConnection();
|
||||
if (ok) {
|
||||
I2PThread t = new I2PThread(sender, "Sender");
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
|
||||
private class Sender implements Runnable {
|
||||
private int _connectionId;
|
||||
private String _remoteDestination;
|
||||
private InputStream _in;
|
||||
private boolean _closed;
|
||||
private long _started;
|
||||
private long _totalSent;
|
||||
|
||||
public Sender() {
|
||||
_closed = false;
|
||||
}
|
||||
|
||||
public boolean openConnection() {
|
||||
try {
|
||||
FileInputStream fin = new FileInputStream(_destFile);
|
||||
byte dest[] = new byte[1024];
|
||||
int read = DataHelper.read(fin, dest);
|
||||
|
||||
_remoteDestination = new String(dest, 0, read);
|
||||
synchronized (_remotePeers) {
|
||||
_connectionId = _remotePeers.size() + 1;
|
||||
_remotePeers.put(new Integer(_connectionId), Sender.this);
|
||||
}
|
||||
|
||||
_context.statManager().createRateStat("send." + _connectionId + ".totalSent", "Data size sent", "swarm", new long[] { 30*1000, 60*1000, 5*60*1000 });
|
||||
_context.statManager().createRateStat("send." + _connectionId + ".started", "When we start", "swarm", new long[] { 5*60*1000 });
|
||||
_context.statManager().createRateStat("send." + _connectionId + ".lifetime", "How long we talk to a peer", "swarm", new long[] { 5*60*1000 });
|
||||
|
||||
byte msg[] = ("STREAM CONNECT ID=" + _connectionId + " DESTINATION=" + _remoteDestination + "\n").getBytes();
|
||||
synchronized (_samOut) {
|
||||
_samOut.write(msg);
|
||||
_samOut.flush();
|
||||
}
|
||||
|
||||
_in = new FileInputStream(_dataFile);
|
||||
return true;
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Unable to connect", ioe);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int getConnectionId() { return _connectionId; }
|
||||
public String getDestination() { return _remoteDestination; }
|
||||
|
||||
public void closed() {
|
||||
if (_closed) return;
|
||||
_closed = true;
|
||||
long lifetime = _context.clock().now() - _started;
|
||||
_context.statManager().addRateData("send." + _connectionId + ".lifetime", lifetime, lifetime);
|
||||
try { _in.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
_started = _context.clock().now();
|
||||
_context.statManager().addRateData("send." + _connectionId + ".started", 1, 0);
|
||||
byte data[] = new byte[1024];
|
||||
long value = 0;
|
||||
long lastSend = _context.clock().now();
|
||||
while (!_closed) {
|
||||
try {
|
||||
int read = _in.read(data);
|
||||
long now = _context.clock().now();
|
||||
if (read == -1) {
|
||||
_log.debug("EOF from the data for " + _connectionId + " after " + (now-lastSend));
|
||||
break;
|
||||
} else if (read > 0) {
|
||||
_log.debug("Sending " + read + " on " + _connectionId + " after " + (now-lastSend));
|
||||
lastSend = now;
|
||||
|
||||
byte msg[] = ("STREAM SEND ID=" + _connectionId + " SIZE=" + read + "\n").getBytes();
|
||||
synchronized (_samOut) {
|
||||
_samOut.write(msg);
|
||||
_samOut.write(data, 0, read);
|
||||
_samOut.flush();
|
||||
}
|
||||
|
||||
_totalSent += read;
|
||||
_context.statManager().addRateData("send." + _connectionId + ".totalSent", _totalSent, 0);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error sending", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
byte msg[] = ("STREAM CLOSE ID=" + _connectionId + "\n").getBytes();
|
||||
try {
|
||||
synchronized (_samOut) {
|
||||
_samOut.write(msg);
|
||||
_samOut.flush();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error closing", ioe);
|
||||
}
|
||||
|
||||
closed();
|
||||
}
|
||||
}
|
||||
}
|
247
apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java
Normal file
247
apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java
Normal file
@ -0,0 +1,247 @@
|
||||
package net.i2p.sam.client;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.I2PThread;
|
||||
|
||||
import net.i2p.sam.client.SAMEventHandler;
|
||||
import net.i2p.sam.client.SAMClientEventListenerImpl;
|
||||
import net.i2p.sam.client.SAMReader;
|
||||
|
||||
/**
|
||||
* Sit around on a SAM destination, receiving lots of data and
|
||||
* writing it to disk
|
||||
*
|
||||
* Usage: SAMStreamSink samHost samPort myKeyFile sinkDir
|
||||
*
|
||||
*/
|
||||
public class SAMStreamSink {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private String _samHost;
|
||||
private String _samPort;
|
||||
private String _destFile;
|
||||
private String _sinkDir;
|
||||
private String _conOptions;
|
||||
private Socket _samSocket;
|
||||
private OutputStream _samOut;
|
||||
private InputStream _samIn;
|
||||
private SAMReader _reader;
|
||||
private boolean _dead;
|
||||
private SAMEventHandler _eventHandler;
|
||||
/** Connection id (Integer) to peer (Flooder) */
|
||||
private Map _remotePeers;
|
||||
|
||||
public static void main(String args[]) {
|
||||
if (args.length < 4) {
|
||||
System.err.println("Usage: SAMStreamSink samHost samPort myDestFile sinkDir");
|
||||
return;
|
||||
}
|
||||
I2PAppContext ctx = new I2PAppContext();
|
||||
SAMStreamSink sink = new SAMStreamSink(ctx, args[0], args[1], args[2], args[3]);
|
||||
sink.startup();
|
||||
}
|
||||
|
||||
public SAMStreamSink(I2PAppContext ctx, String samHost, String samPort, String destFile, String sinkDir) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(SAMStreamSink.class);
|
||||
_dead = false;
|
||||
_samHost = samHost;
|
||||
_samPort = samPort;
|
||||
_destFile = destFile;
|
||||
_sinkDir = sinkDir;
|
||||
_conOptions = "";
|
||||
_eventHandler = new SinkEventHandler(_context);
|
||||
_remotePeers = new HashMap();
|
||||
}
|
||||
|
||||
public void startup() {
|
||||
_log.debug("Starting up");
|
||||
boolean ok = connect();
|
||||
_log.debug("Connected: " + ok);
|
||||
if (ok) {
|
||||
_reader = new SAMReader(_context, _samIn, _eventHandler);
|
||||
_reader.startReading();
|
||||
_log.debug("Reader created");
|
||||
String ourDest = handshake();
|
||||
_log.debug("Handshake complete. we are " + ourDest);
|
||||
if (ourDest != null) {
|
||||
boolean written = writeDest(ourDest);
|
||||
_log.debug("Dest written");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SinkEventHandler extends SAMEventHandler {
|
||||
public SinkEventHandler(I2PAppContext ctx) { super(ctx); }
|
||||
public void streamClosedReceived(String result, int id, String message) {
|
||||
Sink sink = null;
|
||||
synchronized (_remotePeers) {
|
||||
sink = (Sink)_remotePeers.remove(new Integer(id));
|
||||
}
|
||||
if (sink != null) {
|
||||
sink.closed();
|
||||
_log.debug("Connection " + sink.getConnectionId() + " closed to " + sink.getDestination());
|
||||
} else {
|
||||
_log.error("wtf, not connected to " + id + " but we were just closed?");
|
||||
}
|
||||
}
|
||||
public void streamDataReceived(int id, byte data[], int offset, int length) {
|
||||
Sink sink = null;
|
||||
synchronized (_remotePeers) {
|
||||
sink = (Sink)_remotePeers.get(new Integer(id));
|
||||
}
|
||||
if (sink != null) {
|
||||
sink.received(data, offset, length);
|
||||
} else {
|
||||
_log.error("wtf, not connected to " + id + " but we received " + length + "?");
|
||||
}
|
||||
}
|
||||
public void streamConnectedReceived(String dest, int id) {
|
||||
_log.debug("Connection " + id + " received from " + dest);
|
||||
|
||||
try {
|
||||
Sink sink = new Sink(id, dest);
|
||||
synchronized (_remotePeers) {
|
||||
_remotePeers.put(new Integer(id), sink);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error creating a new sink", ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean connect() {
|
||||
try {
|
||||
_samSocket = new Socket(_samHost, Integer.parseInt(_samPort));
|
||||
_samOut = _samSocket.getOutputStream();
|
||||
_samIn = _samSocket.getInputStream();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
_log.error("Unable to connect to SAM at " + _samHost + ":" + _samPort, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private String handshake() {
|
||||
synchronized (_samOut) {
|
||||
try {
|
||||
_samOut.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
|
||||
_samOut.flush();
|
||||
_log.debug("Hello sent");
|
||||
boolean ok = _eventHandler.waitForHelloReply();
|
||||
_log.debug("Hello reply found: " + ok);
|
||||
if (!ok)
|
||||
throw new IOException("wtf, hello failed?");
|
||||
String req = "SESSION CREATE STYLE=STREAM DESTINATION=" + _destFile + " " + _conOptions + "\n";
|
||||
_samOut.write(req.getBytes());
|
||||
_samOut.flush();
|
||||
_log.debug("Session create sent");
|
||||
ok = _eventHandler.waitForSessionCreateReply();
|
||||
_log.debug("Session create reply found: " + ok);
|
||||
|
||||
req = "NAMING LOOKUP NAME=ME\n";
|
||||
_samOut.write(req.getBytes());
|
||||
_samOut.flush();
|
||||
_log.debug("Naming lookup sent");
|
||||
String destination = _eventHandler.waitForNamingReply("ME");
|
||||
_log.debug("Naming lookup reply found: " + destination);
|
||||
if (destination == null) {
|
||||
_log.error("No naming lookup reply found!");
|
||||
return null;
|
||||
} else {
|
||||
_log.info(_destFile + " is located at " + destination);
|
||||
}
|
||||
return destination;
|
||||
} catch (Exception e) {
|
||||
_log.error("Error handshaking", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean writeDest(String dest) {
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream(_destFile);
|
||||
fos.write(dest.getBytes());
|
||||
fos.close();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
_log.error("Error writing to " + _destFile, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private class Sink {
|
||||
private int _connectionId;
|
||||
private String _remoteDestination;
|
||||
private boolean _closed;
|
||||
private long _started;
|
||||
private long _totalReceived;
|
||||
private long _lastReceivedOn;
|
||||
private OutputStream _out;
|
||||
|
||||
public Sink(int conId, String remDest) throws IOException {
|
||||
_connectionId = conId;
|
||||
_remoteDestination = remDest;
|
||||
_closed = false;
|
||||
_lastReceivedOn = _context.clock().now();
|
||||
_context.statManager().createRateStat("sink." + conId + ".totalReceived", "Data size received", "swarm", new long[] { 30*1000, 60*1000, 5*60*1000 });
|
||||
_context.statManager().createRateStat("sink." + conId + ".started", "When we start", "swarm", new long[] { 5*60*1000 });
|
||||
_context.statManager().createRateStat("sink." + conId + ".lifetime", "How long we talk to a peer", "swarm", new long[] { 5*60*1000 });
|
||||
|
||||
File sinkDir = new File(_sinkDir);
|
||||
if (!sinkDir.exists())
|
||||
sinkDir.mkdirs();
|
||||
|
||||
File out = File.createTempFile("sink", ".dat", sinkDir);
|
||||
_out = new FileOutputStream(out);
|
||||
}
|
||||
|
||||
public int getConnectionId() { return _connectionId; }
|
||||
public String getDestination() { return _remoteDestination; }
|
||||
|
||||
public void closed() {
|
||||
if (_closed) return;
|
||||
_closed = true;
|
||||
long lifetime = _context.clock().now() - _started;
|
||||
_context.statManager().addRateData("sink." + _connectionId + ".lifetime", lifetime, lifetime);
|
||||
try {
|
||||
_out.close();
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error closing", ioe);
|
||||
}
|
||||
}
|
||||
public void received(byte data[], int offset, int len) {
|
||||
if (_closed) return;
|
||||
_totalReceived += len;
|
||||
try {
|
||||
_out.write(data, offset, len);
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error writing received data");
|
||||
closed();
|
||||
return;
|
||||
}
|
||||
_log.debug("Received " + len + " on " + _connectionId + " after " + (_context.clock().now()-_lastReceivedOn)
|
||||
+ "ms with " + _remoteDestination.substring(0,6));
|
||||
|
||||
_lastReceivedOn = _context.clock().now();
|
||||
}
|
||||
}
|
||||
}
|
@ -150,7 +150,7 @@ public class TestStreamTransfer {
|
||||
_log.error("Incorrect size read - expected " + payloadSize + " got " + read);
|
||||
return;
|
||||
}
|
||||
_log.info("Received from the stream " + id + ": [" + new String(payload) + "]");
|
||||
_log.info("\n== Received from the stream " + id + ": [" + new String(payload) + "]");
|
||||
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
|
||||
/*
|
||||
// now echo it back
|
||||
@ -217,7 +217,12 @@ public class TestStreamTransfer {
|
||||
}
|
||||
try { Thread.sleep(5*1000) ; } catch (InterruptedException ie) {}
|
||||
req = "STREAM SEND ID=42 SIZE=10\nBlahBlah!!";
|
||||
_log.info("Sending data");
|
||||
_log.info("\n** Sending BlahBlah!!");
|
||||
out.write(req.getBytes());
|
||||
out.flush();
|
||||
try { Thread.sleep(5*1000) ; } catch (InterruptedException ie) {}
|
||||
req = "STREAM SEND ID=42 SIZE=10\nFooBarBaz!";
|
||||
_log.info("\n** Sending FooBarBaz!");
|
||||
out.write(req.getBytes());
|
||||
out.flush();
|
||||
try { Thread.sleep(20*1000); } catch (InterruptedException ie) {}
|
||||
|
312
apps/sam/java/test/net/i2p/sam/TestSwarm.java
Normal file
312
apps/sam/java/test/net/i2p/sam/TestSwarm.java
Normal file
@ -0,0 +1,312 @@
|
||||
package net.i2p.sam;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.I2PThread;
|
||||
|
||||
import net.i2p.sam.client.SAMEventHandler;
|
||||
import net.i2p.sam.client.SAMClientEventListenerImpl;
|
||||
import net.i2p.sam.client.SAMReader;
|
||||
|
||||
/**
|
||||
* Sit around on a SAM destination, receiving lots of data and sending lots of
|
||||
* data to whomever talks to us.
|
||||
*
|
||||
* Usage: TestSwarm samHost samPort myKeyFile [peerDestFile ]*
|
||||
*
|
||||
*/
|
||||
public class TestSwarm {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private String _samHost;
|
||||
private String _samPort;
|
||||
private String _destFile;
|
||||
private String _peerDestFiles[];
|
||||
private String _conOptions;
|
||||
private Socket _samSocket;
|
||||
private OutputStream _samOut;
|
||||
private InputStream _samIn;
|
||||
private SAMReader _reader;
|
||||
private boolean _dead;
|
||||
private SAMEventHandler _eventHandler;
|
||||
/** Connection id (Integer) to peer (Flooder) */
|
||||
private Map _remotePeers;
|
||||
|
||||
public static void main(String args[]) {
|
||||
if (args.length < 3) {
|
||||
System.err.println("Usage: TestSwarm samHost samPort myDestFile [peerDestFile ]*");
|
||||
return;
|
||||
}
|
||||
I2PAppContext ctx = new I2PAppContext();
|
||||
String files[] = new String[args.length - 3];
|
||||
System.arraycopy(args, 3, files, 0, files.length);
|
||||
TestSwarm swarm = new TestSwarm(ctx, args[0], args[1], args[2], files);
|
||||
swarm.startup();
|
||||
}
|
||||
|
||||
public TestSwarm(I2PAppContext ctx, String samHost, String samPort, String destFile, String peerDestFiles[]) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(TestSwarm.class);
|
||||
_dead = false;
|
||||
_samHost = samHost;
|
||||
_samPort = samPort;
|
||||
_destFile = destFile;
|
||||
_peerDestFiles = peerDestFiles;
|
||||
_conOptions = "";
|
||||
_eventHandler = new SwarmEventHandler(_context);
|
||||
_remotePeers = new HashMap();
|
||||
}
|
||||
|
||||
public void startup() {
|
||||
_log.debug("Starting up");
|
||||
boolean ok = connect();
|
||||
_log.debug("Connected: " + ok);
|
||||
if (ok) {
|
||||
_reader = new SAMReader(_context, _samIn, _eventHandler);
|
||||
_reader.startReading();
|
||||
_log.debug("Reader created");
|
||||
String ourDest = handshake();
|
||||
_log.debug("Handshake complete. we are " + ourDest);
|
||||
if (ourDest != null) {
|
||||
boolean written = writeDest(ourDest);
|
||||
_log.debug("Dest written");
|
||||
if (written) {
|
||||
connectWithPeers();
|
||||
_log.debug("connected with peers");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SwarmEventHandler extends SAMEventHandler {
|
||||
public SwarmEventHandler(I2PAppContext ctx) { super(ctx); }
|
||||
public void streamClosedReceived(String result, int id, String message) {
|
||||
Flooder flooder = null;
|
||||
synchronized (_remotePeers) {
|
||||
flooder = (Flooder)_remotePeers.remove(new Integer(id));
|
||||
}
|
||||
if (flooder != null) {
|
||||
flooder.closed();
|
||||
_log.debug("Connection " + flooder.getConnectionId() + " closed to " + flooder.getDestination());
|
||||
} else {
|
||||
_log.error("wtf, not connected to " + id + " but we were just closed?");
|
||||
}
|
||||
}
|
||||
public void streamDataReceived(int id, byte data[], int offset, int length) {
|
||||
Flooder flooder = null;
|
||||
synchronized (_remotePeers) {
|
||||
flooder = (Flooder)_remotePeers.get(new Integer(id));
|
||||
}
|
||||
long value = DataHelper.fromLong(data, 0, 4);
|
||||
if (flooder != null) {
|
||||
flooder.received(length, value);
|
||||
} else {
|
||||
_log.error("wtf, not connected to " + id + " but we received " + value + "?");
|
||||
}
|
||||
}
|
||||
public void streamConnectedReceived(String dest, int id) {
|
||||
_log.debug("Connection " + id + " received from " + dest);
|
||||
|
||||
Flooder flooder = new Flooder(id, dest);
|
||||
synchronized (_remotePeers) {
|
||||
_remotePeers.put(new Integer(id), flooder);
|
||||
}
|
||||
I2PThread t = new I2PThread(flooder, "Flood " + id);
|
||||
t.start();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean connect() {
|
||||
try {
|
||||
_samSocket = new Socket(_samHost, Integer.parseInt(_samPort));
|
||||
_samOut = _samSocket.getOutputStream();
|
||||
_samIn = _samSocket.getInputStream();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
_log.error("Unable to connect to SAM at " + _samHost + ":" + _samPort, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private String handshake() {
|
||||
synchronized (_samOut) {
|
||||
try {
|
||||
_samOut.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
|
||||
_samOut.flush();
|
||||
_log.debug("Hello sent");
|
||||
boolean ok = _eventHandler.waitForHelloReply();
|
||||
_log.debug("Hello reply found: " + ok);
|
||||
if (!ok)
|
||||
throw new IOException("wtf, hello failed?");
|
||||
String req = "SESSION CREATE STYLE=STREAM DESTINATION=" + _destFile + " " + _conOptions + "\n";
|
||||
_samOut.write(req.getBytes());
|
||||
_samOut.flush();
|
||||
_log.debug("Session create sent");
|
||||
ok = _eventHandler.waitForSessionCreateReply();
|
||||
_log.debug("Session create reply found: " + ok);
|
||||
|
||||
req = "NAMING LOOKUP NAME=ME\n";
|
||||
_samOut.write(req.getBytes());
|
||||
_samOut.flush();
|
||||
_log.debug("Naming lookup sent");
|
||||
String destination = _eventHandler.waitForNamingReply("ME");
|
||||
_log.debug("Naming lookup reply found: " + destination);
|
||||
if (destination == null) {
|
||||
_log.error("No naming lookup reply found!");
|
||||
return null;
|
||||
} else {
|
||||
_log.info(_destFile + " is located at " + destination);
|
||||
}
|
||||
return destination;
|
||||
} catch (Exception e) {
|
||||
_log.error("Error handshaking", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean writeDest(String dest) {
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream(_destFile);
|
||||
fos.write(dest.getBytes());
|
||||
fos.close();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
_log.error("Error writing to " + _destFile, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void connectWithPeers() {
|
||||
if (_peerDestFiles != null) {
|
||||
for (int i = 0; i < _peerDestFiles.length; i++) {
|
||||
try {
|
||||
FileInputStream fin = new FileInputStream(_peerDestFiles[i]);
|
||||
byte dest[] = new byte[1024];
|
||||
int read = DataHelper.read(fin, dest);
|
||||
|
||||
String remDest = new String(dest, 0, read);
|
||||
int con = 0;
|
||||
Flooder flooder = null;
|
||||
synchronized (_remotePeers) {
|
||||
con = _remotePeers.size() + 1;
|
||||
flooder = new Flooder(con, remDest);
|
||||
_remotePeers.put(new Integer(con), flooder);
|
||||
}
|
||||
|
||||
byte msg[] = ("STREAM CONNECT ID=" + con + " DESTINATION=" + remDest + "\n").getBytes();
|
||||
synchronized (_samOut) {
|
||||
_samOut.write(msg);
|
||||
_samOut.flush();
|
||||
}
|
||||
I2PThread flood = new I2PThread(flooder, "Flood " + con);
|
||||
flood.start();
|
||||
_log.debug("Starting flooder with peer from " + _peerDestFiles[i] + ": " + con);
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Unable to read the peer from " + _peerDestFiles[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Flooder implements Runnable {
|
||||
private int _connectionId;
|
||||
private String _remoteDestination;
|
||||
private boolean _closed;
|
||||
private long _started;
|
||||
private long _totalSent;
|
||||
private long _totalReceived;
|
||||
private long _lastReceived;
|
||||
private long _lastReceivedOn;
|
||||
private boolean _outOfSync;
|
||||
|
||||
public Flooder(int conId, String remDest) {
|
||||
_connectionId = conId;
|
||||
_remoteDestination = remDest;
|
||||
_closed = false;
|
||||
_outOfSync = false;
|
||||
_lastReceived = -1;
|
||||
_lastReceivedOn = _context.clock().now();
|
||||
_context.statManager().createRateStat("swarm." + conId + ".totalReceived", "Data size received", "swarm", new long[] { 30*1000, 60*1000, 5*60*1000 });
|
||||
_context.statManager().createRateStat("swarm." + conId + ".totalSent", "Data size sent", "swarm", new long[] { 30*1000, 60*1000, 5*60*1000 });
|
||||
_context.statManager().createRateStat("swarm." + conId + ".started", "When we start", "swarm", new long[] { 5*60*1000 });
|
||||
_context.statManager().createRateStat("swarm." + conId + ".lifetime", "How long we talk to a peer", "swarm", new long[] { 5*60*1000 });
|
||||
}
|
||||
|
||||
public int getConnectionId() { return _connectionId; }
|
||||
public String getDestination() { return _remoteDestination; }
|
||||
|
||||
public void closed() {
|
||||
_closed = true;
|
||||
long lifetime = _context.clock().now() - _started;
|
||||
_context.statManager().addRateData("swarm." + _connectionId + ".lifetime", lifetime, lifetime);
|
||||
}
|
||||
public void run() {
|
||||
_started = _context.clock().now();
|
||||
_context.statManager().addRateData("swarm." + _connectionId + ".started", 1, 0);
|
||||
byte data[] = new byte[32*1024];
|
||||
long value = 0;
|
||||
long lastSend = _context.clock().now();
|
||||
while (!_closed) {
|
||||
byte msg[] = ("STREAM SEND ID=" + _connectionId + " SIZE=" + data.length + "\n").getBytes();
|
||||
DataHelper.toLong(data, 0, 4, value);
|
||||
try {
|
||||
synchronized (_samOut) {
|
||||
_samOut.write(msg);
|
||||
_samOut.write(data);
|
||||
_samOut.flush();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error talking to SAM", ioe);
|
||||
return;
|
||||
}
|
||||
_totalSent += data.length;
|
||||
_context.statManager().addRateData("swarm." + _connectionId + ".totalSent", _totalSent, 0);
|
||||
value++;
|
||||
try { Thread.sleep(20); } catch (InterruptedException ie) {}
|
||||
long now = _context.clock().now();
|
||||
_log.debug("Sending " + value + " on " + _connectionId + " after " + (now-lastSend));
|
||||
lastSend = now;
|
||||
}
|
||||
}
|
||||
public void received(int len, long value) {
|
||||
_totalReceived += len;
|
||||
if ( (!_outOfSync) && (len % 32*1024 != 0) ) {
|
||||
_outOfSync = true;
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Out of sync (len=" + len + " after " + (_totalReceived-len) + ")");
|
||||
}
|
||||
_context.statManager().addRateData("swarm." + getConnectionId() + ".totalReceived", _totalReceived, 0);
|
||||
if (value != _lastReceived + 1) {
|
||||
if (!_outOfSync)
|
||||
_log.error("Received " + value + " when expecting " + (_lastReceived+1) + " on "
|
||||
+ _connectionId + " with " + _remoteDestination.substring(0,6));
|
||||
else
|
||||
_log.debug("(out of sync) Received " + value + " when expecting " + (_lastReceived+1) + " on "
|
||||
+ _connectionId + " with " + _remoteDestination.substring(0,6));
|
||||
} else {
|
||||
_log.debug("Received " + value + " on " + _connectionId + " after " + (_context.clock().now()-_lastReceivedOn)
|
||||
+ "ms with " + _remoteDestination.substring(0,6));
|
||||
}
|
||||
_lastReceived = value;
|
||||
_lastReceivedOn = _context.clock().now();
|
||||
}
|
||||
}
|
||||
}
|
@ -32,7 +32,9 @@ public class Connection {
|
||||
private long _lastSendTime;
|
||||
private long _lastSendId;
|
||||
private boolean _resetReceived;
|
||||
private boolean _resetSent;
|
||||
private boolean _connected;
|
||||
private boolean _hardDisconnected;
|
||||
private MessageInputStream _inputStream;
|
||||
private MessageOutputStream _outputStream;
|
||||
private SchedulerChooser _chooser;
|
||||
@ -70,7 +72,7 @@ public class Connection {
|
||||
private long _lifetimeDupMessageReceived;
|
||||
|
||||
public static final long MAX_RESEND_DELAY = 60*1000;
|
||||
public static final long MIN_RESEND_DELAY = 20*1000;
|
||||
public static final long MIN_RESEND_DELAY = 40*1000;
|
||||
|
||||
/** wait up to 5 minutes after disconnection so we can ack/close packets */
|
||||
public static int DISCONNECT_TIMEOUT = 5*60*1000;
|
||||
@ -112,6 +114,8 @@ public class Connection {
|
||||
_connectLock = new Object();
|
||||
_activeResends = 0;
|
||||
_context.statManager().createRateStat("stream.con.windowSizeAtCongestion", "How large was our send window when we send a dup?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.chokeSizeBegin", "How many messages were outstanding when we started to choke?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.chokeSizeEnd", "How many messages were outstanding when we stopped being choked?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
}
|
||||
|
||||
public long getNextOutboundPacketNum() {
|
||||
@ -135,9 +139,14 @@ public class Connection {
|
||||
boolean packetSendChoke(long timeoutMs) {
|
||||
if (false) return true;
|
||||
long writeExpire = timeoutMs;
|
||||
long start = _context.clock().now();
|
||||
boolean started = false;
|
||||
while (true) {
|
||||
long timeLeft = writeExpire - _context.clock().now();
|
||||
synchronized (_outboundPackets) {
|
||||
if (!started)
|
||||
_context.statManager().addRateData("stream.chokeSizeBegin", _outboundPackets.size(), timeoutMs);
|
||||
started = true;
|
||||
if (_outboundPackets.size() >= _options.getWindowSize()) {
|
||||
if (writeExpire > 0) {
|
||||
if (timeLeft <= 0) {
|
||||
@ -154,6 +163,7 @@ public class Connection {
|
||||
try { _outboundPackets.wait(); } catch (InterruptedException ie) {}
|
||||
}
|
||||
} else {
|
||||
_context.statManager().addRateData("stream.chokeSizeEnd", _outboundPackets.size(), _context.clock().now() - start);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -163,6 +173,23 @@ public class Connection {
|
||||
void ackImmediately() {
|
||||
_receiver.send(null, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* got a packet we shouldn't have, send 'em a reset
|
||||
*
|
||||
*/
|
||||
void sendReset() {
|
||||
_resetSent = true;
|
||||
if ( (_remotePeer == null) || (_sendStreamId == null) ) return;
|
||||
PacketLocal reply = new PacketLocal(_context, _remotePeer);
|
||||
reply.setFlag(Packet.FLAG_RESET);
|
||||
reply.setFlag(Packet.FLAG_SIGNATURE_INCLUDED);
|
||||
reply.setSendStreamId(_sendStreamId);
|
||||
reply.setReceiveStreamId(_receiveStreamId);
|
||||
reply.setOptionalFrom(_connectionManager.getSession().getMyDestination());
|
||||
// this just sends the packet - no retries or whatnot
|
||||
_outboundQueue.enqueue(reply);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush any data that we can
|
||||
@ -296,8 +323,8 @@ public class Connection {
|
||||
_ackedPackets++;
|
||||
if (p.getNumSends() > 1) {
|
||||
_activeResends--;
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Active resend of " + p + " successful, # active left: " + _activeResends);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Active resend of " + p + " successful, # active left: " + _activeResends);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -313,50 +340,73 @@ public class Connection {
|
||||
return acked;
|
||||
}
|
||||
|
||||
private long _occurredTime;
|
||||
private long _occurredEventCount;
|
||||
void eventOccurred() {
|
||||
_chooser.getScheduler(this).eventOccurred(this);
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
TaskScheduler sched = _chooser.getScheduler(this);
|
||||
|
||||
now = now - now % 1000;
|
||||
if (_occurredTime == now) {
|
||||
_occurredEventCount++;
|
||||
} else {
|
||||
_occurredTime = now;
|
||||
if ( (_occurredEventCount > 1000) && (_log.shouldLog(Log.WARN)) ) {
|
||||
_log.warn("More than 1000 events (" + _occurredEventCount + ") in a second on "
|
||||
+ toString() + ": scheduler = " + sched);
|
||||
}
|
||||
_occurredEventCount = 0;
|
||||
}
|
||||
|
||||
long before = System.currentTimeMillis();
|
||||
|
||||
sched.eventOccurred(this);
|
||||
long elapsed = System.currentTimeMillis() - before;
|
||||
if ( (elapsed > 1000) && (_log.shouldLog(Log.WARN)) )
|
||||
_log.warn("Took " + elapsed + "ms to pump through " + sched);
|
||||
}
|
||||
|
||||
void resetReceived() {
|
||||
_resetReceived = true;
|
||||
_outputStream.streamErrorOccurred(new IOException("Reset received"));
|
||||
_inputStream.streamErrorOccurred(new IOException("Reset received"));
|
||||
MessageOutputStream mos = _outputStream;
|
||||
MessageInputStream mis = _inputStream;
|
||||
if (mos != null)
|
||||
mos.streamErrorOccurred(new IOException("Reset received"));
|
||||
if (mis != null)
|
||||
mis.streamErrorOccurred(new IOException("Reset received"));
|
||||
_connectionError = "Connection reset";
|
||||
synchronized (_connectLock) { _connectLock.notifyAll(); }
|
||||
}
|
||||
public boolean getResetReceived() { return _resetReceived; }
|
||||
|
||||
public boolean getIsConnected() { return _connected; }
|
||||
public boolean getHardDisconnected() { return _hardDisconnected; }
|
||||
public boolean getResetSent() { return _resetSent; }
|
||||
|
||||
void disconnect(boolean cleanDisconnect) {
|
||||
disconnect(cleanDisconnect, true);
|
||||
}
|
||||
void disconnect(boolean cleanDisconnect, boolean removeFromConMgr) {
|
||||
if (!_connected) return;
|
||||
_connected = false;
|
||||
synchronized (_connectLock) { _connectLock.notifyAll(); }
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Disconnecting " + toString(), new Exception("discon"));
|
||||
|
||||
if (cleanDisconnect) {
|
||||
if (!cleanDisconnect) {
|
||||
_hardDisconnected = true;
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Hard disconnecting and sending a reset on " + toString(), new Exception("cause"));
|
||||
sendReset();
|
||||
}
|
||||
|
||||
if (cleanDisconnect && _connected) {
|
||||
// send close packets and schedule stuff...
|
||||
_outputStream.closeInternal();
|
||||
_inputStream.close();
|
||||
} else {
|
||||
doClose();
|
||||
boolean tagsCancelled = false;
|
||||
synchronized (_outboundPackets) {
|
||||
for (Iterator iter = _outboundPackets.values().iterator(); iter.hasNext(); ) {
|
||||
PacketLocal pl = (PacketLocal)iter.next();
|
||||
if ( (pl.getTagsSent() != null) && (pl.getTagsSent().size() > 0) )
|
||||
tagsCancelled = true;
|
||||
pl.cancelled();
|
||||
}
|
||||
_outboundPackets.clear();
|
||||
_outboundPackets.notifyAll();
|
||||
}
|
||||
if (tagsCancelled)
|
||||
_context.sessionKeyManager().failTags(_remotePeer.getPublicKey());
|
||||
if (_connected)
|
||||
doClose();
|
||||
killOutstandingPackets();
|
||||
}
|
||||
if (removeFromConMgr) {
|
||||
if (!_disconnectScheduled) {
|
||||
@ -364,6 +414,7 @@ public class Connection {
|
||||
SimpleTimer.getInstance().addEvent(new DisconnectEvent(), DISCONNECT_TIMEOUT);
|
||||
}
|
||||
}
|
||||
_connected = false;
|
||||
}
|
||||
|
||||
void disconnectComplete() {
|
||||
@ -371,17 +422,12 @@ public class Connection {
|
||||
if (_socket != null)
|
||||
_socket.destroy();
|
||||
_socket = null;
|
||||
_inputStream = null;
|
||||
if (_outputStream != null)
|
||||
_outputStream.destroy();
|
||||
_outputStream = null;
|
||||
_outboundQueue = null;
|
||||
_handler = null;
|
||||
if (_receiver != null)
|
||||
_receiver.destroy();
|
||||
_receiver = null;
|
||||
if (_activityTimer != null)
|
||||
SimpleTimer.getInstance().addEvent(_activityTimer, 1);
|
||||
SimpleTimer.getInstance().removeEvent(_activityTimer);
|
||||
_activityTimer = null;
|
||||
|
||||
if (!_disconnectScheduled) {
|
||||
@ -393,6 +439,10 @@ public class Connection {
|
||||
_connectionManager.removeConnection(this);
|
||||
}
|
||||
|
||||
killOutstandingPackets();
|
||||
}
|
||||
|
||||
private void killOutstandingPackets() {
|
||||
boolean tagsCancelled = false;
|
||||
synchronized (_outboundPackets) {
|
||||
for (Iterator iter = _outboundPackets.values().iterator(); iter.hasNext(); ) {
|
||||
@ -406,7 +456,6 @@ public class Connection {
|
||||
}
|
||||
if (tagsCancelled)
|
||||
_context.sessionKeyManager().failTags(_remotePeer.getPublicKey());
|
||||
|
||||
}
|
||||
|
||||
private class DisconnectEvent implements SimpleTimer.TimedEvent {
|
||||
@ -416,6 +465,7 @@ public class Connection {
|
||||
+ Connection.this.toString());
|
||||
}
|
||||
public void timeReached() {
|
||||
killOutstandingPackets();
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Connection disconnect timer complete, drop the con "
|
||||
+ Connection.this.toString());
|
||||
@ -671,6 +721,9 @@ public class Connection {
|
||||
buf.append(" wsize: ").append(_options.getWindowSize());
|
||||
buf.append(" cwin: ").append(_congestionWindowEnd - _highestAckedThrough);
|
||||
buf.append(" rtt: ").append(_options.getRTT());
|
||||
// not synchronized to avoid some kooky races
|
||||
buf.append(" unacked outbound: ").append(_outboundPackets.size()).append(" ");
|
||||
/*
|
||||
buf.append(" unacked outbound: ");
|
||||
synchronized (_outboundPackets) {
|
||||
buf.append(_outboundPackets.size()).append(" [");
|
||||
@ -679,6 +732,7 @@ public class Connection {
|
||||
}
|
||||
buf.append("] ");
|
||||
}
|
||||
*/
|
||||
buf.append("unacked inbound? ").append(getUnackedPacketsReceived());
|
||||
if (_inputStream != null) {
|
||||
buf.append(" [high ");
|
||||
@ -702,6 +756,7 @@ public class Connection {
|
||||
public ResendPacketEvent(PacketLocal packet) {
|
||||
_packet = packet;
|
||||
_currentIsActiveResend = false;
|
||||
packet.setResendPacketEvent(ResendPacketEvent.this);
|
||||
}
|
||||
|
||||
public void timeReached() {
|
||||
|
@ -26,6 +26,10 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
|
||||
_dummyStatus = new DummyStatus();
|
||||
}
|
||||
|
||||
public boolean writeInProcess() {
|
||||
return _connection.getUnackedPacketsSent() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send some data through the connection, or if there is no new data, this
|
||||
* may generate a packet with a plain ACK/NACK or CLOSE, or nothing whatsoever
|
||||
@ -63,7 +67,11 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
|
||||
|
||||
if (doSend) {
|
||||
PacketLocal packet = send(buf, off, size);
|
||||
return packet;
|
||||
//dont wait for non-acks
|
||||
if ( (packet.getSequenceNum() > 0) || (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) )
|
||||
return packet;
|
||||
else
|
||||
return _dummyStatus;
|
||||
} else {
|
||||
return _dummyStatus;
|
||||
}
|
||||
@ -91,8 +99,16 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
|
||||
* @return the packet sent
|
||||
*/
|
||||
public PacketLocal send(byte buf[], int off, int size, boolean forceIncrement) {
|
||||
long before = System.currentTimeMillis();
|
||||
PacketLocal packet = buildPacket(buf, off, size, forceIncrement);
|
||||
long built = System.currentTimeMillis();
|
||||
_connection.sendPacket(packet);
|
||||
long sent = System.currentTimeMillis();
|
||||
|
||||
if ( (built-before > 1000) && (_log.shouldLog(Log.WARN)) )
|
||||
_log.warn("wtf, took " + (built-before) + "ms to build a packet: " + packet);
|
||||
if ( (sent-built> 1000) && (_log.shouldLog(Log.WARN)) )
|
||||
_log.warn("wtf, took " + (sent-built) + "ms to send a packet: " + packet);
|
||||
return packet;
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,14 @@ class ConnectionHandler {
|
||||
_acceptTimeout = DEFAULT_ACCEPT_TIMEOUT;
|
||||
}
|
||||
|
||||
public void setActive(boolean active) { _active = active; }
|
||||
public void setActive(boolean active) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("setActive(" + active + ") called");
|
||||
synchronized (_synQueue) {
|
||||
_active = active;
|
||||
_synQueue.notifyAll(); // so we break from the accept()
|
||||
}
|
||||
}
|
||||
public boolean getActive() { return _active; }
|
||||
|
||||
public void receiveNewSyn(Packet packet) {
|
||||
@ -66,8 +73,17 @@ class ConnectionHandler {
|
||||
while (true) {
|
||||
if ( (timeoutMs > 0) && (expiration < _context.clock().now()) )
|
||||
return null;
|
||||
if (!_active)
|
||||
if (!_active) {
|
||||
// fail all the ones we had queued up
|
||||
synchronized (_synQueue) {
|
||||
for (int i = 0; i < _synQueue.size(); i++) {
|
||||
Packet packet = (Packet)_synQueue.get(i);
|
||||
sendReset(packet);
|
||||
}
|
||||
_synQueue.clear();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Packet syn = null;
|
||||
synchronized (_synQueue) {
|
||||
|
@ -366,7 +366,8 @@ public class ConnectionManager {
|
||||
if (removed) {
|
||||
if (_notifier != null)
|
||||
_notifier.pingComplete(false);
|
||||
_log.error("Ping failed");
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Ping failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,8 +87,10 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
setMaxResends(getInt(opts, PROP_MAX_RESENDS, 5));
|
||||
setWriteTimeout(getInt(opts, PROP_WRITE_TIMEOUT, -1));
|
||||
setInactivityTimeout(getInt(opts, PROP_INACTIVITY_TIMEOUT, 5*60*1000));
|
||||
setInactivityAction(getInt(opts, PROP_INACTIVITY_ACTION, INACTIVITY_ACTION_SEND));
|
||||
setInactivityAction(getInt(opts, PROP_INACTIVITY_ACTION, INACTIVITY_ACTION_DISCONNECT));
|
||||
setInboundBufferSize((getMaxMessageSize() + 2) * Connection.MAX_WINDOW_SIZE);
|
||||
|
||||
setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,17 +25,43 @@ public class ConnectionPacketHandler {
|
||||
_context.statManager().createRateStat("stream.con.receiveDuplicateSize", "Size of a duplicate message received on a connection", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.con.packetsAckedPerMessageReceived", "Size of a duplicate message received on a connection", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.sendsBeforeAck", "How many times a message was sent before it was ACKed?", "Stream", new long[] { 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.resetReceived", "How many messages had we sent successfully before receiving a RESET?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
|
||||
}
|
||||
|
||||
/** distribute a packet to the connection specified */
|
||||
void receivePacket(Packet packet, Connection con) throws I2PException {
|
||||
boolean ok = verifyPacket(packet, con);
|
||||
if (!ok) return;
|
||||
if (!ok) {
|
||||
if ( (!packet.isFlagSet(Packet.FLAG_RESET)) && (_log.shouldLog(Log.ERROR)) )
|
||||
_log.error("Packet does NOT verify: " + packet);
|
||||
return;
|
||||
}
|
||||
|
||||
if (con.getHardDisconnected()) {
|
||||
if ( (packet.getSequenceNum() > 0) || (packet.getPayloadSize() > 0) ||
|
||||
(packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) || (packet.isFlagSet(Packet.FLAG_CLOSE)) ) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Received a data packet after hard disconnect: " + packet + " on " + con);
|
||||
con.sendReset();
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Received a packet after hard disconnect, ignoring: " + packet + " on " + con);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
con.packetReceived();
|
||||
|
||||
if (con.getInputStream().getTotalQueuedSize() > con.getOptions().getInboundBufferSize()) {
|
||||
long ready = con.getInputStream().getHighestReadyBockId();
|
||||
int available = con.getOptions().getInboundBufferSize() - con.getInputStream().getTotalReadySize();
|
||||
int allowedBlocks = available/con.getOptions().getMaxMessageSize();
|
||||
if (packet.getSequenceNum() > ready + allowedBlocks) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Inbound buffer exceeded on connection " + con + ": dropping " + packet);
|
||||
_log.warn("Inbound buffer exceeded on connection " + con + " ("
|
||||
+ ready + "/"+ (ready+allowedBlocks) + "/" + available
|
||||
+ ": dropping " + packet);
|
||||
ack(con, packet.getAckThrough(), packet.getNacks(), null, false);
|
||||
con.getOptions().setChoke(5*1000);
|
||||
return;
|
||||
}
|
||||
@ -55,6 +81,8 @@ public class ConnectionPacketHandler {
|
||||
if (packet.isFlagSet(Packet.FLAG_CLOSE) && packet.isFlagSet(Packet.FLAG_SIGNATURE_INCLUDED))
|
||||
con.closeReceived();
|
||||
|
||||
boolean fastAck = false;
|
||||
|
||||
if (isNew) {
|
||||
con.incrementUnackedPacketsReceived();
|
||||
con.incrementBytesReceived(packet.getPayloadSize());
|
||||
@ -72,7 +100,8 @@ public class ConnectionPacketHandler {
|
||||
_log.debug("Scheduling ack in " + delay + "ms for received packet " + packet);
|
||||
}
|
||||
} else {
|
||||
if ( (packet.getSequenceNum() > 0) || (packet.getPayloadSize() > 0) ) {
|
||||
if ( (packet.getSequenceNum() > 0) || (packet.getPayloadSize() > 0) ||
|
||||
(packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) ) {
|
||||
_context.statManager().addRateData("stream.con.receiveDuplicateSize", packet.getPayloadSize(), 0);
|
||||
con.incrementDupMessagesReceived(1);
|
||||
|
||||
@ -83,7 +112,8 @@ public class ConnectionPacketHandler {
|
||||
_log.warn("congestion.. dup " + packet);
|
||||
SimpleTimer.getInstance().addEvent(new AckDup(con), con.getOptions().getSendAckDelay());
|
||||
//con.incrementUnackedPacketsReceived();
|
||||
con.setNextSendTime(_context.clock().now() + con.getOptions().getSendAckDelay());
|
||||
//con.setNextSendTime(_context.clock().now() + con.getOptions().getSendAckDelay());
|
||||
fastAck = true;
|
||||
} else {
|
||||
if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) {
|
||||
//con.incrementUnackedPacketsReceived();
|
||||
@ -95,8 +125,20 @@ public class ConnectionPacketHandler {
|
||||
}
|
||||
}
|
||||
|
||||
fastAck = fastAck || ack(con, packet.getAckThrough(), packet.getNacks(), packet, isNew);
|
||||
con.eventOccurred();
|
||||
if (fastAck) {
|
||||
if (con.getLastSendTime() + 1000 < _context.clock().now()) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Fast ack for dup " + packet);
|
||||
con.ackImmediately();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean ack(Connection con, long ackThrough, long nacks[], Packet packet, boolean isNew) {
|
||||
int numResends = 0;
|
||||
List acked = con.ackPackets(packet.getAckThrough(), packet.getNacks());
|
||||
List acked = con.ackPackets(ackThrough, nacks);
|
||||
if ( (acked != null) && (acked.size() > 0) ) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(acked.size() + " of our packets acked with " + packet);
|
||||
@ -130,18 +172,15 @@ public class ConnectionPacketHandler {
|
||||
_context.statManager().addRateData("stream.con.packetsAckedPerMessageReceived", acked.size(), highestRTT);
|
||||
}
|
||||
|
||||
boolean fastAck = adjustWindow(con, isNew, packet.getSequenceNum(), numResends, (acked != null ? acked.size() : 0));
|
||||
con.eventOccurred();
|
||||
if (fastAck) {
|
||||
if (con.getLastSendTime() + con.getOptions().getRTT() < _context.clock().now()) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Fast ack for dup " + packet);
|
||||
con.ackImmediately();
|
||||
}
|
||||
}
|
||||
if (packet != null)
|
||||
return adjustWindow(con, isNew, packet.getSequenceNum(), numResends, (acked != null ? acked.size() : 0));
|
||||
else
|
||||
return adjustWindow(con, false, -1, numResends, (acked != null ? acked.size() : 0));
|
||||
}
|
||||
|
||||
|
||||
private boolean adjustWindow(Connection con, boolean isNew, long sequenceNum, int numResends, int acked) {
|
||||
boolean congested = false;
|
||||
if ( (!isNew) && (sequenceNum > 0) ) {
|
||||
// dup real packet
|
||||
int oldSize = con.getOptions().getWindowSize();
|
||||
@ -156,62 +195,38 @@ public class ConnectionPacketHandler {
|
||||
+ con.getLastCongestionSeenAt() + " (#resends: " + numResends
|
||||
+ ") for " + con);
|
||||
|
||||
return true;
|
||||
} else if (numResends > 0) {
|
||||
// window sizes are shrunk on resend, not on ack
|
||||
} else {
|
||||
if (acked > 0) {
|
||||
long lowest = con.getHighestAckedThrough();
|
||||
if (lowest >= con.getCongestionWindowEnd()) {
|
||||
// new packet that ack'ed uncongested data, or an empty ack
|
||||
int newWindowSize = con.getOptions().getWindowSize();
|
||||
|
||||
if (newWindowSize > con.getLastCongestionSeenAt() / 2) {
|
||||
// congestion avoidance
|
||||
|
||||
// we can't use newWindowSize += 1/newWindowSize, since we're
|
||||
// integers, so lets use a random distribution instead
|
||||
int shouldIncrement = _context.random().nextInt(newWindowSize);
|
||||
if (shouldIncrement <= 0)
|
||||
newWindowSize += 1;
|
||||
} else {
|
||||
// slow start
|
||||
congested = true;
|
||||
}
|
||||
|
||||
long lowest = con.getHighestAckedThrough();
|
||||
if (lowest >= con.getCongestionWindowEnd()) {
|
||||
// new packet that ack'ed uncongested data, or an empty ack
|
||||
int newWindowSize = con.getOptions().getWindowSize();
|
||||
|
||||
if ( (!congested) && (acked > 0) && (numResends <= 0) ) {
|
||||
if (newWindowSize > con.getLastCongestionSeenAt() / 2) {
|
||||
// congestion avoidance
|
||||
|
||||
// we can't use newWindowSize += 1/newWindowSize, since we're
|
||||
// integers, so lets use a random distribution instead
|
||||
int shouldIncrement = _context.random().nextInt(newWindowSize);
|
||||
if (shouldIncrement <= 0)
|
||||
newWindowSize += 1;
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("New window size " + newWindowSize + " congestionSeenAt: "
|
||||
+ con.getLastCongestionSeenAt() + " (#resends: " + numResends
|
||||
+ ") for " + con);
|
||||
con.getOptions().setWindowSize(newWindowSize);
|
||||
con.setCongestionWindowEnd(newWindowSize + lowest);
|
||||
} else {
|
||||
// slow start
|
||||
newWindowSize += 1;
|
||||
}
|
||||
} else {
|
||||
// received a message that doesn't contain a new ack
|
||||
|
||||
// ehh. cant do this, as we SACK and the acks may be
|
||||
// received out of order:
|
||||
// Alice: RECEIVE 2
|
||||
// Alice: SEND ack 2 nack 1
|
||||
// Alice: RECEIVE 1
|
||||
// Alice: SEND ack 2
|
||||
// Bob: RECEIVE ack 2
|
||||
// Bob: RECEIVE ack 2 nack 1 <-- NOT bad
|
||||
|
||||
/*
|
||||
if (con.getUnackedPacketsSent() > 0) {
|
||||
// peer got a dup
|
||||
int oldSize = con.getOptions().getWindowSize();
|
||||
oldSize >>>= 1;
|
||||
if (oldSize <= 0)
|
||||
oldSize = 1;
|
||||
con.getOptions().setWindowSize(oldSize);
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("New window size " + newWindowSize + " congestionSeenAt: "
|
||||
+ con.getLastCongestionSeenAt() + " (#resends: " + numResends
|
||||
+ ") for " + con);
|
||||
con.getOptions().setWindowSize(newWindowSize);
|
||||
con.setCongestionWindowEnd(newWindowSize + lowest);
|
||||
}
|
||||
return false;
|
||||
|
||||
return congested;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -269,6 +284,8 @@ public class ConnectionPacketHandler {
|
||||
con.resetReceived();
|
||||
con.eventOccurred();
|
||||
|
||||
_context.statManager().addRateData("stream.resetReceived", con.getHighestAckedThrough(), con.getLifetime());
|
||||
|
||||
// no further processing
|
||||
return;
|
||||
}
|
||||
|
@ -32,6 +32,8 @@ public class I2PSocketFull implements I2PSocket {
|
||||
destroy();
|
||||
}
|
||||
|
||||
Connection getConnection() { return _connection; }
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return _connection.getInputStream();
|
||||
}
|
||||
|
@ -33,9 +33,9 @@ public class MessageInputStream extends InputStream {
|
||||
private List _readyDataBlocks;
|
||||
private int _readyDataBlockIndex;
|
||||
/** highest message ID used in the readyDataBlocks */
|
||||
private long _highestReadyBlockId;
|
||||
private volatile long _highestReadyBlockId;
|
||||
/** highest overall message ID */
|
||||
private long _highestBlockId;
|
||||
private volatile long _highestBlockId;
|
||||
/**
|
||||
* Message ID (Long) to ByteArray for blocks received
|
||||
* out of order when there are lower IDs not yet
|
||||
@ -74,15 +74,13 @@ public class MessageInputStream extends InputStream {
|
||||
|
||||
/** What is the highest block ID we've completely received through? */
|
||||
public long getHighestReadyBockId() {
|
||||
synchronized (_dataLock) {
|
||||
return _highestReadyBlockId;
|
||||
}
|
||||
// not synchronized as it doesnt hurt to read a too-low value
|
||||
return _highestReadyBlockId;
|
||||
}
|
||||
|
||||
public long getHighestBlockId() {
|
||||
synchronized (_dataLock) {
|
||||
return _highestBlockId;
|
||||
}
|
||||
// not synchronized as it doesnt hurt to read a too-low value
|
||||
return _highestBlockId;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -272,6 +270,9 @@ public class MessageInputStream extends InputStream {
|
||||
// at least one byte
|
||||
|
||||
while (_readyDataBlocks.size() <= 0) {
|
||||
if (_locallyClosed)
|
||||
throw new IOException("Already closed, you wanker");
|
||||
|
||||
if ( (_notYetReadyBlocks.size() <= 0) && (_closeReceived) ) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("read(...," + offset + ", " + length + ")[" + i
|
||||
@ -391,6 +392,21 @@ public class MessageInputStream extends InputStream {
|
||||
}
|
||||
}
|
||||
|
||||
public int getTotalReadySize() {
|
||||
synchronized (_dataLock) {
|
||||
if (_locallyClosed) return 0;
|
||||
int numBytes = 0;
|
||||
for (int i = 0; i < _readyDataBlocks.size(); i++) {
|
||||
ByteArray cur = (ByteArray)_readyDataBlocks.get(i);
|
||||
if (i == 0)
|
||||
numBytes += cur.getData().length - _readyDataBlockIndex;
|
||||
else
|
||||
numBytes += cur.getData().length;
|
||||
}
|
||||
return numBytes;
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
synchronized (_dataLock) {
|
||||
_readyDataBlocks.clear();
|
||||
@ -402,6 +418,7 @@ public class MessageInputStream extends InputStream {
|
||||
ba.setData(null);
|
||||
}
|
||||
_locallyClosed = true;
|
||||
_dataLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.util.ByteCache;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
|
||||
/**
|
||||
* A stream that we can shove data into that fires off those bytes
|
||||
@ -26,6 +27,11 @@ public class MessageOutputStream extends OutputStream {
|
||||
private long _written;
|
||||
private int _writeTimeout;
|
||||
private ByteCache _dataCache;
|
||||
private Flusher _flusher;
|
||||
private long _lastFlushed;
|
||||
private long _lastBuffered;
|
||||
/** if we enqueue data but don't flush it in this period, flush it passively */
|
||||
private int _passiveFlushDelay;
|
||||
|
||||
public MessageOutputStream(I2PAppContext ctx, DataReceiver receiver) {
|
||||
this(ctx, receiver, Packet.MAX_PAYLOAD_SIZE);
|
||||
@ -41,6 +47,10 @@ public class MessageOutputStream extends OutputStream {
|
||||
_written = 0;
|
||||
_closed = false;
|
||||
_writeTimeout = -1;
|
||||
_passiveFlushDelay = 500;
|
||||
_flusher = new Flusher();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("MessageOutputStream created");
|
||||
}
|
||||
|
||||
public void setWriteTimeout(int ms) { _writeTimeout = ms; }
|
||||
@ -51,10 +61,11 @@ public class MessageOutputStream extends OutputStream {
|
||||
}
|
||||
|
||||
public void write(byte b[], int off, int len) throws IOException {
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("write(b[], " + off + ", " + len + ")");
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("write(b[], " + off + ", " + len + ") ");
|
||||
int cur = off;
|
||||
int remaining = len;
|
||||
long begin = _context.clock().now();
|
||||
while (remaining > 0) {
|
||||
WriteStatus ws = null;
|
||||
// we do any waiting outside the synchronized() block because we
|
||||
@ -70,6 +81,10 @@ public class MessageOutputStream extends OutputStream {
|
||||
cur += remaining;
|
||||
_written += remaining;
|
||||
remaining = 0;
|
||||
_lastBuffered = _context.clock().now();
|
||||
if (_passiveFlushDelay > 0) {
|
||||
_flusher.enqueue();
|
||||
}
|
||||
} else {
|
||||
// buffer whatever we can fit then flush,
|
||||
// repeating until we've pushed all of the
|
||||
@ -79,25 +94,38 @@ public class MessageOutputStream extends OutputStream {
|
||||
remaining -= toWrite;
|
||||
cur += toWrite;
|
||||
_valid = _buf.length;
|
||||
if (_dataReceiver == null) {
|
||||
throwAnyError();
|
||||
return;
|
||||
}
|
||||
ws = _dataReceiver.writeData(_buf, 0, _valid);
|
||||
_written += _valid;
|
||||
_valid = 0;
|
||||
throwAnyError();
|
||||
_lastFlushed = _context.clock().now();
|
||||
}
|
||||
}
|
||||
if (ws != null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Waiting " + _writeTimeout + "ms for accept of " + ws);
|
||||
// ok, we've actually added a new packet - lets wait until
|
||||
// its accepted into the queue before moving on (so that we
|
||||
// dont fill our buffer instantly)
|
||||
ws.waitForAccept(_writeTimeout);
|
||||
if (!ws.writeAccepted()) {
|
||||
if (_writeTimeout > 0)
|
||||
throw new InterruptedIOException("Write not accepted within timeout");
|
||||
throw new InterruptedIOException("Write not accepted within timeout: " + ws);
|
||||
else
|
||||
throw new IOException("Write not accepted into the queue");
|
||||
throw new IOException("Write not accepted into the queue: " + ws);
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Queued " + len + " without sending to the receiver");
|
||||
}
|
||||
}
|
||||
long elapsed = _context.clock().now() - begin;
|
||||
if ( (elapsed > 10*1000) && (_log.shouldLog(Log.DEBUG)) )
|
||||
_log.debug("wtf, took " + elapsed + "ms to write to the stream?", new Exception("foo"));
|
||||
throwAnyError();
|
||||
}
|
||||
|
||||
@ -106,6 +134,66 @@ public class MessageOutputStream extends OutputStream {
|
||||
throwAnyError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush data that has been enqued but not flushed after a certain
|
||||
* period of inactivity
|
||||
*/
|
||||
private class Flusher implements SimpleTimer.TimedEvent {
|
||||
private boolean _enqueued;
|
||||
public void enqueue() {
|
||||
// no need to be overly worried about duplicates - it would just
|
||||
// push it further out
|
||||
if (!_enqueued) {
|
||||
SimpleTimer.getInstance().addEvent(_flusher, _passiveFlushDelay);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Enqueueing the flusher for " + _passiveFlushDelay + "ms out");
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("NOT enqueing the flusher");
|
||||
}
|
||||
_enqueued = true;
|
||||
}
|
||||
public void timeReached() {
|
||||
_enqueued = false;
|
||||
DataReceiver rec = _dataReceiver;
|
||||
long timeLeft = (_lastBuffered + _passiveFlushDelay - _context.clock().now());
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("flusher time reached: left = " + timeLeft);
|
||||
if (timeLeft > 0)
|
||||
enqueue();
|
||||
else if ( (rec != null) && (rec.writeInProcess()) )
|
||||
enqueue(); // don't passive flush if there is a write being done (unacked outbound)
|
||||
else
|
||||
doFlush();
|
||||
}
|
||||
|
||||
private void doFlush() {
|
||||
boolean sent = false;
|
||||
WriteStatus ws = null;
|
||||
synchronized (_dataLock) {
|
||||
long flushTime = _lastBuffered + _passiveFlushDelay;
|
||||
if ( (_valid > 0) && (flushTime < _context.clock().now()) ) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("doFlush() valid = " + _valid);
|
||||
if ( (_buf != null) && (_dataReceiver != null) ) {
|
||||
ws = _dataReceiver.writeData(_buf, 0, _valid);
|
||||
_written += _valid;
|
||||
_valid = 0;
|
||||
_lastFlushed = _context.clock().now();
|
||||
_dataLock.notifyAll();
|
||||
sent = true;
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("doFlush() rejected... valid = " + _valid);
|
||||
}
|
||||
}
|
||||
// ignore the ws
|
||||
if (sent && _log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Passive flush of " + ws);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the data already queued up, blocking until it has been
|
||||
* delivered.
|
||||
@ -114,12 +202,18 @@ public class MessageOutputStream extends OutputStream {
|
||||
* @throws InterruptedIOException if the write times out
|
||||
*/
|
||||
public void flush() throws IOException {
|
||||
long begin = _context.clock().now();
|
||||
WriteStatus ws = null;
|
||||
synchronized (_dataLock) {
|
||||
if (_buf == null) throw new IOException("closed (buffer went away)");
|
||||
if (_dataReceiver == null) {
|
||||
throwAnyError();
|
||||
return;
|
||||
}
|
||||
ws = _dataReceiver.writeData(_buf, 0, _valid);
|
||||
_written += _valid;
|
||||
_valid = 0;
|
||||
_lastFlushed = _context.clock().now();
|
||||
_dataLock.notifyAll();
|
||||
}
|
||||
|
||||
@ -129,6 +223,8 @@ public class MessageOutputStream extends OutputStream {
|
||||
( (_writeTimeout > Connection.DISCONNECT_TIMEOUT) ||
|
||||
(_writeTimeout <= 0) ) )
|
||||
ws.waitForCompletion(Connection.DISCONNECT_TIMEOUT);
|
||||
else if ( (_writeTimeout <= 0) || (_writeTimeout > Connection.DISCONNECT_TIMEOUT) )
|
||||
ws.waitForCompletion(Connection.DISCONNECT_TIMEOUT);
|
||||
else
|
||||
ws.waitForCompletion(_writeTimeout);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@ -137,6 +233,10 @@ public class MessageOutputStream extends OutputStream {
|
||||
throw new InterruptedIOException("Timed out during write");
|
||||
else if (ws.writeFailed())
|
||||
throw new IOException("Write failed");
|
||||
|
||||
long elapsed = _context.clock().now() - begin;
|
||||
if ( (elapsed > 10*1000) && (_log.shouldLog(Log.DEBUG)) )
|
||||
_log.debug("wtf, took " + elapsed + "ms to flush the stream?\n" + ws, new Exception("bar"));
|
||||
throwAnyError();
|
||||
}
|
||||
|
||||
@ -164,7 +264,8 @@ public class MessageOutputStream extends OutputStream {
|
||||
ByteArray ba = null;
|
||||
synchronized (_dataLock) {
|
||||
// flush any data, but don't wait for it
|
||||
_dataReceiver.writeData(_buf, 0, _valid);
|
||||
if (_dataReceiver != null)
|
||||
_dataReceiver.writeData(_buf, 0, _valid);
|
||||
_written += _valid;
|
||||
_valid = 0;
|
||||
|
||||
@ -173,6 +274,7 @@ public class MessageOutputStream extends OutputStream {
|
||||
_buf = null;
|
||||
_valid = 0;
|
||||
}
|
||||
_lastFlushed = _context.clock().now();
|
||||
_dataLock.notifyAll();
|
||||
}
|
||||
if (ba != null) {
|
||||
@ -205,6 +307,7 @@ public class MessageOutputStream extends OutputStream {
|
||||
}
|
||||
void flushAvailable(DataReceiver target, boolean blocking) throws IOException {
|
||||
WriteStatus ws = null;
|
||||
long before = System.currentTimeMillis();
|
||||
synchronized (_dataLock) {
|
||||
// _buf may be null, but the data receiver can handle that just fine,
|
||||
// deciding whether or not to send a packet
|
||||
@ -212,7 +315,12 @@ public class MessageOutputStream extends OutputStream {
|
||||
_written += _valid;
|
||||
_valid = 0;
|
||||
_dataLock.notifyAll();
|
||||
_lastFlushed = _context.clock().now();
|
||||
}
|
||||
long afterBuild = System.currentTimeMillis();
|
||||
if ( (afterBuild - before > 1000) && (_log.shouldLog(Log.DEBUG)) )
|
||||
_log.debug("Took " + (afterBuild-before) + "ms to build a packet? " + ws);
|
||||
|
||||
if (blocking && ws != null) {
|
||||
ws.waitForAccept(_writeTimeout);
|
||||
if (ws.writeFailed())
|
||||
@ -220,6 +328,9 @@ public class MessageOutputStream extends OutputStream {
|
||||
else if (!ws.writeAccepted())
|
||||
throw new InterruptedIOException("Flush available timed out");
|
||||
}
|
||||
long afterAccept = System.currentTimeMillis();
|
||||
if ( (afterAccept - afterBuild > 1000) && (_log.shouldLog(Log.DEBUG)) )
|
||||
_log.debug("Took " + (afterAccept-afterBuild) + "ms to accept a packet? " + ws);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -233,6 +344,7 @@ public class MessageOutputStream extends OutputStream {
|
||||
* Nonblocking write
|
||||
*/
|
||||
public WriteStatus writeData(byte buf[], int off, int size);
|
||||
public boolean writeInProcess();
|
||||
}
|
||||
|
||||
/** Define a way to detect the status of a write */
|
||||
|
@ -104,7 +104,7 @@ public class PacketHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private static final SimpleDateFormat _fmt = new SimpleDateFormat("hh:mm:ss.SSS");
|
||||
private static final SimpleDateFormat _fmt = new SimpleDateFormat("HH:mm:ss.SSS");
|
||||
void displayPacket(Packet packet, String prefix) {
|
||||
String msg = null;
|
||||
synchronized (_fmt) {
|
||||
@ -152,16 +152,28 @@ public class PacketHandler {
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Receive a syn packet with the wrong IDs: " + packet);
|
||||
_log.warn("Receive a syn packet with the wrong IDs, sending reset: " + packet);
|
||||
sendReset(packet);
|
||||
}
|
||||
} else {
|
||||
// someone is sending us a packet on the wrong stream
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Received a packet on the wrong stream: " + packet);
|
||||
_log.warn("Received a packet on the wrong stream: " + packet + " connection: " + con);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendReset(Packet packet) {
|
||||
PacketLocal reply = new PacketLocal(_context, packet.getOptionalFrom());
|
||||
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 receiveUnknownCon(Packet packet, byte sendId[]) {
|
||||
if (packet.isFlagSet(Packet.FLAG_ECHO)) {
|
||||
if (packet.getSendStreamId() != null) {
|
||||
|
@ -5,6 +5,8 @@ import java.util.Set;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
|
||||
/**
|
||||
* coordinate local attributes about a packet - send time, ack time, number of
|
||||
@ -12,6 +14,7 @@ import net.i2p.data.SessionKey;
|
||||
*/
|
||||
public class PacketLocal extends Packet implements MessageOutputStream.WriteStatus {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private Connection _connection;
|
||||
private Destination _to;
|
||||
private SessionKey _keyUsed;
|
||||
@ -22,6 +25,7 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
|
||||
private long _acceptedOn;
|
||||
private long _ackOn;
|
||||
private long _cancelledOn;
|
||||
private SimpleTimer.TimedEvent _resendEvent;
|
||||
|
||||
public PacketLocal(I2PAppContext ctx, Destination to) {
|
||||
this(ctx, to, null);
|
||||
@ -29,6 +33,7 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
|
||||
public PacketLocal(I2PAppContext ctx, Destination to, Connection con) {
|
||||
_context = ctx;
|
||||
_createdOn = ctx.clock().now();
|
||||
_log = ctx.logManager().getLog(PacketLocal.class);
|
||||
_to = to;
|
||||
_connection = con;
|
||||
_lastSend = -1;
|
||||
@ -78,14 +83,16 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
|
||||
_ackOn = _context.clock().now();
|
||||
notifyAll();
|
||||
}
|
||||
_connection = null;
|
||||
SimpleTimer.getInstance().removeEvent(_resendEvent);
|
||||
}
|
||||
public void cancelled() {
|
||||
synchronized (this) {
|
||||
_cancelledOn = _context.clock().now();
|
||||
notifyAll();
|
||||
}
|
||||
_connection = null;
|
||||
SimpleTimer.getInstance().removeEvent(_resendEvent);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Cancelled! " + toString(), new Exception("cancelled"));
|
||||
}
|
||||
|
||||
/** how long after packet creation was it acked? */
|
||||
@ -99,6 +106,8 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
|
||||
public long getLastSend() { return _lastSend; }
|
||||
public Connection getConnection() { return _connection; }
|
||||
|
||||
public void setResendPacketEvent(SimpleTimer.TimedEvent evt) { _resendEvent = evt; }
|
||||
|
||||
public String toString() {
|
||||
String str = super.toString();
|
||||
if (_ackOn > 0)
|
||||
@ -110,17 +119,27 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
|
||||
public void waitForAccept(int maxWaitMs) {
|
||||
if (_connection == null)
|
||||
throw new IllegalStateException("Cannot wait for accept with no connection");
|
||||
long expiration = _context.clock().now()+maxWaitMs;
|
||||
long before = _context.clock().now();
|
||||
long expiration = before+maxWaitMs;
|
||||
int queued = _connection.getUnackedPacketsSent();
|
||||
int window = _connection.getOptions().getWindowSize();
|
||||
boolean accepted = _connection.packetSendChoke(maxWaitMs);
|
||||
long after = _context.clock().now();
|
||||
if (accepted)
|
||||
_acceptedOn = _context.clock().now();
|
||||
_acceptedOn = after;
|
||||
else
|
||||
_acceptedOn = -1;
|
||||
_connection = null;
|
||||
int afterQueued = _connection.getUnackedPacketsSent();
|
||||
if ( (after - before > 1000) && (_log.shouldLog(Log.DEBUG)) )
|
||||
_log.debug("Took " + (after-before) + "ms to get "
|
||||
+ (accepted ? " accepted" : " rejected")
|
||||
+ (_cancelledOn > 0 ? " and CANCELLED" : "")
|
||||
+ ", queued behind " + queued +" with a window size of " + window
|
||||
+ ", finally accepted with " + afterQueued + " queued: "
|
||||
+ toString());
|
||||
}
|
||||
|
||||
public void waitForCompletion(int maxWaitMs) {
|
||||
_connection = null;
|
||||
long expiration = _context.clock().now()+maxWaitMs;
|
||||
while (true) {
|
||||
long timeRemaining = expiration - _context.clock().now();
|
||||
|
@ -24,24 +24,17 @@ class PacketQueue {
|
||||
private Log _log;
|
||||
private I2PSession _session;
|
||||
private ConnectionManager _connectionManager;
|
||||
private byte _buf[];
|
||||
private ByteCache _cache = ByteCache.getInstance(64, 36*1024);
|
||||
|
||||
public PacketQueue(I2PAppContext context, I2PSession session, ConnectionManager mgr) {
|
||||
_context = context;
|
||||
_session = session;
|
||||
_connectionManager = mgr;
|
||||
_buf = _cache.acquire().getData(); // new byte[36*1024];
|
||||
_log = context.logManager().getLog(PacketQueue.class);
|
||||
_context.statManager().createRateStat("stream.con.sendMessageSize", "Size of a message sent on a connection", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.con.sendDuplicateSize", "Size of a message resent on a connection", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
}
|
||||
|
||||
protected void finalize() throws Throwable {
|
||||
_cache.release(new ByteArray(_buf));
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new packet to be sent out ASAP
|
||||
*/
|
||||
@ -53,7 +46,7 @@ class PacketQueue {
|
||||
keyUsed = new SessionKey();
|
||||
Set tagsSent = packet.getTagsSent();
|
||||
if (tagsSent == null)
|
||||
tagsSent = new HashSet();
|
||||
tagsSent = new HashSet(0);
|
||||
|
||||
// cache this from before sendMessage
|
||||
String conStr = (packet.getConnection() != null ? packet.getConnection().toString() : "");
|
||||
@ -63,29 +56,36 @@ class PacketQueue {
|
||||
} else {
|
||||
_log.debug("Sending... " + packet);
|
||||
}
|
||||
|
||||
ByteArray ba = _cache.acquire();
|
||||
byte buf[] = ba.getData();
|
||||
|
||||
long begin = 0;
|
||||
long end = 0;
|
||||
boolean sent = false;
|
||||
try {
|
||||
int size = 0;
|
||||
synchronized (this) {
|
||||
Arrays.fill(_buf, (byte)0x0);
|
||||
if (packet.shouldSign())
|
||||
size = packet.writeSignedPacket(_buf, 0, _context, _session.getPrivateKey());
|
||||
else
|
||||
size = packet.writePacket(_buf, 0);
|
||||
long beforeWrite = System.currentTimeMillis();
|
||||
if (packet.shouldSign())
|
||||
size = packet.writeSignedPacket(buf, 0, _context, _session.getPrivateKey());
|
||||
else
|
||||
size = packet.writePacket(buf, 0);
|
||||
long writeTime = System.currentTimeMillis() - beforeWrite;
|
||||
if ( (writeTime > 1000) && (_log.shouldLog(Log.WARN)) )
|
||||
_log.warn("took " + writeTime + "ms to write the packet: " + packet);
|
||||
|
||||
// this should not block!
|
||||
begin = _context.clock().now();
|
||||
sent = _session.sendMessage(packet.getTo(), _buf, 0, size, keyUsed, tagsSent);
|
||||
end = _context.clock().now();
|
||||
}
|
||||
// this should not block!
|
||||
begin = _context.clock().now();
|
||||
sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent);
|
||||
end = _context.clock().now();
|
||||
|
||||
if ( (end-begin > 1000) && (_log.shouldLog(Log.WARN)) )
|
||||
_log.warn("Took " + (end-begin) + "ms to sendMessage(...) " + packet);
|
||||
|
||||
_context.statManager().addRateData("stream.con.sendMessageSize", size, packet.getLifetime());
|
||||
if (packet.getNumSends() > 1)
|
||||
_context.statManager().addRateData("stream.con.sendDuplicateSize", size, packet.getLifetime());
|
||||
|
||||
|
||||
Connection con = packet.getConnection();
|
||||
if (con != null) {
|
||||
con.incrementBytesSent(size);
|
||||
@ -97,6 +97,8 @@ class PacketQueue {
|
||||
_log.warn("Unable to send the packet " + packet, ise);
|
||||
}
|
||||
|
||||
_cache.release(ba);
|
||||
|
||||
if (!sent) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Send failed for " + packet);
|
||||
|
@ -38,6 +38,7 @@ class SchedulerChooser {
|
||||
|
||||
private List createSchedulers() {
|
||||
List rv = new ArrayList(8);
|
||||
rv.add(new SchedulerHardDisconnected(_context));
|
||||
rv.add(new SchedulerPreconnect(_context));
|
||||
rv.add(new SchedulerConnecting(_context));
|
||||
rv.add(new SchedulerReceived(_context));
|
||||
@ -54,8 +55,7 @@ class SchedulerChooser {
|
||||
}
|
||||
|
||||
public void eventOccurred(Connection con) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Event occurred on " + con, new Exception("source"));
|
||||
_log.log(Log.CRIT, "Yell at jrandom: Event occurred on " + con, new Exception("source"));
|
||||
}
|
||||
public boolean accept(Connection con) { return true; }
|
||||
};
|
||||
|
@ -47,7 +47,8 @@ class SchedulerClosed extends SchedulerImpl {
|
||||
}
|
||||
|
||||
public void eventOccurred(Connection con) {
|
||||
long timeLeft = con.getCloseSentOn() + Connection.DISCONNECT_TIMEOUT - _context.clock().now();
|
||||
reschedule(timeLeft, con);
|
||||
// noop. we do the timeout through the simpleTimer anyway
|
||||
//long timeLeft = con.getCloseSentOn() + Connection.DISCONNECT_TIMEOUT - _context.clock().now();
|
||||
//reschedule(timeLeft, con);
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ class SchedulerConnecting extends SchedulerImpl {
|
||||
public boolean accept(Connection con) {
|
||||
if (con == null) return false;
|
||||
boolean notYetConnected = (con.getIsConnected()) &&
|
||||
(con.getSendStreamId() == null) &&
|
||||
//(con.getSendStreamId() == null) && // not null on recv
|
||||
(con.getLastSendId() >= 0) &&
|
||||
(con.getAckedPackets() <= 0) &&
|
||||
(!con.getResetReceived());
|
||||
@ -55,6 +55,7 @@ class SchedulerConnecting extends SchedulerImpl {
|
||||
_log.debug("waited too long: " + waited);
|
||||
return;
|
||||
} else {
|
||||
// should we be doing a con.sendAvailable here?
|
||||
if (con.getOptions().getConnectTimeout() > 0)
|
||||
reschedule(con.getOptions().getConnectTimeout(), con);
|
||||
}
|
||||
|
@ -0,0 +1,45 @@
|
||||
package net.i2p.client.streaming;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* <p>Scheduler used after we've locally done a hard disconnect,
|
||||
* but the final timeout hasn't passed.</p>
|
||||
*
|
||||
* <h2>Entry conditions:</h2><ul>
|
||||
* <li>Locally disconnected hard.</li>
|
||||
* <li>Less than the final timeout period has passed since the last ACK.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Events:</h2><ul>
|
||||
* <li>Packets received</li>
|
||||
* <li>RESET received</li>
|
||||
* <li>Message sending fails (error talking to the session)</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Next states:</h2>
|
||||
* <li>{@link SchedulerDead dead} - after the final timeout passes</li>
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
*/
|
||||
class SchedulerHardDisconnected extends SchedulerImpl {
|
||||
private Log _log;
|
||||
public SchedulerHardDisconnected(I2PAppContext ctx) {
|
||||
super(ctx);
|
||||
_log = ctx.logManager().getLog(SchedulerHardDisconnected.class);
|
||||
}
|
||||
|
||||
public boolean accept(Connection con) {
|
||||
if (con == null) return false;
|
||||
long timeSinceClose = _context.clock().now() - con.getCloseSentOn();
|
||||
boolean ok = (con.getHardDisconnected() || con.getResetSent()) &&
|
||||
(timeSinceClose < Connection.DISCONNECT_TIMEOUT);
|
||||
return ok;
|
||||
}
|
||||
|
||||
public void eventOccurred(Connection con) {
|
||||
// noop. we do the timeout through the simpleTimer anyway
|
||||
}
|
||||
}
|
@ -32,5 +32,6 @@ abstract class SchedulerImpl implements TaskScheduler {
|
||||
// _log.debug("firing event on " + _connection, _addedBy);
|
||||
_connection.eventOccurred();
|
||||
}
|
||||
public String toString() { return "event on " + _connection; }
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,138 @@
|
||||
package net.i2p.client.streaming;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.I2PClientFactory;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Have a client connect to a server, where the server waits 5
|
||||
* seconds and closes the socket and the client detect that
|
||||
* EOF.
|
||||
*
|
||||
*/
|
||||
public class ConnectCloseTest {
|
||||
private Log _log;
|
||||
private I2PSession _server;
|
||||
public void test() {
|
||||
try {
|
||||
I2PAppContext context = I2PAppContext.getGlobalContext();
|
||||
_log = context.logManager().getLog(ConnectCloseTest.class);
|
||||
_log.debug("creating server session");
|
||||
_server = createSession();
|
||||
_log.debug("running server");
|
||||
runServer(context, _server);
|
||||
_log.debug("running client");
|
||||
runClient(context, createSession());
|
||||
} catch (Exception e) {
|
||||
_log.error("error running", e);
|
||||
}
|
||||
try { Thread.sleep(10*60*1000); } catch (Exception e) {}
|
||||
}
|
||||
|
||||
private void runClient(I2PAppContext ctx, I2PSession session) {
|
||||
Thread t = new Thread(new ClientRunner(ctx, session));
|
||||
t.setName("client");
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
}
|
||||
|
||||
private void runServer(I2PAppContext ctx, I2PSession session) {
|
||||
Thread t = new Thread(new ServerRunner(ctx, session));
|
||||
t.setName("server");
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
}
|
||||
|
||||
private class ServerRunner implements Runnable {
|
||||
private I2PAppContext _context;
|
||||
private I2PSession _session;
|
||||
private Log _log;
|
||||
public ServerRunner(I2PAppContext ctx, I2PSession session) {
|
||||
_context = ctx;
|
||||
_session = session;
|
||||
_log = ctx.logManager().getLog(ServerRunner.class);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
Properties opts = new Properties();
|
||||
I2PSocketManager mgr = new I2PSocketManagerFull(_context, _session, opts, "client");
|
||||
_log.debug("* manager created");
|
||||
I2PServerSocket ssocket = mgr.getServerSocket();
|
||||
_log.debug("* server socket created");
|
||||
while (true) {
|
||||
I2PSocket socket = ssocket.accept();
|
||||
_log.debug("* socket accepted: " + socket);
|
||||
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
|
||||
socket.close();
|
||||
_log.debug("* socket closed: " + socket);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
_log.error("error running", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class ClientRunner implements Runnable {
|
||||
private I2PAppContext _context;
|
||||
private I2PSession _session;
|
||||
private Log _log;
|
||||
public ClientRunner(I2PAppContext ctx, I2PSession session) {
|
||||
_context = ctx;
|
||||
_session = session;
|
||||
_log = ctx.logManager().getLog(ClientRunner.class);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
Properties opts = new Properties();
|
||||
I2PSocketManager mgr = new I2PSocketManagerFull(_context, _session, opts, "client");
|
||||
_log.debug("* manager created");
|
||||
I2PSocket socket = mgr.connect(_server.getMyDestination());
|
||||
_log.debug("* socket created");
|
||||
InputStream in = socket.getInputStream();
|
||||
int c = in.read();
|
||||
if (c != -1)
|
||||
throw new RuntimeException("hrm, we got data? [" + c + "]");
|
||||
socket.close();
|
||||
_log.debug("* socket closed");
|
||||
mgr.destroySocketManager();
|
||||
mgr = null;
|
||||
socket = null;
|
||||
} catch (Exception e) {
|
||||
_log.error("error running", e);
|
||||
}
|
||||
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private I2PSession createSession() {
|
||||
try {
|
||||
I2PClient client = I2PClientFactory.createClient();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
|
||||
Destination dest = client.createDestination(baos);
|
||||
I2PSession sess = client.createSession(new ByteArrayInputStream(baos.toByteArray()), System.getProperties());
|
||||
sess.connect();
|
||||
return sess;
|
||||
} catch (Exception e) {
|
||||
_log.error("error running", e);
|
||||
throw new RuntimeException("b0rk b0rk b0rk");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
ConnectCloseTest ct = new ConnectCloseTest();
|
||||
ct.test();
|
||||
}
|
||||
}
|
@ -57,6 +57,7 @@ public class MessageOutputStreamTest {
|
||||
_data.write(buf, off, size);
|
||||
return new DummyWriteStatus();
|
||||
}
|
||||
public boolean writeInProcess() { return false; }
|
||||
public byte[] getData() { return _data.toByteArray(); }
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
Prior to building the jbigi library, you will need to fetch the GMP source
|
||||
from http://www.swox.com/gmp/, saving it to jbigi/gmp-4.1.3.tar.bz2 (it will
|
||||
from http://www.swox.com/gmp/, saving it to jbigi/gmp-4.1.4.tar.bz2 (it will
|
||||
be unpacked and built as necessary).
|
||||
|
||||
To build the native jbigi and jcpuid libraries for the current host CPU,
|
||||
|
@ -7,7 +7,6 @@ mkdir -p t/freenet/support/CPUInformation/
|
||||
cp jcpuid/lib/freenet/support/CPUInformation/*jcpuid* t/freenet/support/CPUInformation/
|
||||
|
||||
mkdir -p t/net/i2p/util/
|
||||
cp jbigi/lib/net/i2p/util/*jbigi* t/net/i2p/util/
|
||||
cp jbigi/lib/*jbigi* t/
|
||||
|
||||
(cd t ; jar cf ../jbigi.jar . ; cd ..)
|
||||
|
@ -13,7 +13,7 @@ FreeBSD*)
|
||||
esac
|
||||
|
||||
echo "Extracting GMP..."
|
||||
tar -xjf gmp-4.1.3.tar.bz2
|
||||
tar -xjf gmp-4.1.4.tar.bz2
|
||||
echo "Building..."
|
||||
mkdir bin
|
||||
mkdir lib
|
||||
@ -24,7 +24,7 @@ for x in none pentium pentiummmx pentium2 pentium3 pentium4 k6 k62 k63 athlon
|
||||
do
|
||||
mkdir bin/$x
|
||||
cd bin/$x
|
||||
../../gmp-4.1.3/configure --build=$x
|
||||
../../gmp-4.1.4/configure --build=$x
|
||||
make
|
||||
sh ../../build_jbigi.sh static
|
||||
case `uname -sr` in
|
||||
|
@ -1,41 +1,15 @@
|
||||
#/bin/sh
|
||||
|
||||
case `uname -sr` in
|
||||
MINGW*)
|
||||
echo "Building windows .dll's";;
|
||||
CYGWIN*)
|
||||
echo "Building windows .dll's";;
|
||||
Linux*)
|
||||
echo "Building linux .so's";;
|
||||
FreeBSD*)
|
||||
echo "Building freebsd .so's";;
|
||||
*)
|
||||
echo "Unsupported build environment"
|
||||
exit;;
|
||||
esac
|
||||
echo "Building the jbigi library with GMP"
|
||||
|
||||
echo "Extracting GMP..."
|
||||
tar -xjf gmp-4.1.3.tar.bz2
|
||||
tar -xjf gmp-4.1.4.tar.bz2
|
||||
echo "Building..."
|
||||
mkdir bin
|
||||
mkdir lib
|
||||
mkdir lib/net
|
||||
mkdir lib/net/i2p
|
||||
mkdir lib/net/i2p/util
|
||||
mkdir bin/local
|
||||
mkdir -p lib/
|
||||
mkdir -p bin/local
|
||||
cd bin/local
|
||||
../../gmp-4.1.3/configure
|
||||
../../gmp-4.1.4/configure
|
||||
make
|
||||
sh ../../build_jbigi.sh static
|
||||
case `uname -sr` in
|
||||
MINGW*)
|
||||
cp jbigi.dll ../../lib/jbigi;;
|
||||
CYGWIN*)
|
||||
cp jbigi.dll ../../lib/jbigi;;
|
||||
Linux*)
|
||||
cp libjbigi.so ../../lib/jbigi;;
|
||||
FreeBSD*)
|
||||
cp libjbigi.so ../../lib/jbigi;;
|
||||
esac
|
||||
cd ..
|
||||
cd ..
|
||||
cp *jbigi???* ../../lib/
|
||||
cd ../..
|
||||
|
@ -67,6 +67,7 @@ class I2CPMessageProducer {
|
||||
*
|
||||
*/
|
||||
public void disconnect(I2PSessionImpl session) throws I2PSessionException {
|
||||
if (session.isClosed()) return;
|
||||
DestroySessionMessage dmsg = new DestroySessionMessage();
|
||||
dmsg.setSessionId(session.getSessionId());
|
||||
session.sendMessage(dmsg);
|
||||
|
@ -108,6 +108,8 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
|
||||
private boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent)
|
||||
throws I2PSessionException {
|
||||
long begin = _context.clock().now();
|
||||
|
||||
SessionKey key = _context.sessionKeyManager().getCurrentKey(dest.getPublicKey());
|
||||
if (key == null) key = _context.sessionKeyManager().createSession(dest.getPublicKey());
|
||||
SessionTag tag = _context.sessionKeyManager().consumeNextAvailableTag(dest.getPublicKey(), key);
|
||||
@ -180,9 +182,17 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
+ " sync took " + (inSendingSync-beforeSendingSync)
|
||||
+ " add took " + (afterSendingSync-inSendingSync));
|
||||
_producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey);
|
||||
|
||||
// since this is 'best effort', all we're waiting for is a status update
|
||||
// saying that the router received it - in theory, that should come back
|
||||
// immediately, but in practice can take up to a second (though usually
|
||||
// much quicker). setting this to false will short-circuit that delay
|
||||
boolean actuallyWait = true;
|
||||
|
||||
long beforeWaitFor = _context.clock().now();
|
||||
state.waitFor(MessageStatusMessage.STATUS_SEND_ACCEPTED,
|
||||
_context.clock().now() + getTimeout());
|
||||
if (actuallyWait)
|
||||
state.waitFor(MessageStatusMessage.STATUS_SEND_ACCEPTED,
|
||||
_context.clock().now() + getTimeout());
|
||||
long afterWaitFor = _context.clock().now();
|
||||
long inRemovingSync = 0;
|
||||
synchronized (_sendingStates) {
|
||||
@ -190,10 +200,23 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
_sendingStates.remove(state);
|
||||
}
|
||||
long afterRemovingSync = _context.clock().now();
|
||||
boolean found = state.received(MessageStatusMessage.STATUS_SEND_ACCEPTED);
|
||||
boolean found = !actuallyWait || state.received(MessageStatusMessage.STATUS_SEND_ACCEPTED);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "After waitFor sending state " + state.getMessageId()
|
||||
+ " / " + state.getNonce() + " found = " + found);
|
||||
|
||||
long timeToSend = afterRemovingSync - beforeSendingSync;
|
||||
if ( (timeToSend > 10*1000) && (_log.shouldLog(Log.WARN)) ) {
|
||||
_log.warn("wtf, took " + timeToSend + "ms to send the message?!", new Exception("baz"));
|
||||
}
|
||||
|
||||
if ( (afterRemovingSync - begin > 500) && (_log.shouldLog(Log.WARN) ) ) {
|
||||
_log.warn("Took " + (afterRemovingSync-begin) + "ms to sendBestEffort, "
|
||||
+ (afterSendingSync-begin) + "ms to prepare, "
|
||||
+ (beforeWaitFor-afterSendingSync) + "ms to send, "
|
||||
+ (afterRemovingSync-beforeWaitFor) + "ms waiting for reply");
|
||||
}
|
||||
|
||||
if (found) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(getPrefix() + "Message sent after " + state.getElapsed() + "ms with "
|
||||
|
@ -16,6 +16,7 @@ import net.i2p.data.i2cp.I2CPMessage;
|
||||
import net.i2p.data.i2cp.MessageId;
|
||||
import net.i2p.data.i2cp.MessagePayloadMessage;
|
||||
import net.i2p.data.i2cp.ReceiveMessageEndMessage;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Handle I2CP MessagePayloadMessages from the router delivering the contents
|
||||
@ -30,7 +31,8 @@ class MessagePayloadMessageHandler extends HandlerImpl {
|
||||
}
|
||||
|
||||
public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
|
||||
_log.debug("Handle message " + message);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Handle message " + message);
|
||||
try {
|
||||
MessagePayloadMessage msg = (MessagePayloadMessage) message;
|
||||
MessageId id = msg.getMessageId();
|
||||
@ -55,9 +57,8 @@ class MessagePayloadMessageHandler extends HandlerImpl {
|
||||
Payload payload = msg.getPayload();
|
||||
byte[] data = _context.elGamalAESEngine().decrypt(payload.getEncryptedData(), session.getDecryptionKey());
|
||||
if (data == null) {
|
||||
_log
|
||||
.error("Error decrypting the payload to public key "
|
||||
+ session.getMyDestination().getPublicKey().toBase64() + "\nPayload: " + payload.calculateHash());
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error decrypting the payload");
|
||||
throw new DataFormatException("Unable to decrypt the payload");
|
||||
}
|
||||
payload.setUnencryptedData(data);
|
||||
|
@ -27,7 +27,7 @@ but there are three other subpackages that are helpful. Specifically:<ul>
|
||||
<li>{@link net.i2p.client.streaming} - for applications that want to use
|
||||
a streaming API to provide reliable in order message delivery (<b>note</b>:
|
||||
the streaming library is packaged seperate from the main SDK - in the
|
||||
ministreaming.jar)</li>
|
||||
mstreaming.jar and streaming.jar)</li>
|
||||
</ul></p>
|
||||
|
||||
<p>The {@link net.i2p.client.I2PSession} implementation itself communicates with
|
||||
|
@ -18,6 +18,7 @@ import java.io.Serializable;
|
||||
*/
|
||||
public class ByteArray implements Serializable, Comparable {
|
||||
private byte[] _data;
|
||||
private int _valid;
|
||||
|
||||
public ByteArray() {
|
||||
this(null);
|
||||
@ -25,6 +26,7 @@ public class ByteArray implements Serializable, Comparable {
|
||||
|
||||
public ByteArray(byte[] data) {
|
||||
_data = data;
|
||||
_valid = 0;
|
||||
}
|
||||
|
||||
public final byte[] getData() {
|
||||
@ -34,6 +36,14 @@ public class ByteArray implements Serializable, Comparable {
|
||||
public void setData(byte[] data) {
|
||||
_data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count how many of the bytes in the array are 'valid'.
|
||||
* this property does not necessarily have meaning for all byte
|
||||
* arrays.
|
||||
*/
|
||||
public final int getValid() { return _valid; }
|
||||
public final void setValid(int valid) { _valid = valid; }
|
||||
|
||||
public final boolean equals(Object o) {
|
||||
if (o == null) return false;
|
||||
|
@ -658,18 +658,41 @@ public class DataHelper {
|
||||
}
|
||||
|
||||
public static int read(InputStream in, byte target[]) throws IOException {
|
||||
int cur = 0;
|
||||
while (cur < target.length) {
|
||||
int numRead = in.read(target, cur, target.length - cur);
|
||||
return read(in, target, 0, target.length);
|
||||
}
|
||||
public static int read(InputStream in, byte target[], int offset, int length) throws IOException {
|
||||
int cur = offset;
|
||||
while (cur < length) {
|
||||
int numRead = in.read(target, cur, length - cur);
|
||||
if (numRead == -1) {
|
||||
if (cur == 0) return -1; // throw new EOFException("EOF Encountered during reading");
|
||||
|
||||
if (cur == offset) return -1; // throw new EOFException("EOF Encountered during reading");
|
||||
return cur;
|
||||
}
|
||||
cur += numRead;
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read a newline delimited line from the stream, returning the line (without
|
||||
* the newline), or null if EOF reached before the newline was found
|
||||
*/
|
||||
public static String readLine(InputStream in) throws IOException {
|
||||
StringBuffer buf = new StringBuffer(128);
|
||||
|
||||
int c = -1;
|
||||
while ( (c = in.read()) != -1) {
|
||||
if (c == '\n')
|
||||
break;
|
||||
buf.append((char)c);
|
||||
}
|
||||
if (c == -1)
|
||||
return null;
|
||||
else
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
||||
public static List sortStructures(Collection dataStructures) {
|
||||
if (dataStructures == null) return new ArrayList();
|
||||
@ -735,7 +758,7 @@ public class DataHelper {
|
||||
|
||||
/** decompress the GZIP compressed data (returning null on error) */
|
||||
public static byte[] decompress(byte orig[]) throws IOException {
|
||||
return decompress(orig, 0, orig.length);
|
||||
return (orig != null ? decompress(orig, 0, orig.length) : null);
|
||||
}
|
||||
public static byte[] decompress(byte orig[], int offset, int length) throws IOException {
|
||||
if ((orig == null) || (orig.length <= 0)) return orig;
|
||||
|
@ -124,7 +124,8 @@ public class Timestamper implements Runnable {
|
||||
alreadyBitched = true;
|
||||
}
|
||||
}
|
||||
try { Thread.sleep(_queryFrequency); } catch (InterruptedException ie) {}
|
||||
long sleepTime = _context.random().nextInt(_queryFrequency) + _queryFrequency;
|
||||
try { Thread.sleep(sleepTime); } catch (InterruptedException ie) {}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
_log.log(Log.CRIT, "Timestamper died!", t);
|
||||
|
@ -42,8 +42,8 @@ public class Clock implements Timestamper.UpdateListener {
|
||||
|
||||
/** if the clock is skewed by 3+ days, fuck 'em */
|
||||
public final static long MAX_OFFSET = 3 * 24 * 60 * 60 * 1000;
|
||||
/** after we've started up and shifted the clock, don't allow shifts of more than a minute */
|
||||
public final static long MAX_LIVE_OFFSET = 60 * 1000;
|
||||
/** after we've started up and shifted the clock, don't allow shifts of more than 10 minutes */
|
||||
public final static long MAX_LIVE_OFFSET = 10 * 60 * 1000;
|
||||
/** if the clock skewed changes by less than 1s, ignore the update (so we don't slide all over the place) */
|
||||
public final static long MIN_OFFSET_CHANGE = 10 * 1000;
|
||||
|
||||
|
@ -18,20 +18,30 @@ import net.i2p.I2PAppContext;
|
||||
public class SimpleTimer {
|
||||
private static final SimpleTimer _instance = new SimpleTimer();
|
||||
public static SimpleTimer getInstance() { return _instance; }
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
/** event time (Long) to event (TimedEvent) mapping */
|
||||
private TreeMap _events;
|
||||
/** event (TimedEvent) to event time (Long) mapping */
|
||||
private Map _eventTimes;
|
||||
private List _readyEvents;
|
||||
|
||||
private SimpleTimer() {
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(SimpleTimer.class);
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
_log = _context.logManager().getLog(SimpleTimer.class);
|
||||
_events = new TreeMap();
|
||||
_eventTimes = new HashMap();
|
||||
_readyEvents = new ArrayList(4);
|
||||
I2PThread runner = new I2PThread(new SimpleTimerRunner());
|
||||
runner.setName("SimpleTimer");
|
||||
runner.setDaemon(true);
|
||||
runner.start();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
I2PThread executor = new I2PThread(new Executor());
|
||||
executor.setName("SimpleTimerExecutor " + i);
|
||||
executor.setDaemon(true);
|
||||
executor.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,18 +50,42 @@ public class SimpleTimer {
|
||||
*/
|
||||
public void addEvent(TimedEvent event, long timeoutMs) {
|
||||
long eventTime = System.currentTimeMillis() + timeoutMs;
|
||||
Long time = new Long(eventTime);
|
||||
synchronized (_events) {
|
||||
// remove the old scheduled position, then reinsert it
|
||||
if (_eventTimes.containsKey(event))
|
||||
_events.remove(_eventTimes.get(event));
|
||||
while (_events.containsKey(new Long(eventTime)))
|
||||
eventTime++;
|
||||
_events.put(new Long(eventTime), event);
|
||||
_eventTimes.put(event, new Long(eventTime));
|
||||
while (_events.containsKey(time))
|
||||
time = new Long(time.longValue() + 1);
|
||||
_events.put(time, event);
|
||||
_eventTimes.put(event, time);
|
||||
|
||||
if ( (_events.size() != _eventTimes.size()) ) {
|
||||
_log.error("Skewed events: " + _events.size() + " for " + _eventTimes.size());
|
||||
for (Iterator iter = _eventTimes.keySet().iterator(); iter.hasNext(); ) {
|
||||
TimedEvent evt = (TimedEvent)iter.next();
|
||||
Long when = (Long)_eventTimes.get(evt);
|
||||
TimedEvent cur = (TimedEvent)_events.get(when);
|
||||
if (cur != evt) {
|
||||
_log.error("event " + evt + " @ " + when + ": " + cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_events.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeEvent(TimedEvent evt) {
|
||||
if (evt == null) return false;
|
||||
synchronized (_events) {
|
||||
Long when = (Long)_eventTimes.remove(evt);
|
||||
if (when != null)
|
||||
_events.remove(when);
|
||||
return null != when;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple interface for events to be queued up and notified on expiration
|
||||
*/
|
||||
@ -72,14 +106,18 @@ public class SimpleTimer {
|
||||
_log.log(Log.CRIT, msg, t);
|
||||
}
|
||||
|
||||
private long _occurredTime;
|
||||
private long _occurredEventCount;
|
||||
private TimedEvent _recentEvents[] = new TimedEvent[5];
|
||||
|
||||
private class SimpleTimerRunner implements Runnable {
|
||||
public void run() {
|
||||
List eventsToFire = new ArrayList(1);
|
||||
while (true) {
|
||||
try {
|
||||
synchronized (_events) {
|
||||
if (_events.size() <= 0)
|
||||
_events.wait();
|
||||
//if (_events.size() <= 0)
|
||||
// _events.wait();
|
||||
//if (_events.size() > 100)
|
||||
// _log.warn("> 100 events! " + _events.values());
|
||||
long now = System.currentTimeMillis();
|
||||
@ -93,7 +131,7 @@ public class SimpleTimer {
|
||||
if (evt != null) {
|
||||
_eventTimes.remove(evt);
|
||||
eventsToFire.add(evt);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nextEventDelay = when.longValue() - now;
|
||||
nextEvent = _events.get(when);
|
||||
@ -121,15 +159,55 @@ public class SimpleTimer {
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < eventsToFire.size(); i++) {
|
||||
TimedEvent evt = (TimedEvent)eventsToFire.get(i);
|
||||
long now = System.currentTimeMillis();
|
||||
now = now - (now % 1000);
|
||||
|
||||
synchronized (_readyEvents) {
|
||||
for (int i = 0; i < eventsToFire.size(); i++)
|
||||
_readyEvents.add(eventsToFire.get(i));
|
||||
_readyEvents.notifyAll();
|
||||
}
|
||||
|
||||
if (_occurredTime == now) {
|
||||
_occurredEventCount += eventsToFire.size();
|
||||
} else {
|
||||
_occurredTime = now;
|
||||
if (_occurredEventCount > 1000) {
|
||||
StringBuffer buf = new StringBuffer(128);
|
||||
buf.append("Too many simpleTimerJobs (").append(_occurredEventCount);
|
||||
buf.append(") in a second!");
|
||||
_log.log(Log.CRIT, buf.toString());
|
||||
}
|
||||
_occurredEventCount = 0;
|
||||
}
|
||||
|
||||
eventsToFire.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Executor implements Runnable {
|
||||
public void run() {
|
||||
while (true) {
|
||||
TimedEvent evt = null;
|
||||
synchronized (_readyEvents) {
|
||||
if (_readyEvents.size() <= 0)
|
||||
try { _readyEvents.wait(); } catch (InterruptedException ie) {}
|
||||
if (_readyEvents.size() > 0)
|
||||
evt = (TimedEvent)_readyEvents.remove(0);
|
||||
}
|
||||
|
||||
if (evt != null) {
|
||||
long before = _context.clock().now();
|
||||
try {
|
||||
evt.timeReached();
|
||||
} catch (Throwable t) {
|
||||
log("wtf, event borked: " + evt, t);
|
||||
}
|
||||
long time = _context.clock().now() - before;
|
||||
if ( (time > 1000) && (_log != null) && (_log.shouldLog(Log.WARN)) )
|
||||
_log.warn("wtf, event execution took " + time + ": " + evt);
|
||||
}
|
||||
eventsToFire.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
206
core/java/test/net/i2p/crypto/ElGamalVerify.java
Normal file
206
core/java/test/net/i2p/crypto/ElGamalVerify.java
Normal file
@ -0,0 +1,206 @@
|
||||
package net.i2p.crypto;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.PrivateKey;
|
||||
import net.i2p.data.PublicKey;
|
||||
|
||||
/**
|
||||
* Unit test verifying the ElGamal encryption/decryption with some test
|
||||
* data. The keys generated & data stored were generated by jrandom on
|
||||
* a pentium4 w/ an optimized jbigi installed and verified with pure java.
|
||||
*
|
||||
*/
|
||||
public class ElGamalVerify {
|
||||
private I2PAppContext _context;
|
||||
|
||||
private static final String UNENCRYPTED[] = new String[] {
|
||||
"",
|
||||
"hello world",
|
||||
"1234567890123456789012345678901234567890123456789012345678901234567890" +
|
||||
"1234567890123456789012345678901234567890123456789012345678901234567890" +
|
||||
"1234567890123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012",
|
||||
"\0x00",
|
||||
"\0x00\0x00\0x00",
|
||||
"\0x00\0x01\0x02\0x00",
|
||||
};
|
||||
private static final String PUBLIC_KEY = new String(
|
||||
"pOvBUMrSUUeN5awynzbPbCAwe3MqWprhSpp3OR7pvdfm9PhWaNbPoKRLeEmDoUwyNDoHE0" +
|
||||
"E6mcZSG8qPQ8XUZFlczpilOl0MJBvsI9u9SMyi~bEqzSgzh9FNfS-NcGji3q2wI~Ux~q5B" +
|
||||
"KOjGlyMLgd1nxl5R5wIYL4uHKZNaYuArsRYmtV~MgMQPGvDtIbdGTV6aL6UbOYryzQSUMY" +
|
||||
"OuO3S~YoBjA6Nmi0SeJM3tyTxlI6U1EYjR6oQcI4SOFUW4L~8pfYWijcncCODAqpXVN6ZI" +
|
||||
"AJ3a6vjxGu56IDp4xCcKlOEHgdXvqmEC67dR5qf2btH6dtWoB3-Z6QPsS6tPTQ=="
|
||||
);
|
||||
private static final String PRIVATE_KEY = new String(
|
||||
"gMlIhURVXU8uPube20Xr8E1K11g-3qZxOj1riThHqt-rBx72MPq5ivT1rr28cE9mzOmsXi" +
|
||||
"bbsuBuQKYDvF7hGICRB3ROSPePYhcupV3j7XiXUIYjWNw9hvylHXK~nTT7jkpIBazBJZfr" +
|
||||
"LJPcDZTDB0YnCOHOL-KFn4N1R5B22g0iYRABN~O10AUjQmf1epklAXPqYlzmOYeJSfTPBI" +
|
||||
"E44nEccWJp0M0KynhKVbDI0v9VYm6sPFK7WrzRyWwHL~r735wiRkwywuMmKJtA7-PuJjcW" +
|
||||
"NLkJwx6WScH2msMzhzYPi8JSZJBl~PosX934l-L0T-KNV4jg1Ih6yoCnm1748A=="
|
||||
);
|
||||
private static final String ENCRYPTED[] = new String[] {
|
||||
"AMfISa8KvTpaC7KXZzSvC2axyiSk0xPexBAf29yU~IKq21DzaU19wQcGJg-ktpG4hjGSg7" +
|
||||
"u-mJ07b61yo-EGmVGZsv3nYuQYW-GjvsZQa9nm98VljlMtWrxu7TsRXw~SQlWQxMvthqJB" +
|
||||
"1A7Y7Qa~C7-UlRytkD-cpVdgUfM-esuMWmjGs6Vc33N5U-tce5Fywa-9y7PSn3ukBO8KGR" +
|
||||
"wm7T12~H2gvhgxrVeK2roOzsV7f5dGkvBQRZJ309Vg3j0kjaxWutgI3vli0pzDbSK9d5NR" +
|
||||
"-GUDtdOb6IIfLiOckBegcv6I-wlSXjYJe8mIoaK45Ok3rEpHwWKVKS2MeuI7AmsAWgkQmW" +
|
||||
"f8irmZaKc9X910VWSO5GYu6006hSc~r2TL3O7vwtW-Z9Oq~sAam9av1PPVJzAx8A4g~m~1" +
|
||||
"avtNnncwlChsGo6mZHXqz-QMdMJXXP57f4bx36ZomkvpM-ZLlFAn-a~42KQJAApo4LfEyk" +
|
||||
"7DPY2aTXL9ArOCNQIQB4f8QLyjvAvu6M3jzCoGo0wVX6oePfdiokGflriYOcD8rL4NbnCP" +
|
||||
"~MSnVzC8LKyRzQVN1tDYj8~njuFqekls6En8KFJ-qgtL4PiYxbnBQDUPoW6y61m-S9r9e9" +
|
||||
"y8qWd6~YtdAHAxVlw287~HEp9r7kqI-cjdo1337b7~5dm83KK45g5Nfw==",
|
||||
|
||||
"AIrd65mG1FJ~9J-DDSyhryVejJBSIjYOqV3GYmHDWgwLchTwq-bJS7dub3ENk9MZ-C6FIN" +
|
||||
"gjUFRaLBtfwJnySmNf8pIf1srmgdfqGV2h77ufG5Gs0jggKPmPV~7Z1kTcgsqpL8MyrfXr" +
|
||||
"Gi86X5ey-T0SZSFc0X1EhaE-47WlyWaGf-~xth6VOR~KG7clOxaOBpks-7WKZNQf7mpQRE" +
|
||||
"4IsPJyj5p1Rf-MeDbVKbK~52IfXSuUZQ8uZr34KMoy4chjn6e-jBhM4XuaQWhsM~a3Q-zE" +
|
||||
"pV-ea6t0bQTYfsbG9ch7pJuDPHM64o5mF9FS5-JGr7MOtfP7KDNHiYM2~-uC6BIAbiqBN8" +
|
||||
"WSLX1mrHVuhiM-hiJ7U4oq~HYB6N~U980sCIW0dgFBbhalzzQhJQSrC1DFDqGfL5-L25mj" +
|
||||
"ArP8dtvN0JY3LSnbcsm-pT9ttFHCPGomLfaAuP7ohknBoXK0j9e6~splg5sUA9TfLeBfqc" +
|
||||
"Lr0Sf8b3l~PvmrVkbVcaE8yUqSS6JFdt3pavjyyAQSmSlb2jVNKGPlrov5QLzlbH7G~AUv" +
|
||||
"IehsbGQX5ptRROtSojN~iYx3WQTOa-JLEC-AL7RbRu6B62p9I0pD0JgbUfCc4C4l9E9W~s" +
|
||||
"MuaJLAXxh0b2miF7C5bzZHxbt~MtZ7Ho5qpZMitXyoE3icb43B6Y1sbA==",
|
||||
|
||||
"ACjb0FkTIQbnEzCZlYXGxekznfJad5uW~F5Mbu~0wtsI1O2veqdr7Mb0N754xdIz7929Ti" +
|
||||
"1Kz-CxVEAkb3RBbVNcYHLfjy23oQ4BCioDKQaJcdkJqXa~Orm7Ta2tbkhM1Mx05MDrQaVF" +
|
||||
"gCVXtwTsPSLVK8VwScjPIFLXgQqqZ5osq~WhaMcYe2I2RCQLOx2VzaKbT21MMbtF70a-nK" +
|
||||
"WovkRUNfJEPeJosFwF2duAD0BHHrPiryK9BPDhyOiyN82ahOi2uim1Nt5yhlP3xo7cLV2p" +
|
||||
"6kTlR1BNC5pYjtsvetZf6wk-solNUrJWIzcuc18uRDNH5K90GTL6FXPMSulM~E4ATRQfhZ" +
|
||||
"fkW9xCrBIaIQM49ms2wONsp7fvI07b1r0rt7ZwCFOFit1HSAKl8UpsAYu-EsIO1qAK7vvO" +
|
||||
"UV~0OuBXkMZEyJT-uIVfbE~xrwPE0zPYE~parSVQgi~yNQBxukUM1smAM5xXVvJu8GjmE-" +
|
||||
"kJZw1cxaYLGsJjDHDk4HfEsyQVVPZ0V3bQvhB1tg5cCsTH~VNjts4taDTPWfDZmjtVaxxr" +
|
||||
"PRII4NEDKqEzg3JBevM~yft-RDfMc8RVlm-gCGANrRQORFii7uD3o9~y~4P2tLnO7Fy3m5" +
|
||||
"rdjRsOsWnCQZzw37mcBoT9rEZPrVpD8pjebJ1~HNc764xIpXDWVt8CbA==",
|
||||
|
||||
"AHDZBKiWeaIYQS9R1l70IlRnoplwKTkLP2dLlXmVh1gB33kx65uX8OMb3hdZEO0Bbzxkkx" +
|
||||
"quqlNn5w166nJO4nPbpEzVfgtY4ClUuv~W4H4CXBr0FcZM1COAkd6rtp6~lUp7cZ8FAkpH" +
|
||||
"spl95IxlFM-F1HwiPcbmTjRO1AwCal4sH8S5WmJCvBU6jH6pBPo~9B9vAtP7vX1EwsG2Jf" +
|
||||
"CQXkVkfvbWpSicbsWn77aECedS3HkIMrXrxojp7gAiPgQhX4NR387rcUPFsMHGeUraTUPZ" +
|
||||
"D7ctk5tpUuYYwRQc5cRKHa4zOq~AQyljx5w5~FByLda--6yCe7qDcILyTygudJ4AHRs1pJ" +
|
||||
"RU3uuRTHZx0XJQo~cPsoQ2piAOohITX9~yMCimCgv2EIhY3Z-mAgo8qQ4iMbItoE1cl93I" +
|
||||
"u2YV2n4wMq9laBx0shuKOJqO3rjRnszzCbqMuFAXfc3KgGDEaCpI7049s3i2yIcv4vT9uU" +
|
||||
"AlrM-dsrdw0JgJiFYl0JXh~TO0IyrcVcLpgZYgRhEvTAdkDNwTs-2GK4tzdPEd34os4a2c" +
|
||||
"DPL8joh3jhp~eGoRzrpcdRekxENdzheL4w3wD1fJ9W2-leil1FH6EPc3FSL6e~nqbw69gN" +
|
||||
"bsuXAMQ6CobukJdJEy37uKmEw4v6WPyfYMUUacchv1JoNfkHLpnAWifQ==",
|
||||
|
||||
"AGwvKAMJcPAliP-n7F0Rrj0JMRaFGjww~zvBjyzc~SPJrBF831cMqZFRmMHotgA7S5BrH2" +
|
||||
"6CL8okI2N-7as0F2l7OPx50dFEwSVSjqBjVV6SGRFC8oS-ii1FURMz2SCHSaj6kazAYq4s" +
|
||||
"DwyqR7vnUrOtPnZujHSU~a02jinyn-QOaHkxRiUp-Oo0jlZiU5xomXgLdkhtuz6725WUDj" +
|
||||
"3uVlMtIYfeKQsTdasujHe1oQhUmp58jfg5vgZ8g87cY8rn4p9DRwDBBuo6vi5on7T13sGx" +
|
||||
"tY9wz6HTpwzDhEqpNrj~h4JibElfi0Jo8ZllmNTO1ZCNpUQgASoTtyFLD5rk6cIAMK0R7A" +
|
||||
"7hjB0aelKM-V7AHkj-Fhrcm8xIgWhKaLn2wKbVNpAkllkiLALyfWJ9dhJ804RWQTMPE-GD" +
|
||||
"kBMIFOOJ9MhpEN533OBQDwUKcoxMjl0zOMNCLx8IdCE6cLtUDKJXLB0atnDpLkBer6FwXP" +
|
||||
"81EvKDYhtp1GsbiKvZDt8LSPJQnm2EdA3Pr9fpAisJ5Ocaxlfa6~uQCuqGA9nJ9n6w03u-" +
|
||||
"ZpSMhSh4zm2s1MqijmaJRc-QNKmN~u1hh3R2hwWNi7FoStMA87sutEBXMdFI8un7StHNSE" +
|
||||
"iCYwmmW2Nu3djkM-X8gGjSsdrphTU7uOXbwazmguobFGxI0JujYruM5Q==",
|
||||
|
||||
"ALFYtPSwEEW3eTO4hLw6PZNlBKoSIseQNBi034gq6FwYEZsJOAo-1VXcvMviKw2MCP9ZkH" +
|
||||
"lTNBfzc79ms2TU8kXxc7zwUc-l2HJLWh6dj2tIQLR8bbWM7U0iUx4XB1B-FEvdhbjz7dsu" +
|
||||
"6SBXVhxo2ulrk7Q7vX3kPrePhZZldcNZcS0t65DHYYwL~E~ROjQwOO4Cb~8FgiIUjb8CCN" +
|
||||
"w5zxJpBaEt7UvZffkVwj-EWTzFy3DIjWIRizxnsI~mUI-VspPE~xlmFX~TwPS9UbwJDpm8" +
|
||||
"-WzINFcehSzF3y9rzSMX-KbU8m4YZj07itZOiIbWgLeulTUB-UgwEkfJBG0xiSUAspZf2~" +
|
||||
"t~NthBlpcdrBLADXTJ7Jmkk4MIfysV~JpDB7IVg0v4WcUUwF3sYMmBCdPCwyYf0hTrl2Yb" +
|
||||
"L6kmm4u97WgQqf0TyzXtVZYwjct4LzZlyH591y6O6AQ4Fydqos9ABInzu-SbXq6S1Hi6vr" +
|
||||
"aNWU3mcy2myie32EEXtkX7P8eXWY35GCv9ThPEYHG5g1qKOk95ZCTYYwlpgeyaMKsnN3C~" +
|
||||
"x9TJA8K8T44v7vE6--Nw4Z4zjepwkIOht9iQsA6D6wRUQpeYX8bjIyYDPC7GUHq0WhXR6E" +
|
||||
"6Ojc9k8V5uh0SZ-rCQX6sccdk3JbyRhjGP4rSKr6MmvxVVsqBjcbpxsg=="
|
||||
};
|
||||
|
||||
public static void main(String args[]) {
|
||||
ElGamalVerify verify = new ElGamalVerify();
|
||||
verify.verifySelf();
|
||||
verify.verifyCompatability();
|
||||
if (args.length > 0)
|
||||
verify.generateEncrypted();
|
||||
}
|
||||
|
||||
public ElGamalVerify() {
|
||||
_context = new I2PAppContext();
|
||||
}
|
||||
|
||||
/** verify that we can decrypt what we encrypt */
|
||||
private void verifySelf() {
|
||||
try {
|
||||
Object keypair[] = _context.keyGenerator().generatePKIKeypair();
|
||||
PublicKey pub = (PublicKey)keypair[0];
|
||||
PrivateKey priv = (PrivateKey)keypair[1];
|
||||
|
||||
for (int i = 0; i < UNENCRYPTED.length; i++) {
|
||||
byte orig[] = UNENCRYPTED[i].getBytes();
|
||||
|
||||
byte encrypted[] = _context.elGamalEngine().encrypt(orig, pub);
|
||||
byte decrypted[] = _context.elGamalEngine().decrypt(encrypted, priv);
|
||||
|
||||
if (DataHelper.eq(decrypted, orig))
|
||||
log("OK : verifySelf[" + i + "] passed");
|
||||
else
|
||||
log("ERROR: verifySelf[" + i + "] failed");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log("ERROR: verifySelf blew up: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/** verify that we can decrypt what other people encrypt */
|
||||
private void verifyCompatability() {
|
||||
verifyDecrypt();
|
||||
}
|
||||
|
||||
private void verifyDecrypt() {
|
||||
try {
|
||||
PublicKey pub = new PublicKey();
|
||||
PrivateKey priv = new PrivateKey();
|
||||
pub.fromBase64(PUBLIC_KEY);
|
||||
priv.fromBase64(PRIVATE_KEY);
|
||||
|
||||
for (int i = 0; i < ENCRYPTED.length; i++) {
|
||||
byte enc[] = Base64.decode(ENCRYPTED[i]);
|
||||
byte decrypted[] = _context.elGamalEngine().decrypt(enc, priv);
|
||||
|
||||
if (DataHelper.eq(decrypted, UNENCRYPTED[i].getBytes()))
|
||||
log("OK : verifyDecrypt[" + i + "] passed");
|
||||
else
|
||||
log("ERROR: verifyDecrypt[" + i + "] failed");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log("ERROR: generateEncrypted blew up: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private void generateEncrypted() {
|
||||
try {
|
||||
Object keypair[] = _context.keyGenerator().generatePKIKeypair();
|
||||
PublicKey pub = (PublicKey)keypair[0];
|
||||
PrivateKey priv = (PrivateKey)keypair[1];
|
||||
|
||||
log("PUBLIC : " + pub.toBase64());
|
||||
log("PRIVATE: " + priv.toBase64());
|
||||
|
||||
for (int i = 0; i < UNENCRYPTED.length; i++) {
|
||||
byte orig[] = UNENCRYPTED[i].getBytes();
|
||||
|
||||
byte encrypted[] = _context.elGamalEngine().encrypt(orig, pub);
|
||||
|
||||
System.out.println("Encrypted [" + i + "]: ");
|
||||
String enc = Base64.encode(encrypted);
|
||||
for (int j = 0; j < enc.length(); j++) {
|
||||
int remaining = enc.length() - j*70;
|
||||
if (remaining > 0) {
|
||||
String cur = enc.substring(j * 70, remaining > 70 ? (j+1)*70 : enc.length());
|
||||
System.out.println(cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log("ERROR: generateEncrypted blew up: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void log(String msg) {
|
||||
System.out.println(msg);
|
||||
}
|
||||
}
|
238
history.txt
238
history.txt
@ -1,4 +1,240 @@
|
||||
$Id: history.txt,v 1.82 2004/11/25 16:57:19 jrandom Exp $
|
||||
$Id: history.txt,v 1.110 2004/12/16 05:21:24 jrandom Exp $
|
||||
|
||||
* 2004-12-18 0.4.2.4 released
|
||||
|
||||
2004-12-16 jrandom
|
||||
* Catch another oddball case for a reset connection in the streaming lib.
|
||||
* Add a dumpprofile.jsp page, called with ?peer=base64OfPeerHash, which
|
||||
dumps the current state of that peer's profile. Instead of the full
|
||||
base64, you can pass in however many characters you have and it will
|
||||
return the first match found.
|
||||
|
||||
2004-12-16 jrandom
|
||||
* Remove the randomized factor in the tunnel rejection by bandwidth -
|
||||
we now accept the request if we've allocated less than our limit
|
||||
and reject it if we've allocated more.
|
||||
* Stick to the standard capacity scale on tunnel rejection, even for
|
||||
the 10m period.
|
||||
* Build the time message at the very last possible moment
|
||||
|
||||
2004-12-15 jrandom
|
||||
* Handle hard disconnects more gracefully within the streaming lib, and
|
||||
log unmonitored events more aggressively.
|
||||
* If we drop a peer after connection due to clock skew, log it to the
|
||||
/logs.jsp#connectionlogs with relevent info. In addition, toss it in
|
||||
the stat 'tcp.disconnectAfterSkew'.
|
||||
* Fixed the formatting in the skew display
|
||||
* Added an ERROR message that is fired once after we run out of
|
||||
routerInfo files (thanks susi!)
|
||||
* Set the connect timeout equal to the streaming lib's disconnect timeout
|
||||
if not already specified (the I2PTunnel httpclient already enforces a
|
||||
60s connect timeout)
|
||||
* Fix for another connection startup problem in the streaming lib.
|
||||
* Fix for a stupid error in the probabalistic drop (rand <= P, not > P)
|
||||
* Adjust the capacity calculations so that tunnel failures alone in the
|
||||
last 10m will not trigger a 0 capacity rank.
|
||||
|
||||
2004-12-14 jrandom
|
||||
* Periodically send a message along all I2NP connections with the router's
|
||||
current time, allowing the receiving peer to determine that the clock
|
||||
has skewed too much, and hence, disconnect. For backwards compatability
|
||||
reasons, this is being kludged into a DeliveryStatusMessage (ewww). The
|
||||
next time we have a backwards compatability break, we can put in a proper
|
||||
message setup for it.
|
||||
|
||||
2004-12-14 jrandom
|
||||
* Reenable the probabalistic drop on the TCP queues to deal with good old
|
||||
fashioned bandwidth limiting. However, by default the probability is
|
||||
rigged to reserve 0% of the queue free - meaning we just aggressively
|
||||
fail messages in the queue if we're transferring too slowly. That
|
||||
reservation factor can be increased with 'tcp.queueFreeFactor=0.25'
|
||||
(or whatever) and the drop code can be disabled with the parameter
|
||||
'tcp.dropProbabalistically=false'.
|
||||
* Still penalize a peer on tunnel failure, but don't immediately drop
|
||||
their capacity to 0.
|
||||
* More aggressively ACK duplicates
|
||||
* Randomize the timestamper period
|
||||
* Display the clock skew on the connection logs when a peer sends it.
|
||||
* Allow the timestamper to fix skews of up to 10 minutes
|
||||
* Logging
|
||||
|
||||
2004-12-13 jrandom
|
||||
* Added some error checking on the new client send job (thanks duck!)
|
||||
* Implemented tunnel rejection based on bandwidth usage (rejecting tunnels
|
||||
proportional to the bytes allocated in existing tunnels vs the bytes
|
||||
allowed through the bandwidth limiter).
|
||||
* Enable a new configuration parameter for triggering a tunnel rebuild
|
||||
(tunnel.maxTunnelFailures), where that is the max allowed test failures
|
||||
before killing the tunnel (default 0).
|
||||
* Gather more data that we rank capacity by (now we monitor and balance the
|
||||
data from 10m/30m/60m/1d instead of just 10m/60m/1d).
|
||||
* Fix a truncation/type conversion problem on the long term capacity
|
||||
values (we were ignoring the daily stats outright)
|
||||
|
||||
2004-12-11 jrandom
|
||||
* Fix the missing HTTP timeout, which was caused by the deferred syn used
|
||||
by default. This, in turn, meant the I2PSocket creation doesn't fail
|
||||
on .connect, but is unable to transfer any data in any direction. We now
|
||||
detect that condition for the I2PTunnelHTTPClient and throw up the right
|
||||
error page.
|
||||
* Logging
|
||||
|
||||
2004-12-11 jrandom
|
||||
* Use a simpler and less memory intensive job for processing outbound
|
||||
client messages when the session is in mode=bestEffort. We can
|
||||
immediately discard the data as soon as its sent the first time,
|
||||
rather than wait for an ack, since we will never internally resend.
|
||||
* Reduce some synchronization to avoid a rare deadlock
|
||||
* Replaced 'localhost' with 127.0.0.1 in the i2ptunnel config, and special
|
||||
case it within the tunnel controller.
|
||||
* Script cleanup for building jbigi/jcpuid
|
||||
* Logging
|
||||
|
||||
* 2004-12-08 0.4.2.3 released
|
||||
|
||||
2004-12-08 jrandom
|
||||
* Revised the buffering when reading from the SAM client and writing
|
||||
to the stream. Also added a thread (sigh) so we don't block the
|
||||
SAM client from giving us more messages for abnormally long periods
|
||||
of time.
|
||||
* Display the router version in the logs on startup (oft requested)
|
||||
* Fix a race during the closing of a messageOutputStream
|
||||
|
||||
2004-12-06 jrandom
|
||||
* Don't do a 'passive flush' while there are already outbound messages
|
||||
unacked.
|
||||
* Show the reseed link if up to 10 peers profiles are active (thanks
|
||||
dburton!)
|
||||
|
||||
2004-12-06 jrandom
|
||||
* Don't propogate streaming connection failures out to the SAM bridge as
|
||||
fatal errors.
|
||||
* Dont barf on repeated I2CP closure.
|
||||
|
||||
2004-12-05 jrandom
|
||||
* Explicitly use "127.0.0.1" to bind the I2CP listener, not the JVM's
|
||||
getLocalhost call
|
||||
|
||||
2004-12-05 jrandom
|
||||
* Default the I2CP listener to localhost only, unless overridden by
|
||||
i2cp.tcp.bindAllInterfaces=true (thanks dm!)
|
||||
* More SAM fixes for things recently broken (whee)
|
||||
|
||||
2004-12-05 jrandom
|
||||
* Fix the recently broken SAM bridge (duh)
|
||||
* Add a new pair of SAM apps - net.i2p.sam.client.SAMStreamSink and
|
||||
net.i2p.sam.client.SAMStreamSend, mirroring the streaming lib's
|
||||
StreamSink and StreamSend apps for transferring files.
|
||||
* Make the passive flush timer fire more frequently.
|
||||
|
||||
2004-12-05 jrandom
|
||||
* Fixed some links in the console (thanks ugha!) and the javadoc
|
||||
(thanks dinoman!)
|
||||
* Fix the stream's passive flush timer (oh, its supposed to work?)
|
||||
|
||||
2004-12-03 jrandom
|
||||
* Toss in a small pool of threads (3) to execute the events queued up with
|
||||
the SimpleTimer, as we do currently see the occational event
|
||||
notification spiking up to a second or so.
|
||||
* Implement a SAM client API in java, useful for event based streaming (or
|
||||
for testing the SAM bridge)
|
||||
* Added support to shut down the SAM bridge on OOM (useful if the SAM
|
||||
bridge is being run outside of the router).
|
||||
* Include the SAM test code in the sam.jar
|
||||
* Remove an irrelevent warning message from SAM, which was caused by
|
||||
perfectly normal operation due to a session being closed.
|
||||
* Removed some unnecessary synchronization in the streaming lib's
|
||||
PacketQueue
|
||||
* More quickly clean up the memory used by the streaming lib by
|
||||
immediately killing each packet's resend job as soon as it is ACKed (or
|
||||
cancelled), so that there are no longer any valid pointers to the
|
||||
(potentially 32KB) packet.
|
||||
* Fixed the timestamps dumped to stdout when debugging the PacketHandler.
|
||||
* Drop packets that would expand our inbound window beyond our maximum
|
||||
buffer size (default 32 messages)
|
||||
* Always read the ACK/NACK data from the verified packets received, even
|
||||
if we are going to drop them
|
||||
* Always adjust the window when there are messages ACKed, though do not
|
||||
change its size except as before.
|
||||
* Streamlined some synchronization in the router's I2CP handling
|
||||
* Streamlined some memory allocation in the SAM bridge
|
||||
* Default the streaming lib to disconnect on inactivity, rather than send
|
||||
an empty message.
|
||||
|
||||
2004-12-01 jrandom
|
||||
* Fix for a race in the streaming lib as caused by some odd SAM activity
|
||||
|
||||
* 2004-12-01 0.4.2.2 released
|
||||
|
||||
2004-12-01 jrandom
|
||||
* Fixed a stupid typo that inadvertantly allowed persistent HTTP
|
||||
connections to work (thanks duck!)
|
||||
* Make sure we override the inactivity timeout too
|
||||
|
||||
* 2004-12-01 0.4.2.1 released
|
||||
|
||||
2004-12-01 jrandom
|
||||
* Strip out any of the Accept-* HTTP header lines, and always make sure to
|
||||
include the forged User-agent header.
|
||||
* Adjust the default read timeout on the eepproxy to 60s, unless
|
||||
overridden.
|
||||
* Minor tweak on stream shutdown.
|
||||
|
||||
2004-11-30 jrandom
|
||||
* Render the burst rate fields on /config.jsp properly (thanks ugha!)
|
||||
* Build in a simple timeout to flush data queued into the I2PSocket but
|
||||
not yet flushed.
|
||||
* Don't explicitly flush after each SAM stream write, but leave it up to
|
||||
the [nonblocking] passive flush.
|
||||
* Don't whine about 10-99 connection events occurring in a second
|
||||
* Don't wait for completion of packets that will not be ACKed (duh)
|
||||
* Adjust the congestion window, even if the packet was resent (duh)
|
||||
* Make sure to wake up any blocking read()'s when the MessageInputStream
|
||||
is close()ed (duh)
|
||||
* Never wait more than the disconnect timeout for a write to complete
|
||||
|
||||
2004-11-29 jrandom
|
||||
* Minor fixes to avoid unnecessary errors on shutdown (thanks susi!)
|
||||
|
||||
2004-11-29 jrandom
|
||||
* Reduced contention for local client delivery
|
||||
* Drop the new code that munges the wrapper.config. Instead, updates that
|
||||
need to change it will include their own wrapper.config in the
|
||||
i2pupdate.zip, overwriting the existing file. If the file
|
||||
"wrapper.config.updated" is included, it is deleted at first opportunity
|
||||
and the router shut down, displaying a notice that the router must be
|
||||
started again cleanly to allow the changes to the wrapper.config to take
|
||||
effect.
|
||||
* Properly stop accept()ing I2PSocket connections if we close down the
|
||||
session (duh).
|
||||
* Make sure we cancel any outstanding Packets in flight when a connection
|
||||
is terminated (thanks susi!)
|
||||
* Split up the I2PTunnel closing a little further.
|
||||
|
||||
2004-11-28 jrandom
|
||||
* Accept IP address detection changes with a 2-out-of-3 minimum.
|
||||
* As long as the router is up, keep retrying to bind the I2CP listener.
|
||||
* Decrease the java service wrapper ping frequency to once every 10
|
||||
minutes, rather than once every 5 seconds.
|
||||
|
||||
2004-11-27 jrandom
|
||||
* Some cleanup and bugfixes for the IP address detection code where we
|
||||
only consider connections that have actually sent and received messages
|
||||
recently as active, rather than the mere presence of a TCP socket as
|
||||
activity.
|
||||
|
||||
2004-11-27 jrandom
|
||||
* Removed the I2PTunnel inactivity timeout thread, since the new streaming
|
||||
lib can do that (without an additional per-connection thread).
|
||||
* Close the I2PTunnel forwarder threads more aggressively
|
||||
|
||||
2004-11-27 jrandom
|
||||
* Fix for a fast loop caused by a race in the new streaming library (thanks
|
||||
DrWoo, frontier, pwk_, and thetower!)
|
||||
* Minor updates to the SimpleTimer and Connection to help track down a
|
||||
high CPU usage problem (dumping debug info to stdout/wrapper.log if too
|
||||
many events/tasks fire in a second)
|
||||
* Minor fixes for races on client disconnects (causing NPEs)
|
||||
|
||||
* 2004-11-26 0.4.2 released
|
||||
|
||||
|
40
hosts.txt
40
hosts.txt
@ -1,6 +1,23 @@
|
||||
; TC's hosts.txt guaranteed freshness
|
||||
; $Id: hosts.txt,v 1.79 2004/11/22 22:58:46 jrandom Exp $
|
||||
; $Id: hosts.txt,v 1.96 2004/12/14 23:23:16 jrandom Exp $
|
||||
; changelog:
|
||||
; (1.119) added piespy.i2p
|
||||
; (1.118) added sciencebooks.i2p
|
||||
; (1.117) added forum.fr.i2p, fedo.i2p, and pastebin.i2p
|
||||
; (1.116) added frosk.i2p
|
||||
; (1.115) added theland.i2p
|
||||
; (1.114) added dox.i2p
|
||||
; (1.113) added amiga.i2p
|
||||
; (1.112) added frooze.i2p
|
||||
; (1.111) aliased gott.i2p as jrandom.i2p (ed. note: no, i am not gott)
|
||||
; (1.110) added sonax.i2p
|
||||
; (1.109) added 1.fcp.freenet.i2p and copied fcp.i2p to 2.fcp.freenet.i2p
|
||||
; (1.108) added asciiwhite.i2p
|
||||
; (1.107) added fcp.i2p
|
||||
; (1.106) added greenflog.i2p
|
||||
; (1.105) added bdl.i2p
|
||||
; (1.104) added bacardi.i2p and guttersnipe.i2p
|
||||
; (1.103) added evil.i2p
|
||||
; (1.102) added bsdm.i2p
|
||||
; (1.101) added eschaton.i2p
|
||||
; (1.100) added blog.curiosity.i2p
|
||||
@ -228,4 +245,25 @@ slacker.i2p=BR~D1lQNF~NLjdrJILDBA4DaQZpDoQZhVFNAwBgUz3sEP2tHnuiS7-EO2jwXEPKHIVZN
|
||||
blog.curiosity.i2p=GqgGNWvNcbEABjAxBNJmILDIVJq0GSpf3eyjQt5EQsJFPCA3fYFYUULh4WoPFEk3I4pOV0lPGlkFX6Q52nkIk~kW~YGsCjxDJJ~08m31KgPJ70-svh7Fj-D8Bs27VaG9nXEltCJfjzfHsNY6m5jCCf400-14jTEghAjguIwPbpCpJ351jrE36sSU861LhhKnCiTItIN6ig9LROIUOJnTvh4-vRHrHS~oQVesAUqZ0zMPRNYlC1GHgTvou8iKLXlPnq3u1ITfCQYjeC6lpNn3bnnJOfrzGsPW3Y70jG5fqTPRqCyef5j0gNa~ZewMAQvV~oKxpDFTixNx8pBRv913pRXTdZfxT8D-fpYbXP1O0GtaHTgPO7PYWFKHCAaxmleD7tzSpIyfl4LpO0Y1Be5eHAZh5Lh1nE~tqVJZUbcVqZzDPmrmT20oCDljsZ8kEIRb3mn3VpzN5wOikPTh8AHg1jfic9hGBv5Lqu5SBffSCQZi7znTB7peX3ZavGODotR2AAAA
|
||||
eschaton.i2p=YTlIyW42pdmE~D8fc3eUSdyN9x~SsddHcAXi3hISTTovLO-CphmfAqyvAZ1GV2-xExVlK7u~WBT8w9NBeig48NsAXCkNuWvZS~hzw9AERj5HVe5Jwzvd78x6rsFLaYbAFeSngtH~Bxi~ZLfLtXbcD7aZsXh2bCSV8OwKPRmPiKsV48Bwi9BSJA84VQLtpnbg3MtSDAciLVVH2Prvu939CciLAkdaST6JWBDY553c~4RlaHkxR~uHsskO0sNnCp2ASGRR8zoaDTP9eKhy9vXcCJZ6EYKDmRGcb~0hpuB67lP47PJaiNCWNk5jI~VNdCUE2O9D-XBPA88qvDZVK0wZWTL3Hxh5uvqr-rKZ2Txlh-qZ1vispjv47VC88ht~mQi~PGbqZxvPIOe9LxemIjlBedDjcS675cyVm-stwx-8G8au-hE7QNhZ3XN8kRlNcCTtMJxX2QRXT6juCZVNXfnO~sCexp7inl7~Q~yCeBbOoWhQoOOsm7kHMNEGxOkpkxjxAAAA
|
||||
bdsm.i2p=qOcqbBo4vxHbldI758R76ZJZu92zWefovF6IEEv~XKA-UkHNbY-y6v9w-9ts-tbZxpak8l1QyB6Mp1Rwy-sE1YxevjIPhV-UQZSsJHRJHVZasR3ULncY-g-dvzGLab9YGtkdfub2GMlCJzDm9F8sUvGnLxlZ28mj3~ZLC0vEt5BeCl5hDuT9CiAzWarcSC2M1PIOMxIUYz9K5ueanOZjmAkNet4igKM0XmfXL8c85VeiPO8jx9G0ZDARwObXxvNdPq5pDwOZSY-5f-crDWMV1KesdmqsKIgydZ5GSPzq5jKLJdY3bUsxViNoWzjIWKfbwm6Wgl3eb9wzLEXDP1m2WGWAetNCgwSbT30A2yhTEWFwEqSE0GvWKvlztyG3oGm8eKN-VymRwduORnpKYbALNZ2MG4ZtVhKklER4hrgYSLuuXIi7KhQ0WXrKjPFnnL2MmnpPsFYn0ZQz6ilYOSAYcV3rBsd83RAFPWodlCjt0TpnpvcFfYZqfKZtYQryzkdFAAAA
|
||||
evil.i2p=NGNaN9qrpk2dC~OQlo-1yHT~Tvb6SvU8VH9SiBkwNZxuNVRKN~2ZtU50nkTklevB5V08GMT5tkpyZCyVwVgZNNl50uz25t~xP3Pte6GUxzBfBJ8n2GoFdlhn5jGHhmM6QI3nlDgLO7AaNdIQu8LxaoO4lln~fReKvY3sNRMAOJlBO0mHH1PSsemJmqkhl02VX6-3KllgZ99E7uVT-ap2i7Pf3wvLtBtoHryZXvYtKL9zV17tBfhmIVT~VSuhsGRVoy43eoep~fPNCE3s0jr4GLEeWfx2LF0WsvN2avii43DFB23BaZAKd7myLV~pmN9L7pOILnsrktO2MQXy2q2OriZs8dxN3w0yboE5iAfTt8IWtHfNLp8aGx2HzmF1TacrPxuJmg~C9mawTGtZ2aKMBr7vHtN1VuZnOdVi5hcC7tQ0YuwPtAkOB1iO2Wbh3csYtfusPoIqezP1O-iUndNCRg86u3PZ71jXhSq4mMZaNZGYiiqB7nXVkBi15nUbQRb8AAAA
|
||||
bacardi.i2p=oZm0JRHiUFKwAzLz1wlNOK2h2fI8V6u1nUhgCpt1RcErs99QMPHqu4oR4cel5lsJbeg1X0GgHe72JYabsntjimjWs1zi0RzknddVvq0hMGnn9EA-9Atu9qViScXp42ddnYhIlMBNNswMp8AJ01jHkO3SUSDSVk-rF5wnrhpyN9BFyho8h18nHrr6S4jaGHsuLVage8ImwQRv~PYfr4hVULdiFn5HDRePdvgzKUD3o4Y7nFiQMXswP02ZhXXtE-rMGDs2TD15qYah6mkQWdZtRYFuKuCh7Myn35u2IHGyMs56zpnuVr~w~Uh7wydg26JShsAz4AyZhGy70eTFUC2dtEr7bUvQSE3V3xNKOeBuGYadipWB30xmGDX2kfaU5Efy45MLPBwTU-8f4owQBokgSD3jBEc4vV0DbJrGoNnA3zpAC0JwZ9rFLolQNzenRgkWYlO09PEkKPUyUMpUBGY~Wj7jcsGnt5uNskeHHpV4hsvdpboHnlPAcZKvN7rTCenoAAAA
|
||||
guttersnipe.i2p=yS6ECPvCobRLKMXJg-rLZ5PF6dRcXliR2e9KEX~aUZMEsznpzE-lCs~FgGd7w9-~GW8ObvKiyt47S7SuCunjndl874xPhrQ0dFRUNEsdovLBr6s9v2xi7kpWC1yKLbT-Ti3p8WhNPZkGsJl6YEIc357j4FjNVFx2-b~V16G0lhjhY1a55nsCoU7xXisdqOaKZL-4Y7vEVHqZNgM6cAZpY8vHtui30J3oGmV-RcJG9ahYkorqleKtb2G0fwbUMakxHv5uzlHyAU~r7OmS7NCIfoyBgU3MrSiwkYiW5elQ294XV-1Bciy9DFnJh4KA0rLIA7GanvkQ-NwqoGLsXz08mpi12vVssDhdavXooBeZLg7gipFYzle3-B1rgGKZ~51~9Tou~XSn6jGJGgtqKV7fTmys4~AXNy7vCWrMKQaWh6M1HI69yV466bJWv6nVhwNmOmtk48EIC3ik2IXC1wjA6Rgz2AcSiR-UEyaBkv251Vu6H92HZJTrsTzuzVmZcYt0AAAA
|
||||
bdl.i2p=IMzcrD4jQLBtyzV8TdL-jgbyDhRjMBS1BfI6xGuddMyJEoMnZ1ZWDUn3NwqdyEYRQD7zREgbsQgzpE7Gdmpp-vLrzba3W1mUR5Ddu13tgIiDrOWR38Omv7L~DTDimK9m0Y-HCJROVigRfxdZbsI6P37d77NArdpwLzFos6qyK2C40JQFIaNCPYGkf05DsXtHLPwTJXYVKVp0R2V7nGmI77BUdwDgt4zSotlyvmsX6U1mYqKwutwr~oxggdvgfoNbrGC0~xQCbfQtEOwFYwxc5oUeJlt4jjb1-C9HAb7r4LtJ3Daqr0bx1hXS5hADw68cfUHbEjbfrhJhBA0mMqgGMm~r~0II8R19EXprc91YY4d0QycR2Osdn9MVNXS01Ziy~JX-SfT8DTjj2ZZn7rpyjcq7rgVbspPLJOdNQNERiAm06yjosOrPjtl8mrWXxWcOcGlMO5ftoTvX2hFsfc-vmfN7S8IZEfUdJRpJxDytChNTUr~qMWuP0LicNv0xKtA3AAAA
|
||||
greenflog.i2p=4RdKwi7a5wrn-B1crYUuH8H5xHExl8oIxouASnG5Q-s--Sk2eQr7ajeXCdkxzz0Ep9CKhGY4rARRWvCuNYokkl3Jzpc-17oj-Qrr46kRdjqad2AlcJ0q920nmU3tjoqoE4qNshnP7H7NcephURg2GbHsrmUJm1EqA9SsTPk6~kUvvPiLtpbEAoas9uv0a5oI-Ykpwc2VSuZnk9hZQ25ndeW6pSH68FCbzZZfDVNAT1vcTtKGYoop2rmaGTkRsxT0vCG06S8iAoIBt4kHznZRldnnND5n31Dz8rYB7ICLplN54vlntrcjaL82HGSdxSmPg5crG3bwbvwC-7jspiZNGHFM8gun047A3b5g4YaNmCuzrLGgoW5pMlCWgOK7Wdf8NTQ9yrY1iBRIua2-zZwVEuCaIR8mlRdZ-UW37kad-OabJ18iavapc0iz~CaJBer4zlrJev3H9TfIMlk2WhG~SSDW9Y3dELIoZDfvgkfIcyPrNJThrcxpFZWGREhFuI5oAAAA
|
||||
fcp.i2p=yY8ahms44MDj2cdI6o7Fc2ozz-jM4ACEp8t4YJiRsKgEYx6XNB7QrNK8jZQoKIQ8G6GMmKMYsgRGmbwWFt60au5HFNv56NWsB4Nd80G9iSRLiEHOHhaUSFnR~ZnSdzL-nbMqA8-vzteaqZ8HNnC2NNjJXsMyULFdkBsIKYS0jsS5hl7ddf0v69X7sCSp1CCy5H28rmZSEtKA4aYXGcBHJ8bXFomV5ovy~Mh2xZzWdeVmjdmJACG~ekDBcMkm7G5mlYoTN2aEBghxiHv~plGlHFqERB3lgISlomYIwYwPqxcmpxwbg9OSEo53t45P9~dat8XMvabzXiUdm0UgVnFsNBAIyNgyTEktw38~5v2XHhqpn8RSoMUeNOqp~k740v9fYH7xxHYCrfN7y3rKg~~GlWekbNmX5g0UvKjV-QWHIvrdOVZmII9qNTTwm3Uf2Tyi2aFll7TF9CvKUJCI0Mt1BpQEmPQdY~FgFXNd0p-~DvlRrwaI4jorbn4eUUEJqgzFAAAA
|
||||
asciiwhite.i2p=kJJmuP~sE0SrQFX-nyV2cEFDffnqv3D4-vBNJX6wqEq7vbTjQzZVn3jtt5kYlv4FpMyJnhPloYJWZNw-Yt2opdRnxNGpY~AHnI~iW-rmjuwkv0Qp~NG5sKEfTd7b6tAcop87He9FZ~ANNMmPpJbS1CNGGxLtkcceg4El7pJvodCp9uPjtAfMqnsKUYK6HKg~Wlz-8yC1QUxufkoT6PF645BNWp9PUVioCnUIaqizqBmDjTuUNqf9NOXnoQnx2PuqXxHGphKJqLp8faexDSTvLgns~yudyACMexzdnQMdEQJCkUqvwFuyeS2-uw6OFfHfACIc1yr3UdHPPFzsHT0WtwF5rFBpJqeWTiWph~Vas26824PdM-sK-PKo388UemmDJmY1YknGxsky6qhDGYkFHtWXF2D7fuOL~6V1aVw8DpIGW4PS~pjug9wtPoUkcKPd4zey7HNlgnVae4P7hviKlwRPPnxfor9athm1omU75GlIEwUi79GqVPb4N~qrbZOfAAAA
|
||||
1.fcp.freenet.i2p=r2zbc34IQSzOIF4N0enKf0xXkJKgsj9yTGGspRnstKZf~4UoAljZOW5aFZywGo-NlaXwt~tIyj4NC0Til0vl1D5N9ip7OMYUCajNNgiXEH~FN33yl-AcJbeTlB-FychSmVfYciTQj6yd19~6wICwkdpy6AYo90bAejSVGpvtFeP5P2pnSwPmcB8m79wyq~C2XjQCe5UcBxnfYolWKgr3uDFrgbhqBVCCkO7zTiARwOWZLVOvZsvKZR4WvYAmQI6CQaxnmT5n1FKO6NBb-HOxVw4onERq86Sc6EQ5d48719Yk-73wq1Mxmr7Y2UwmL~FCnY33rT1FJY2KzUENICL1uEuiVmr9N924CT9RbtldOUUcXmM1gaHlPS40-Hz4AvPxFXHynbyySktN3hBLPwfwhyIQw95ezSNuiBB0xPcujazCw02103n2CO-59rMDmWpttLjpLMggP9IwsAPa9FVLnBqfuCn3NrC4fia50RDwfR41AD1GOOWiUT0avYzbbOdsAAAA
|
||||
2.fcp.freenet.i2p=yY8ahms44MDj2cdI6o7Fc2ozz-jM4ACEp8t4YJiRsKgEYx6XNB7QrNK8jZQoKIQ8G6GMmKMYsgRGmbwWFt60au5HFNv56NWsB4Nd80G9iSRLiEHOHhaUSFnR~ZnSdzL-nbMqA8-vzteaqZ8HNnC2NNjJXsMyULFdkBsIKYS0jsS5hl7ddf0v69X7sCSp1CCy5H28rmZSEtKA4aYXGcBHJ8bXFomV5ovy~Mh2xZzWdeVmjdmJACG~ekDBcMkm7G5mlYoTN2aEBghxiHv~plGlHFqERB3lgISlomYIwYwPqxcmpxwbg9OSEo53t45P9~dat8XMvabzXiUdm0UgVnFsNBAIyNgyTEktw38~5v2XHhqpn8RSoMUeNOqp~k740v9fYH7xxHYCrfN7y3rKg~~GlWekbNmX5g0UvKjV-QWHIvrdOVZmII9qNTTwm3Uf2Tyi2aFll7TF9CvKUJCI0Mt1BpQEmPQdY~FgFXNd0p-~DvlRrwaI4jorbn4eUUEJqgzFAAAA
|
||||
sonax.i2p=s~FYxHeR1n0aN0kuilNtJU9IbXRX~jWI6vGz4TUf6oZyjZBYYmY9OpgDa3sr-8AERqsuG6EKA~UBn4KiQdkI-LU16DwEHvbgfZXAdwY73G6SkHOgwVAnT2SIOuh90v1QevYtxiGpA7IVm6icoz5V4I6V29hPlUNJxBFF~Qn2cY6zj0d6wBk6ulYc0vbyOwu8Lfc8T8bTnBLut3SZWgKHYLbxabcXJtcQpoN7k815N1MEUi0APD45o5TwPhvpHLt8fF10aNcMScpL01RR~tKHgC6jPX~HKM~Ld4l0z0lvLeq9197Qp6zHRvRD3YUQxyvgHi2ANkYprvJof1Abgs8TD1JWkZUAzlfqFPyjO~f3pJSDd5eM7E0Fk69ym1Y4Rltih~Y2N1IVtJPfo9Tx9a3RQWOJZ0MN12xfiZjyF92gDRgfKW5RDpwCoTVmP79u~WUr8lQ-qQbKhsMQQqMn8Uy8DeXvKLRpHaECtevjohL4lWBBR0ezXlBLCXKqaVXKcMTjAAAA
|
||||
jrandom.i2p=BxciUZkGQwi6Sj1Ucg5GGagDdwujlj8ClePa4~3d-1nnRJsBhTNJtvs-UuQYY77gPGNlpNk9dt02mxeS7f~pEC4E1KxJH9mhnf0OlIGB4hOOhDlXokAaKE2u1E-vVCDJlZCq32r~Ne53w-L2m5h5FAq5Bx7NXrTzWAEPgAlC8A2wASJWIF-EKOX5kfkkYoF6sKZqam5CxAAAMAUwmnbD--7wo8d2mz0C-c~oE7hzuHsg2J8yME5Zd~-FOZUxkoNCBJdfrVlVRn9~6685zqpaotL-SIqFK6~28ZCNMNCtnZcZZLeG2gZxq2pe8HtgzDrMMSx45vs3JLUp8Bap6~D9oFX0o7k2DQm-5R9D5NWhsJgol5Ny~8ABTXBpW~WQQOuKxE5xJauXM5rQia2yyXgldfOyGjxYnkrTGu6LaxlLmhyAva3ZbOO0mtLvrKLZebLKCYUxP7~TtNmSWEAzPKFYbjdZ~NjE0q4TanLFBaWotefPmy3IuAc8Mr7PbCvi4GmdAAAA
|
||||
frooze.i2p=kyjnpna6HHapU~tLQXRCHrX6Dpb2YCMEtFTloKF298480axGCZFY-WtpjW2Mo1ci5FlE7dkmEQ1PU0GfHZ6s0O~Hert5AcZpKh5nxRIMtS7uc~oErYCiX0yQp2P9D47B9VU5D2UmjYKWovqbrbKEAS1F0NDxXh65Ppnv-oBHVksLp7IrKLU3IOesdYtGcc9cSYJB6~mdbtqXxgHNNLTkyouSJKgSlJoe-tk1T3eYEsOJ6vzpeS6pNa6Px2dknejT~aVeNKLhjsXy706g22Mbl1-zCL87OE1bL2qWQbQN43qpLwCSv0LbRovO3r6iXRWWWrKhYj5nti~EQEWapJ2OL26~ElrpD9V9gbUVLZoxeYuu~-GKKwg6CvZYKtdf6YBYTZ0Ef4PUcv9faSqp7S4emcGOLQnSW9LezXhGhn0OPaAO~85nhdY~OfXDZ2Dg2tYYF-D70SzKycfHFLgKBQ9fkCxpOP0MHhhgGmKHabPvDHyQRFppcKLN6AYmUH1Lke-jAAAA
|
||||
amiga.i2p=-lqgoqA5KcJCuGilYz~cOBK3X7MI5VFBK7UwpSEwqbiQgSe340wABmE8V-tX7xH7rjc4Z2rGxqas5NKuOL3pCJozeKF4Z61d05nCemSXbLrqZvJUkGuZJfUz9lzeZ5OGO8L32o-I6DIGIUgFk6zRpiRW0lqO4lzV~fbiHacDhUXPLBM5gYjvZ319MLoVPp-YWG-STJFhjmAnzvj~8h-jJXHowM72YsWPo-B~oD1YtcAcXkpxVJOaLlqQq2yfjJv7YgIde3tqQ6drH7xmHdG~nF~dsu2dacqqt~HjDiUnPd12vJEhR18DDkVJVhDUbnUCcI83bt5RDdsMK8mSKRTie3h1sUam~1fLr8~gnEGTFp-yivGV1JkeleElR-rTJC7PsrlPCWzW2N4l1IMzRGOedJNAyqeXbkfjHtDBd58iZPugS5BZZ~u5MGqXgfu9TYFnLnTIGuMc3a2wSpe3miMvf2PKFnM~1nzStN0zEjFwhn4tZhOWA1TfTH5HMutqrxGxAAAA
|
||||
dox.i2p=ygf2mdGRnU12BNXXF-26OFhfPpAMigwasf93JEHVYEICVmz6Fk2BBKiGBWL4YNFUtcFo7y1iG3ms8BdmTBBkgCgEasuFZNzphEMgcNYi0eYAg1QLNgmbr5HEDpBNuXIV7fOpxYn6iij76oA09e5opPNdOLUk80kWrgrlJTel61n90uzEzz46fWIbFEw5K8xR-8aqLgC2sJDzGj8vEBY2NDKmCtjRK7aNBUayh~8vFfJyo5oN0gw8b5cOCrfSoxzqdpzI5CJ7070GjznWbWh~pueKWF4CpHn92ll3jvyd86KvUwdQh1qr1B-8fk6Oh~M9l5HRzqdUsSzp8SiG7WvLQoXrNdVX2Sjw22xOZDr7kOfEHZWYUdmrcYyqNLuIT2vgMFXdvCZnLhhGSaihkzuFqZv-34oGSqGDVlCanAPC4v6492IRRK7RiYnUo-JPhuWgpg9CdFTZtULa~drZbWNCZvAekn44D42wDx5V5vAJxuRoyir1s4gAnXHL7n9r7xdiAAAA
|
||||
theland.i2p=Lf52G95rF1kjlRu29NMpPQ46Ivd2Nz-sdIhvx5D4qU3Y3MPs50GNTjg81yJmUgbg95Q90Eyf~SHsgbzdlnjr-qQEZOXIgLQErq2UhUFGwUX6muqkCYYDIAZ-f7EI-yJiU-3~KDwWu77z-8XDjaJIf78tNV6kN3MvRwUSbBRLD7pAa3HtdhNnNyO-PPBJJVMxorQ-~AgEhmdgLj39WfgetETiRm4ew0NsN6IOHWn~~vh26FJ4awhLpJ0fCBD-fPve~jTbCovjsszKMBumTslvQS~p-dfjfQSTMRZAE3fs262NjdEwoVZP-k~~0HmzOmV9M-Oe8yez6ORDKm8sDyeDoQ~e21b77SiE2tNrgHeB3siITcZoTNyICX~ZSG~Qnb4k4kXdllVkqmXaf7k0pYE1yTKXM3sKphP~VJFMBgdSzZPprhp6RGjWibKy~94Ns8IyCA6c08810yodEdoeU53acQPp~wk0FP1HgcF3tVggk-wHg65cD09ncjOmly5Gc7uZAAAA
|
||||
frosk.i2p=Dez-OBQLhsI7Wq~O8YmYYJjl~IPvqUCZbLIFVMnyyqOCG9TEO~kGUVpWVD5nF0B5fE8kEWq8CuGKeYbVfqt0Vuz6Uvvo4gCdGnthohthbolIOZ9ZCci9MTNE6t29mJ2thHqc9Nw5TvYJGcvCVNi569~o5PDtYhZjVsOtrv8r3f9fUcUV-wKokD-y0fdF3Dzw4I7tJ6pfohj4avzDwwndAHXnNjB9mDgQsfjPF2KtvWLzXyAsCmfCoanV65tkc09ZX4x5QkekdVgTbD0BcHDP-o71MVufRsm4~ojRkbdSLw-VlbEcYFyeHTPBTpM68gTjueJlRwmvpXyP46w~Xnl9IEDfWu92CV9xjRt8qDxiyQ9jXR6dbWLptTF2UCznkBWvWph7P2wliTiawt~1bWGw2YvkfB6EaENYvDNY2cGn0GxpPhL4K19slEwJqX9f~EzLKYx4xaB5I8~W9syJAJSX8PsJbUlk6OhbbRoYOndd6OCRqHRUyh1w7dM~LvbY0W8hAAAA
|
||||
forum.fr.i2p=glvyM8eKBTYnlwEl3hVWDRBvfIfe-utzjGAlvDM-rwB4tb4CaM49Aeb5k0lxm7IaFjkp0u9t~1a2KIxwrU~Mj-JTJPKf0TrElVfOT6P0sVFOD3U1wllFwne5e7~0d8MeuylMrBe3dlz5KSPQ69stpDTbJgxcynoKiPMWcbmq~yQjnIvtDIpAPaB1JVYNo8mDhqMqg8GLrk9ttTHO90dYUpdI5lgxB6vR1nuPPwyFczOjVn8GvmT1DXGNoc6yAW0gCLuk7Hq-jNcNtXkNRC5TTdNiBLdLpsNMD3kKWDM04fODM5N~qp-nww-lEqfuZ3f1RT3gpjDwpMMAtKbd~VsRaCEINL5iBaAlZupW6fYTiuGUUdOZ739JK9GpQDsPXBOy~VSoM~cLXJCih12v-GDODDN0i8sSkqZ7CXDGg~jkg0ab2Y-Y2JnpYFLsgYziPtNRij1~rMCcC~-huiSfQio15gxczb~RRS-lpBQgyfaZ5JgIR2uVjWWe-uwKD-WquuIPAAAA
|
||||
fedo.i2p=pklxM9~hpluoCoiPgzMBpTHXMpwEdCMMNEatVc4gnQDOXsKkW5FbUMQ9y0vj6EUs2vUZLkwp3FP3-PuJEHMCLCTBdkVlE5JErwhZU6V3S5lOZ2jTrSuYAhcph~ML~ntjDzc-IZWgAax98ChzCpwxhEk4eP4B9vEhMG7njoxbJLfwAYu1qgq0A-Whm08rAhAcDwl8a-21Kqj8~iGq0XytMqyJU5XArq4aArWEwZFFLKJD9uR9bKeMaHIkJLNAyKIvfpgwRjYIMlIf6ekN3cpBU3B6c8NgDNPNqNBI6DQ1XGfKOEqX6d31mTOSzyjs-zgIJkt5zg-MH1pnMSVAI-vQbiAaPJc3Rjeqarv5Z89ZX~dghQFNQX1Wd7ezJy0WltaY6V7lLxd6QPTosDCDawT7ZoNhmLUNtFXI7oGS4Cm4ZTZAjZV-ZFMUXWUKFTZGyh4s2WbYqJq1Sackk8JmCvZ7JHazJ9ghnrQQ2bpzWeeGgtCPIcTnaUMPOZdsU96ehme7AAAA
|
||||
pastebin.i2p=mUDz9K6KmWe2zE4wj~YjqwD5f8pCjEbze-DuafzjtXOaj9SnRBrLNqgOTt063y9foZett484g9PFm~3ibqFZfUk3LsJi6YhZje-V~RZndBElRJ1cX~MBOG5wdHA2BYpBt7jX-N5J9ww7POtcPFDjyYlJLhRrY5FuRfxdsWJ4BOUHOwvbTq7IWvqHReayte6vKavpyvNcAotcFHAhOGpR6Ua3RncS~b6NA5k2CQIeS4mR6~iNCh0sx1gj4cWWWqXSa6pN19io82L2fcNEcxE-UETj4HkG5fTdBlB4I2cNAm1KnxQw9ntRf19p5EzYkOFYST5ra~RWmy0bWzJMBFlK9QcogVi97gmszuNPyfIpJIE1ssZ6BNHKMkPJ-fjxtSAseTBV-Fa51TIBwNyXC6VxMsixKIBX4Cg64oipoGpm~-7SdRgabHbB0vWwiV6RYEQ88oNcV2ycDqjyfQymT0T5S9b0aVd2Ey1ZWly-PR44~uC3mCctcMqfZVfNFs3NoY3ZAAAA
|
||||
sciencebooks.i2p=Rrqf6EmeuqckygNrim-ZcZ97uEIL9Ykkr8cj1RSGbeih33~UUpjAp0x2fxrpbibXwuGufWMNoaMkZH8FxOxZr1hvBeV4Mvjrn05kKWyPpBB1x7rAcVCCoD~bl6CUU2k4NtFrO9~BCCZdoIvMxXYWRy4nj18RVaIlOS3qE6~F04pU7Fy0sj7xtd02wgeyKkJ6pXYGfETMr6phVusHSSQfCumDBpnqEt7EcXTIsPz58y9Zim4u1wp6xj~OvGWFIP8fKIxpH~zf6o0qYytXrf6SJbIEvwITKGIXZTM5KxQ0LbEydSgPn4q5PttQlKg~7hxuGG5RYqP2IMg-f9z4-1SfYTbz~EixEq8gv0R30c60tqsyAaw9o37R3k9inkT5WylYwBKM0pzfcAVjejcoPYnFF1iithGl0AYkP~~isxCErCNwLs39NyE688C5amDIpXl7AgB~IvbwZfONbd2cwMOB2RFzTShYZlwcILB7EzgMvEt2l8GBkstP3CzNrjQ2gAL0AAAA
|
||||
piespy.i2p=jTCW8w04d~Sucb7OXFBZtq7tEOxeFBNm5T4uVZW3xNapThxk0Tie8OAIQMmz4lzDUaHoc97DN1s6k3rTprRrhbr7Idyls5I-r6jAQnrljqUveIMA-lbzXDYTgo8TqBR4~fpSL4RM5u4~sM4ZRKdwufnEjCSlYtTe8qmrofFx3cIuIKhfypsYcu-BEdrw7P~cRpmqJqBV~igulwfIxeABa8ygX~Uk~i68NELte55J70z~kijYGMAgUJVhtQxD~jZD5l3FrGkND9lVaam0lHseujNyy~ZB-ma5IKqJWKLEo19e224EVhIK4jcKYdnr7bE86mZiWg36GvWFhjJOkEvCJ3BWxaMjcDXyk-9vCY7MA403OCMGGHoKlzB2AKq1PEc1teMtoqMG~a42DTKevUbbiALtauxCe-BOxEtxpYTvvql-RVumrUh3hckh0wqAd1Gqr2uqRx4VaCF3X3duOxdoKV6Kgf~icfVIFq5HR7AhN63BGFhAeQLcudp3oA828a06AAAA
|
||||
|
||||
|
26
install.txt
Normal file
26
install.txt
Normal file
@ -0,0 +1,26 @@
|
||||
$Id$
|
||||
I2P source installation instructions
|
||||
|
||||
To build and install I2P from source, you must first build
|
||||
and package up the appropriate installer by running:
|
||||
|
||||
ant dist
|
||||
|
||||
This will produce a few key files:
|
||||
* i2p.tar.bz2: the headless installation
|
||||
* install.jar: the GUI installer
|
||||
* i2pupdate.zip: the update package
|
||||
|
||||
From there, you can follow the headless installation instructions
|
||||
with the headless installer, run the GUI installer, or deploy
|
||||
the update into an existing installation.
|
||||
|
||||
You will need to have ant installed from http://ant.apache.org/
|
||||
(1.5 or newer)
|
||||
|
||||
Supported JVMs:
|
||||
Windows: Latest available from http://java.sun.com/ (1.3+ supported)
|
||||
Linux: Latest available from http://java.sun.com/ (1.3+ supported)
|
||||
FreeBSD: /usr/ports/java/linux-sun-jdk1.4
|
||||
various: http://www.kaffe.org/ using CVS HEAD as of Sept 1, 2004
|
||||
(or any subsequent releases)
|
@ -4,7 +4,7 @@
|
||||
|
||||
<info>
|
||||
<appname>i2p</appname>
|
||||
<appversion>0.4.2</appversion>
|
||||
<appversion>0.4.2.4</appversion>
|
||||
<authors>
|
||||
<author name="I2P" email="support@i2p.net"/>
|
||||
</authors>
|
||||
|
@ -5,7 +5,7 @@ tunnel.0.type=httpclient
|
||||
tunnel.0.interface=127.0.0.1
|
||||
tunnel.0.listenPort=4444
|
||||
tunnel.0.proxyList=squid.i2p,www1.squid.i2p
|
||||
tunnel.0.i2cpHost=localhost
|
||||
tunnel.0.i2cpHost=127.0.0.1
|
||||
tunnel.0.i2cpPort=7654
|
||||
tunnel.0.option.tunnels.depthInbound=2
|
||||
tunnel.0.option.tunnels.numInbound=2
|
||||
@ -19,7 +19,7 @@ tunnel.1.type=client
|
||||
tunnel.1.interface=127.0.0.1
|
||||
tunnel.1.listenPort=6668
|
||||
tunnel.1.targetDestination=irc.duck.i2p,irc.baffled.i2p
|
||||
tunnel.1.i2cpHost=localhost
|
||||
tunnel.1.i2cpHost=127.0.0.1
|
||||
tunnel.1.i2cpPort=7654
|
||||
tunnel.1.option.tunnels.depthInbound=2
|
||||
tunnel.1.option.tunnels.numInbound=2
|
||||
@ -33,7 +33,7 @@ tunnel.2.type=client
|
||||
tunnel.2.interface=127.0.0.1
|
||||
tunnel.2.listenPort=2401
|
||||
tunnel.2.targetDestination=cvs.i2p
|
||||
tunnel.2.i2cpHost=localhost
|
||||
tunnel.2.i2cpHost=127.0.0.1
|
||||
tunnel.2.i2cpPort=7654
|
||||
tunnel.2.option.tunnels.depthInbound=2
|
||||
tunnel.2.option.tunnels.numInbound=2
|
||||
@ -43,10 +43,10 @@ tunnel.2.startOnLoad=false
|
||||
tunnel.3.name=eepsite
|
||||
tunnel.3.description=My eepsite
|
||||
tunnel.3.type=server
|
||||
tunnel.3.targetHost=localhost
|
||||
tunnel.3.targetHost=127.0.0.1
|
||||
tunnel.3.targetPort=7658
|
||||
tunnel.3.privKeyFile=eepsite/eepPriv.dat
|
||||
tunnel.3.i2cpHost=localhost
|
||||
tunnel.3.i2cpHost=127.0.0.1
|
||||
tunnel.3.i2cpPort=7654
|
||||
tunnel.3.option.tunnels.depthInbound=2
|
||||
tunnel.3.option.tunnels.numInbound=2
|
||||
@ -54,7 +54,7 @@ tunnel.3.startOnLoad=true
|
||||
|
||||
# postman's SMTP server - see www.postman.i2p
|
||||
tunnel.4.description=smtp server
|
||||
tunnel.4.i2cpHost=localhost
|
||||
tunnel.4.i2cpHost=127.0.0.1
|
||||
tunnel.4.i2cpPort=7654
|
||||
tunnel.4.interface=127.0.0.1
|
||||
tunnel.4.listenPort=7659
|
||||
@ -69,7 +69,7 @@ tunnel.4.type=client
|
||||
# postman's POP3 server - see www.postman.i2p
|
||||
tunnel.5.name=pop3.postman.i2p
|
||||
tunnel.5.description=pop3 server
|
||||
tunnel.5.i2cpHost=localhost
|
||||
tunnel.5.i2cpHost=127.0.0.1
|
||||
tunnel.5.i2cpPort=7654
|
||||
tunnel.5.interface=127.0.0.1
|
||||
tunnel.5.listenPort=7660
|
||||
@ -78,4 +78,4 @@ tunnel.5.option.tunnels.numInbound=2
|
||||
tunnel.5.option.i2p.streaming.connectDelay=1000
|
||||
tunnel.5.startOnLoad=false
|
||||
tunnel.5.targetDestination=pop.postman.i2p
|
||||
tunnel.5.type=client
|
||||
tunnel.5.type=client
|
||||
|
@ -103,6 +103,9 @@ wrapper.jvm_exit.timeout=10
|
||||
# give the OS 60s to clear all the old sockets / etc before restarting
|
||||
wrapper.restart.delay=60
|
||||
|
||||
wrapper.ping.interval=600
|
||||
wrapper.ping.timeout=605
|
||||
|
||||
# use the wrapper's internal timer thread. otherwise this would
|
||||
# force a restart of the router during daylight savings time as well
|
||||
# as any time that the OS clock changes
|
||||
|
@ -8,8 +8,12 @@ package net.i2p.router;
|
||||
*
|
||||
*/
|
||||
|
||||
//import net.i2p.router.message.ProcessOutboundClientMessageJob;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.client.I2PClient;
|
||||
|
||||
import net.i2p.router.message.OutboundClientMessageJob;
|
||||
import net.i2p.router.message.OutboundClientMessageOneShotJob;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
@ -55,7 +59,22 @@ public class ClientMessagePool {
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Adding message for remote delivery");
|
||||
_context.jobQueue().addJob(new OutboundClientMessageJob(_context, msg));
|
||||
if (isGuaranteed(msg))
|
||||
_context.jobQueue().addJob(new OutboundClientMessageJob(_context, msg));
|
||||
else
|
||||
_context.jobQueue().addJob(new OutboundClientMessageOneShotJob(_context, msg));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isGuaranteed(ClientMessage msg) {
|
||||
Properties opts = null;
|
||||
if (msg.getSenderConfig() != null)
|
||||
opts = msg.getSenderConfig().getOptions();
|
||||
if (opts != null) {
|
||||
String val = opts.getProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT);
|
||||
return val.equals(I2PClient.PROP_RELIABILITY_GUARANTEED);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,6 @@ import net.i2p.data.i2np.TunnelMessage;
|
||||
import net.i2p.router.message.GarlicMessageHandler;
|
||||
import net.i2p.router.message.TunnelMessageHandler;
|
||||
import net.i2p.router.startup.StartupJob;
|
||||
import net.i2p.router.startup.VerifyClasspath;
|
||||
import net.i2p.stat.Rate;
|
||||
import net.i2p.stat.RateStat;
|
||||
import net.i2p.util.FileUtil;
|
||||
@ -793,8 +792,10 @@ public class Router {
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
System.out.println("Starting I2P " + RouterVersion.VERSION + "-" + RouterVersion.BUILD);
|
||||
System.out.println(RouterVersion.ID);
|
||||
installUpdates();
|
||||
verifyClasspath();
|
||||
verifyWrapperConfig();
|
||||
Router r = new Router();
|
||||
r.runRouter();
|
||||
}
|
||||
@ -820,10 +821,11 @@ public class Router {
|
||||
}
|
||||
}
|
||||
|
||||
private static void verifyClasspath() {
|
||||
boolean updated = VerifyClasspath.updateClasspath();
|
||||
if (updated) {
|
||||
System.out.println("INFO: Classpath updated, but the service wrapper requires you to manually restart");
|
||||
private static void verifyWrapperConfig() {
|
||||
File cfgUpdated = new File("wrapper.config.updated");
|
||||
if (cfgUpdated.exists()) {
|
||||
cfgUpdated.delete();
|
||||
System.out.println("INFO: Wrapper config updated, but the service wrapper requires you to manually restart");
|
||||
System.out.println("INFO: Shutting down the router - please rerun it!");
|
||||
System.exit(EXIT_HARD);
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ class RouterThrottleImpl implements RouterThrottle {
|
||||
private static int THROTTLE_EVENT_LIMIT = 300;
|
||||
|
||||
private static final String PROP_MAX_TUNNELS = "router.maxParticipatingTunnels";
|
||||
private static final String PROP_DEFAULT_KBPS_THROTTLE = "router.defaultKBpsThrottle";
|
||||
private static final String PROP_BANDWIDTH_SHARE_PERCENTAGE = "router.sharePercentage";
|
||||
|
||||
public RouterThrottleImpl(RouterContext context) {
|
||||
_context = context;
|
||||
@ -43,6 +45,7 @@ class RouterThrottleImpl implements RouterThrottle {
|
||||
_context.statManager().createRateStat("router.throttleTunnelMaxExceeded", "How many tunnels we are participating in when we refuse one due to excees?", "Throttle", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("router.throttleTunnelProbTooFast", "How many tunnels beyond the previous 1h average are we participating in when we throttle?", "Throttle", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("router.throttleTunnelProbTestSlow", "How slow are our tunnel tests when our average exceeds the old average and we throttle?", "Throttle", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("router.throttleTunnelBandwidthExceeded", "How much bandwidth is allocated when we refuse due to bandwidth allocation?", "Throttle", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
|
||||
}
|
||||
|
||||
public boolean acceptNetworkMessage() {
|
||||
@ -207,9 +210,12 @@ class RouterThrottleImpl implements RouterThrottle {
|
||||
}
|
||||
}
|
||||
|
||||
if (!allowTunnel(bytesAllocated, numTunnels)) {
|
||||
_context.statManager().addRateData("router.throttleTunnelBandwidthExceeded", (long)bytesAllocated, 0);
|
||||
return false;
|
||||
}
|
||||
_context.statManager().addRateData("tunnel.bytesAllocatedAtAccept", (long)bytesAllocated, msg.getTunnelDurationSeconds()*1000);
|
||||
// todo: um, throttle (include bw usage of the netDb, our own tunnels, the clients,
|
||||
// and check to see that they are less than the bandwidth limits
|
||||
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Accepting a new tunnel request (now allocating " + bytesAllocated + " bytes across " + numTunnels
|
||||
@ -217,6 +223,99 @@ class RouterThrottleImpl implements RouterThrottle {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* with bytesAllocated already accounted for across the numTunnels existing
|
||||
* tunnels we have agreed to, can we handle another tunnel with our existing
|
||||
* bandwidth?
|
||||
*
|
||||
*/
|
||||
private boolean allowTunnel(double bytesAllocated, int numTunnels) {
|
||||
long bytesAllowed = getBytesAllowed();
|
||||
|
||||
bytesAllowed *= getSharePercentage();
|
||||
|
||||
double bytesPerTunnel = (numTunnels > 0 ? bytesAllocated / numTunnels : 0);
|
||||
double toAllocate = (numTunnels > 0 ? bytesPerTunnel * (numTunnels + 1) : 0);
|
||||
|
||||
double pctFull = toAllocate / bytesAllowed;
|
||||
|
||||
double allocatedKBps = toAllocate / (10 * 60 * 1024);
|
||||
|
||||
if (pctFull < 1.0) { // (_context.random().nextInt(100) > 100 * pctFull) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Probabalistically allowing the tunnel w/ " + pctFull + " of our " + bytesAllowed
|
||||
+ "bytes/" + allocatedKBps + "KBps allocated through " + numTunnels + " tunnels");
|
||||
return true;
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Rejecting the tunnel w/ " + pctFull + " of our " + bytesAllowed
|
||||
+ "bytes allowed (" + toAllocate + "bytes / " + allocatedKBps
|
||||
+ "KBps) through " + numTunnels + " tunnels");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* What fraction of the bandwidth specified in our bandwidth limits should
|
||||
* we allow to be consumed by participating tunnels?
|
||||
*
|
||||
*/
|
||||
private double getSharePercentage() {
|
||||
String pct = _context.getProperty(PROP_BANDWIDTH_SHARE_PERCENTAGE, "0.8");
|
||||
if (pct != null) {
|
||||
try {
|
||||
return Double.parseDouble(pct);
|
||||
} catch (NumberFormatException nfe) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Unable to get the share percentage");
|
||||
}
|
||||
}
|
||||
return 0.8;
|
||||
}
|
||||
|
||||
/**
|
||||
* BytesPerSecond that we can pass along data
|
||||
*/
|
||||
private long getBytesAllowed() {
|
||||
String kbpsOutStr = _context.getProperty("i2np.bandwidth.outboundKBytesPerSecond");
|
||||
long kbpsOut = -1;
|
||||
if (kbpsOutStr != null) {
|
||||
try {
|
||||
kbpsOut = Integer.parseInt(kbpsOutStr);
|
||||
} catch (NumberFormatException nfe) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Unable to get the bytes allowed (outbound)");
|
||||
}
|
||||
}
|
||||
|
||||
String kbpsInStr = _context.getProperty("i2np.bandwidth.inboundKBytesPerSecond");
|
||||
long kbpsIn = -1;
|
||||
if (kbpsInStr != null) {
|
||||
try {
|
||||
kbpsIn = Integer.parseInt(kbpsInStr);
|
||||
} catch (NumberFormatException nfe) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Unable to get the bytes allowed (inbound)");
|
||||
}
|
||||
}
|
||||
|
||||
// whats our choke?
|
||||
long kbps = (kbpsOut > kbpsIn ? kbpsIn : kbpsOut);
|
||||
|
||||
if (kbps <= 0) {
|
||||
try {
|
||||
kbps = Integer.parseInt(_context.getProperty(PROP_DEFAULT_KBPS_THROTTLE, "64")); // absurd
|
||||
} catch (NumberFormatException nfe) {
|
||||
kbps = 64;
|
||||
}
|
||||
}
|
||||
|
||||
return kbps
|
||||
* 60 // per minute
|
||||
* 10 // per 10 minute period
|
||||
* 1024; // bytes;
|
||||
}
|
||||
|
||||
/** dont ever probabalistically throttle tunnels if we have less than this many */
|
||||
private int getMinThrottleTunnels() {
|
||||
try {
|
||||
|
@ -15,8 +15,8 @@ import net.i2p.CoreVersion;
|
||||
*
|
||||
*/
|
||||
public class RouterVersion {
|
||||
public final static String ID = "$Revision: 1.87 $ $Date: 2004/11/25 16:57:20 $";
|
||||
public final static String VERSION = "0.4.2";
|
||||
public final static String ID = "$Revision: 1.115 $ $Date: 2004/12/16 05:21:23 $";
|
||||
public final static String VERSION = "0.4.2.4";
|
||||
public final static long BUILD = 0;
|
||||
public static void main(String args[]) {
|
||||
System.out.println("I2P Router version: " + VERSION);
|
||||
|
@ -103,6 +103,7 @@ public class StatisticsManager implements Service {
|
||||
|
||||
includeThroughput(stats);
|
||||
includeRate("transport.sendProcessingTime", stats, new long[] { 60*60*1000 });
|
||||
includeRate("tcp.probabalisticDropQueueSize", stats, new long[] { 60*1000l, 60*60*1000l });
|
||||
//includeRate("tcp.queueSize", stats);
|
||||
//includeRate("jobQueue.jobLag", stats, new long[] { 60*1000, 60*60*1000 });
|
||||
//includeRate("jobQueue.jobRun", stats, new long[] { 60*1000, 60*60*1000 });
|
||||
@ -143,6 +144,8 @@ public class StatisticsManager implements Service {
|
||||
//includeRate("transport.receiveMessageMedium", stats, new long[] { 5*60*1000, 60*60*1000 });
|
||||
//includeRate("transport.receiveMessageLarge", stats, new long[] { 5*60*1000, 60*60*1000 });
|
||||
includeRate("client.sendAckTime", stats, new long[] { 60*60*1000 }, true);
|
||||
includeRate("stream.con.sendDuplicateSize", stats, new long[] { 60*60*1000 });
|
||||
includeRate("stream.con.receiveDuplicateSize", stats, new long[] { 60*60*1000 });
|
||||
//includeRate("client.sendsPerFailure", stats, new long[] { 60*60*1000, 24*60*60*1000l }, true);
|
||||
//includeRate("client.timeoutCongestionTunnel", stats, new long[] { 60*60*1000, 24*60*60*1000l }, true);
|
||||
//includeRate("client.timeoutCongestionMessage", stats, new long[] { 60*60*1000, 24*60*60*1000l }, true);
|
||||
|
@ -55,7 +55,7 @@ public class ClientConnectionRunner {
|
||||
/** user's config */
|
||||
private SessionConfig _config;
|
||||
/** static mapping of MessageId to Payload, storing messages for retrieval */
|
||||
private static Map _messages;
|
||||
private Map _messages;
|
||||
/** lease set request state, or null if there is no request pending on at the moment */
|
||||
private LeaseRequestState _leaseRequest;
|
||||
/** currently allocated leaseSet, or null if none is allocated */
|
||||
@ -227,7 +227,7 @@ public class ClientConnectionRunner {
|
||||
}
|
||||
|
||||
void disconnectClient(String reason) {
|
||||
_log.error("Disconnecting the client: " + reason, new Exception("Disconnecting!"));
|
||||
_log.error("Disconnecting the client: " + reason);
|
||||
DisconnectMessage msg = new DisconnectMessage();
|
||||
msg.setReason(reason);
|
||||
try {
|
||||
|
@ -9,6 +9,7 @@ package net.i2p.router.client;
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
|
||||
@ -28,23 +29,26 @@ public class ClientListenerRunner implements Runnable {
|
||||
private ClientManager _manager;
|
||||
private ServerSocket _socket;
|
||||
private int _port;
|
||||
private boolean _bindAllInterfaces;
|
||||
private boolean _running;
|
||||
private long _nextFailDelay = 1000;
|
||||
|
||||
public static final String BIND_ALL_INTERFACES = "i2cp.tcp.bindAllInterfaces";
|
||||
|
||||
public ClientListenerRunner(RouterContext context, ClientManager manager, int port) {
|
||||
_context = context;
|
||||
_log = _context.logManager().getLog(ClientListenerRunner.class);
|
||||
_manager = manager;
|
||||
_port = port;
|
||||
_running = false;
|
||||
|
||||
String val = context.getProperty(BIND_ALL_INTERFACES, "False");
|
||||
_bindAllInterfaces = Boolean.valueOf(val).booleanValue();
|
||||
}
|
||||
|
||||
public void setPort(int port) { _port = port; }
|
||||
public int getPort() { return _port; }
|
||||
|
||||
/** max time to bind */
|
||||
private final static int MAX_FAIL_DELAY = 5*60*1000;
|
||||
|
||||
/**
|
||||
* Start up the socket listener, listens for connections, and
|
||||
* fires those connections off via {@link #runConnection runConnection}.
|
||||
@ -55,10 +59,14 @@ public class ClientListenerRunner implements Runnable {
|
||||
public void runServer() {
|
||||
_running = true;
|
||||
int curDelay = 0;
|
||||
while ( (_running) && (curDelay < MAX_FAIL_DELAY) ) {
|
||||
while (_running) {
|
||||
try {
|
||||
_log.info("Starting up listening for connections on port " + _port);
|
||||
_socket = new ServerSocket(_port);
|
||||
if (_bindAllInterfaces)
|
||||
_socket = new ServerSocket(_port);
|
||||
else
|
||||
_socket = new ServerSocket(_port, 0, InetAddress.getByName("127.0.0.1"));
|
||||
|
||||
curDelay = 0;
|
||||
while (_running) {
|
||||
try {
|
||||
@ -85,7 +93,7 @@ public class ClientListenerRunner implements Runnable {
|
||||
if (_context.router().isAlive())
|
||||
_log.error("Error listening on port " + _port, ioe);
|
||||
}
|
||||
|
||||
|
||||
if (_socket != null) {
|
||||
try { _socket.close(); } catch (IOException ioe) {}
|
||||
_socket = null;
|
||||
|
@ -145,20 +145,21 @@ public class ClientManager {
|
||||
if (runner != null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Message " + msgId + " is targeting a local destination. distribute it as such");
|
||||
runner.receiveMessage(toDest, fromDest, payload);
|
||||
if (fromDest != null) {
|
||||
ClientConnectionRunner sender = getRunner(fromDest);
|
||||
if (sender != null) {
|
||||
sender.updateMessageDeliveryStatus(msgId, true);
|
||||
} else {
|
||||
_log.log(Log.CRIT, "Um, wtf, we're sending a local message, but we can't find who sent it?", new Exception("wtf"));
|
||||
}
|
||||
ClientConnectionRunner sender = getRunner(fromDest);
|
||||
if (sender == null) {
|
||||
// sender went away
|
||||
return;
|
||||
}
|
||||
_context.jobQueue().addJob(new DistributeLocal(toDest, runner, sender, fromDest, payload, msgId));
|
||||
} else {
|
||||
// remote. w00t
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Message " + msgId + " is targeting a REMOTE destination! Added to the client message pool");
|
||||
runner = getRunner(fromDest);
|
||||
if (runner == null) {
|
||||
// sender went away
|
||||
return;
|
||||
}
|
||||
ClientMessage msg = new ClientMessage();
|
||||
msg.setDestination(toDest);
|
||||
msg.setPayload(payload);
|
||||
@ -170,6 +171,32 @@ public class ClientManager {
|
||||
}
|
||||
}
|
||||
|
||||
private class DistributeLocal extends JobImpl {
|
||||
private Destination _toDest;
|
||||
private ClientConnectionRunner _to;
|
||||
private ClientConnectionRunner _from;
|
||||
private Destination _fromDest;
|
||||
private Payload _payload;
|
||||
private MessageId _msgId;
|
||||
|
||||
public DistributeLocal(Destination toDest, ClientConnectionRunner to, ClientConnectionRunner from, Destination fromDest, Payload payload, MessageId id) {
|
||||
super(_context);
|
||||
_toDest = toDest;
|
||||
_to = to;
|
||||
_from = from;
|
||||
_fromDest = fromDest;
|
||||
_payload = payload;
|
||||
_msgId = id;
|
||||
}
|
||||
public String getName() { return "Distribute local message"; }
|
||||
public void runJob() {
|
||||
_to.receiveMessage(_toDest, _fromDest, _payload);
|
||||
if (_from != null) {
|
||||
_from.updateMessageDeliveryStatus(_msgId, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request that a particular client authorize the Leases contained in the
|
||||
|
@ -25,8 +25,6 @@ class ClientWriterRunner implements Runnable {
|
||||
|
||||
private static final long MAX_WAIT = 5*1000;
|
||||
|
||||
/** notify this lock when there are messages to write */
|
||||
private Object _activityLock = new Object();
|
||||
/** lock on this when updating the class level data structs */
|
||||
private Object _dataLock = new Object();
|
||||
|
||||
@ -47,9 +45,7 @@ class ClientWriterRunner implements Runnable {
|
||||
synchronized (_dataLock) {
|
||||
_messagesToWrite.add(msg);
|
||||
_messagesToWriteTimes.add(new Long(_context.clock().now()));
|
||||
}
|
||||
synchronized (_activityLock) {
|
||||
_activityLock.notifyAll();
|
||||
_dataLock.notifyAll();
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("["+_id+"] addMessage completed for " + msg.getClass().getName());
|
||||
@ -60,8 +56,8 @@ class ClientWriterRunner implements Runnable {
|
||||
*
|
||||
*/
|
||||
public void stopWriting() {
|
||||
synchronized (_activityLock) {
|
||||
_activityLock.notifyAll();
|
||||
synchronized (_dataLock) {
|
||||
_dataLock.notifyAll();
|
||||
}
|
||||
}
|
||||
public void run() {
|
||||
@ -70,6 +66,9 @@ class ClientWriterRunner implements Runnable {
|
||||
List messageTimes = null;
|
||||
|
||||
synchronized (_dataLock) {
|
||||
if (_messagesToWrite.size() <= 0)
|
||||
try { _dataLock.wait(); } catch (InterruptedException ie) {}
|
||||
|
||||
if (_messagesToWrite.size() > 0) {
|
||||
messages = new ArrayList(_messagesToWrite.size());
|
||||
messageTimes = new ArrayList(_messagesToWriteTimes.size());
|
||||
@ -80,19 +79,13 @@ class ClientWriterRunner implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
if (messages == null) {
|
||||
try {
|
||||
synchronized (_activityLock) {
|
||||
_activityLock.wait();
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Interrupted while waiting for activity", ie);
|
||||
}
|
||||
} else {
|
||||
if (messages != null) {
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
I2CPMessage msg = (I2CPMessage)messages.get(i);
|
||||
Long when = (Long)messageTimes.get(i);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("["+_id+"] writeMessage before writing "
|
||||
+ msg.getClass().getName());
|
||||
_runner.writeMessage(msg);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("["+_id+"] writeMessage time since addMessage(): "
|
||||
|
@ -0,0 +1,490 @@
|
||||
package net.i2p.router.message;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import net.i2p.data.Certificate;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.Lease;
|
||||
import net.i2p.data.LeaseSet;
|
||||
import net.i2p.data.PublicKey;
|
||||
import net.i2p.data.SessionTag;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.TunnelId;
|
||||
|
||||
import net.i2p.data.i2cp.MessageId;
|
||||
|
||||
import net.i2p.data.i2np.DataMessage;
|
||||
import net.i2p.data.i2np.DeliveryStatusMessage;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
import net.i2p.data.i2np.GarlicMessage;
|
||||
import net.i2p.data.i2np.DeliveryInstructions;
|
||||
|
||||
import net.i2p.router.message.PayloadGarlicConfig;
|
||||
|
||||
import net.i2p.router.ClientMessage;
|
||||
import net.i2p.router.JobImpl;
|
||||
import net.i2p.router.ReplyJob;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.TunnelSelectionCriteria;
|
||||
import net.i2p.router.MessageSelector;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Send a client message out a random outbound tunnel and into a random inbound
|
||||
* tunnel on the target leaseSet. This also bundles the sender's leaseSet and
|
||||
* a DeliveryStatusMessage (for ACKing any sessionTags used in the garlic).
|
||||
*
|
||||
*/
|
||||
public class OutboundClientMessageOneShotJob extends JobImpl {
|
||||
private Log _log;
|
||||
private long _overallExpiration;
|
||||
private boolean _shouldBundle;
|
||||
private ClientMessage _clientMessage;
|
||||
private MessageId _clientMessageId;
|
||||
private int _clientMessageSize;
|
||||
private Destination _from;
|
||||
private Destination _to;
|
||||
/** target destination's leaseSet, if known */
|
||||
private LeaseSet _leaseSet;
|
||||
/** Actual lease the message is being routed through */
|
||||
private Lease _lease;
|
||||
private PayloadGarlicConfig _clove;
|
||||
private long _cloveId;
|
||||
private long _start;
|
||||
private boolean _finished;
|
||||
|
||||
/**
|
||||
* final timeout (in milliseconds) that the outbound message will fail in.
|
||||
* This can be overridden in the router.config or the client's session config
|
||||
* (the client's session config takes precedence)
|
||||
*/
|
||||
public final static String OVERALL_TIMEOUT_MS_PARAM = "clientMessageTimeout";
|
||||
private final static long OVERALL_TIMEOUT_MS_DEFAULT = 60*1000;
|
||||
|
||||
/** priority of messages, that might get honored some day... */
|
||||
private final static int SEND_PRIORITY = 500;
|
||||
|
||||
/**
|
||||
* If the client's config specifies shouldBundleReplyInfo=true, messages sent from
|
||||
* that client to any peers will probabalistically include the sending destination's
|
||||
* current LeaseSet (allowing the recipient to reply without having to do a full
|
||||
* netDb lookup). This should improve performance during the initial negotiations,
|
||||
* but is not necessary for communication that isn't bidirectional.
|
||||
*
|
||||
*/
|
||||
public static final String BUNDLE_REPLY_LEASESET = "shouldBundleReplyInfo";
|
||||
/**
|
||||
* Allow the override of the frequency of bundling the reply info in with a message.
|
||||
* The client app can specify bundleReplyInfoProbability=80 (for instance) and that
|
||||
* will cause the router to include the sender's leaseSet with 80% of the messages
|
||||
* sent to the peer.
|
||||
*
|
||||
*/
|
||||
public static final String BUNDLE_PROBABILITY = "bundleReplyInfoProbability";
|
||||
/**
|
||||
* How often do messages include the reply leaseSet (out of every 100 tries).
|
||||
* Including it each time is probably overkill, but who knows.
|
||||
*/
|
||||
private static final int BUNDLE_PROBABILITY_DEFAULT = 100;
|
||||
|
||||
/**
|
||||
* Send the sucker
|
||||
*/
|
||||
public OutboundClientMessageOneShotJob(RouterContext ctx, ClientMessage msg) {
|
||||
super(ctx);
|
||||
_log = ctx.logManager().getLog(OutboundClientMessageOneShotJob.class);
|
||||
|
||||
ctx.statManager().createFrequencyStat("client.sendMessageFailFrequency", "How often does a client fail to send a message?", "ClientMessages", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||
ctx.statManager().createRateStat("client.sendMessageSize", "How large are messages sent by the client?", "ClientMessages", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||
ctx.statManager().createRateStat("client.sendAckTime", "How long does it take to get an ACK back from a message?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||
ctx.statManager().createRateStat("client.timeoutCongestionTunnel", "How lagged our tunnels are when a send times out?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||
ctx.statManager().createRateStat("client.timeoutCongestionMessage", "How fast we process messages locally when a send times out?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||
ctx.statManager().createRateStat("client.timeoutCongestionInbound", "How much faster we are receiving data than our average bps when a send times out?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||
|
||||
long timeoutMs = OVERALL_TIMEOUT_MS_DEFAULT;
|
||||
_clientMessage = msg;
|
||||
_clientMessageId = msg.getMessageId();
|
||||
_clientMessageSize = msg.getPayload().getSize();
|
||||
_from = msg.getFromDestination();
|
||||
_to = msg.getDestination();
|
||||
|
||||
String param = msg.getSenderConfig().getOptions().getProperty(OVERALL_TIMEOUT_MS_PARAM);
|
||||
if (param == null)
|
||||
param = ctx.router().getConfigSetting(OVERALL_TIMEOUT_MS_PARAM);
|
||||
if (param != null) {
|
||||
try {
|
||||
timeoutMs = Long.parseLong(param);
|
||||
} catch (NumberFormatException nfe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Invalid client message timeout specified [" + param
|
||||
+ "], defaulting to " + OVERALL_TIMEOUT_MS_DEFAULT, nfe);
|
||||
timeoutMs = OVERALL_TIMEOUT_MS_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
_start = getContext().clock().now();
|
||||
_overallExpiration = timeoutMs + _start;
|
||||
_shouldBundle = getShouldBundle();
|
||||
_finished = false;
|
||||
}
|
||||
|
||||
public String getName() { return "Outbound client message"; }
|
||||
|
||||
public void runJob() {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": Send outbound client message job beginning");
|
||||
buildClove();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": Clove built");
|
||||
long timeoutMs = _overallExpiration - getContext().clock().now();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": Send outbound client message - sending off leaseSet lookup job");
|
||||
getContext().netDb().lookupLeaseSet(_to.calculateHash(), new SendJob(), new LookupLeaseSetFailedJob(), timeoutMs);
|
||||
}
|
||||
|
||||
private boolean getShouldBundle() {
|
||||
Properties opts = _clientMessage.getSenderConfig().getOptions();
|
||||
String wantBundle = opts.getProperty(BUNDLE_REPLY_LEASESET, "true");
|
||||
if ("true".equals(wantBundle)) {
|
||||
int probability = BUNDLE_PROBABILITY_DEFAULT;
|
||||
String str = opts.getProperty(BUNDLE_PROBABILITY);
|
||||
try {
|
||||
if (str != null)
|
||||
probability = Integer.parseInt(str);
|
||||
} catch (NumberFormatException nfe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getJobId() + ": Bundle leaseSet probability overridden incorrectly ["
|
||||
+ str + "]", nfe);
|
||||
}
|
||||
if (probability >= getContext().random().nextInt(100))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** send a message to a random lease */
|
||||
private class SendJob extends JobImpl {
|
||||
public SendJob() {
|
||||
super(OutboundClientMessageOneShotJob.this.getContext());
|
||||
}
|
||||
public String getName() { return "Send outbound client message through the lease"; }
|
||||
public void runJob() {
|
||||
boolean ok = getNextLease();
|
||||
if (ok)
|
||||
send();
|
||||
else
|
||||
dieFatal();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fetch the next lease that we should try sending through, randomly chosen
|
||||
* from within the sorted leaseSet (sorted by # of failures through each
|
||||
* lease).
|
||||
*
|
||||
*/
|
||||
private boolean getNextLease() {
|
||||
_leaseSet = getContext().netDb().lookupLeaseSetLocally(_to.calculateHash());
|
||||
if (_leaseSet == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getJobId() + ": Lookup locally didn't find the leaseSet");
|
||||
return false;
|
||||
}
|
||||
long now = getContext().clock().now();
|
||||
|
||||
// get the possible leases
|
||||
List leases = new ArrayList(_leaseSet.getLeaseCount());
|
||||
for (int i = 0; i < _leaseSet.getLeaseCount(); i++) {
|
||||
Lease lease = _leaseSet.getLease(i);
|
||||
if (lease.isExpired(Router.CLOCK_FUDGE_FACTOR)) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getJobId() + ": getNextLease() - expired lease! - " + lease);
|
||||
continue;
|
||||
} else {
|
||||
leases.add(lease);
|
||||
}
|
||||
}
|
||||
|
||||
if (leases.size() <= 0) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(getJobId() + ": No leases found from: " + _leaseSet);
|
||||
return false;
|
||||
}
|
||||
|
||||
// randomize the ordering (so leases with equal # of failures per next
|
||||
// sort are randomly ordered)
|
||||
Collections.shuffle(leases);
|
||||
|
||||
// ordered by lease number of failures
|
||||
TreeMap orderedLeases = new TreeMap();
|
||||
for (Iterator iter = leases.iterator(); iter.hasNext(); ) {
|
||||
Lease lease = (Lease)iter.next();
|
||||
long id = lease.getNumFailure();
|
||||
while (orderedLeases.containsKey(new Long(id)))
|
||||
id++;
|
||||
orderedLeases.put(new Long(id), lease);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": ranking lease we havent sent it down as " + id);
|
||||
}
|
||||
|
||||
_lease = (Lease)orderedLeases.get(orderedLeases.firstKey());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* we couldn't even find the leaseSet, but try again (or die
|
||||
* if we've already tried too hard)
|
||||
*
|
||||
*/
|
||||
private class LookupLeaseSetFailedJob extends JobImpl {
|
||||
public LookupLeaseSetFailedJob() {
|
||||
super(OutboundClientMessageOneShotJob.this.getContext());
|
||||
}
|
||||
public String getName() { return "Lookup for outbound client message failed"; }
|
||||
public void runJob() {
|
||||
dieFatal();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the message to the specified tunnel by creating a new garlic message containing
|
||||
* the (already created) payload clove as well as a new delivery status message. This garlic
|
||||
* message is sent out one of our tunnels, destined for the lease (tunnel+router) specified, and the delivery
|
||||
* status message is targetting one of our free inbound tunnels as well. We use a new
|
||||
* reply selector to keep an eye out for that delivery status message's token
|
||||
*
|
||||
*/
|
||||
private void send() {
|
||||
if (_finished) return;
|
||||
long token = getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE);
|
||||
PublicKey key = _leaseSet.getEncryptionKey();
|
||||
SessionKey sessKey = new SessionKey();
|
||||
Set tags = new HashSet();
|
||||
LeaseSet replyLeaseSet = null;
|
||||
if (_shouldBundle) {
|
||||
replyLeaseSet = getContext().netDb().lookupLeaseSetLocally(_from.calculateHash());
|
||||
}
|
||||
|
||||
GarlicMessage msg = OutboundClientMessageJobHelper.createGarlicMessage(getContext(), token,
|
||||
_overallExpiration, key,
|
||||
_clove,
|
||||
_to,
|
||||
sessKey, tags,
|
||||
true, replyLeaseSet);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": send() - token expected " + token);
|
||||
|
||||
SendSuccessJob onReply = new SendSuccessJob(sessKey, tags);
|
||||
SendTimeoutJob onFail = new SendTimeoutJob();
|
||||
ReplySelector selector = new ReplySelector(token);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": Placing GarlicMessage into the new tunnel message bound for "
|
||||
+ _lease.getTunnelId() + " on "
|
||||
+ _lease.getRouterIdentity().getHash().toBase64());
|
||||
|
||||
TunnelId outTunnelId = selectOutboundTunnel();
|
||||
if (outTunnelId != null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": Sending tunnel message out " + outTunnelId + " to "
|
||||
+ _lease.getTunnelId() + " on "
|
||||
+ _lease.getRouterIdentity().getHash().toBase64());
|
||||
SendTunnelMessageJob j = new SendTunnelMessageJob(getContext(), msg, outTunnelId,
|
||||
_lease.getRouterIdentity().getHash(),
|
||||
_lease.getTunnelId(), null, onReply,
|
||||
onFail, selector,
|
||||
_overallExpiration-getContext().clock().now(),
|
||||
SEND_PRIORITY);
|
||||
getContext().jobQueue().addJob(j);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error(getJobId() + ": Could not find any outbound tunnels to send the payload through... wtf?");
|
||||
dieFatal();
|
||||
}
|
||||
_clientMessage = null;
|
||||
_clove = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick an arbitrary outbound tunnel to send the message through, or null if
|
||||
* there aren't any around
|
||||
*
|
||||
*/
|
||||
private TunnelId selectOutboundTunnel() {
|
||||
TunnelSelectionCriteria crit = new TunnelSelectionCriteria();
|
||||
crit.setMaximumTunnelsRequired(1);
|
||||
crit.setMinimumTunnelsRequired(1);
|
||||
List tunnelIds = getContext().tunnelManager().selectOutboundTunnelIds(crit);
|
||||
if (tunnelIds.size() <= 0)
|
||||
return null;
|
||||
else
|
||||
return (TunnelId)tunnelIds.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* give up the ghost, this message just aint going through. tell the client to fuck off.
|
||||
*
|
||||
* this is safe to call multiple times (only tells the client once)
|
||||
*/
|
||||
private void dieFatal() {
|
||||
if (_finished) return;
|
||||
_finished = true;
|
||||
|
||||
long sendTime = getContext().clock().now() - _start;
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getJobId() + ": Failed to send the message " + _clientMessageId + " after "
|
||||
+ sendTime + "ms", new Exception("Message send failure"));
|
||||
|
||||
long messageDelay = getContext().throttle().getMessageDelay();
|
||||
long tunnelLag = getContext().throttle().getTunnelLag();
|
||||
long inboundDelta = (long)getContext().throttle().getInboundRateDelta();
|
||||
|
||||
getContext().statManager().addRateData("client.timeoutCongestionTunnel", tunnelLag, 1);
|
||||
getContext().statManager().addRateData("client.timeoutCongestionMessage", messageDelay, 1);
|
||||
getContext().statManager().addRateData("client.timeoutCongestionInbound", inboundDelta, 1);
|
||||
|
||||
getContext().messageHistory().sendPayloadMessage(_clientMessageId.getMessageId(), false, sendTime);
|
||||
getContext().clientManager().messageDeliveryStatusUpdate(_from, _clientMessageId, false);
|
||||
getContext().statManager().updateFrequency("client.sendMessageFailFrequency");
|
||||
_clientMessage = null;
|
||||
_clove = null;
|
||||
}
|
||||
|
||||
/** build the payload clove that will be used for all of the messages, placing the clove in the status structure */
|
||||
private void buildClove() {
|
||||
PayloadGarlicConfig clove = new PayloadGarlicConfig();
|
||||
|
||||
DeliveryInstructions instructions = new DeliveryInstructions();
|
||||
instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_DESTINATION);
|
||||
instructions.setDestination(_to.calculateHash());
|
||||
|
||||
instructions.setDelayRequested(false);
|
||||
instructions.setDelaySeconds(0);
|
||||
instructions.setEncrypted(false);
|
||||
|
||||
clove.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
|
||||
clove.setDeliveryInstructions(instructions);
|
||||
clove.setExpiration(_overallExpiration);
|
||||
clove.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||
|
||||
DataMessage msg = new DataMessage(getContext());
|
||||
msg.setData(_clientMessage.getPayload().getEncryptedData());
|
||||
|
||||
clove.setPayload(msg);
|
||||
clove.setRecipientPublicKey(null);
|
||||
clove.setRequestAck(false);
|
||||
|
||||
_clove = clove;
|
||||
_cloveId = _clove.getId();
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": Built payload clove with id " + clove.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep an eye out for any of the delivery status message tokens that have been
|
||||
* sent down the various tunnels to deliver this message
|
||||
*
|
||||
*/
|
||||
private class ReplySelector implements MessageSelector {
|
||||
private long _pendingToken;
|
||||
public ReplySelector(long token) {
|
||||
_pendingToken = token;
|
||||
}
|
||||
|
||||
public boolean continueMatching() { return false; }
|
||||
public long getExpiration() { return _overallExpiration; }
|
||||
|
||||
public boolean isMatch(I2NPMessage inMsg) {
|
||||
if (inMsg.getType() == DeliveryStatusMessage.MESSAGE_TYPE) {
|
||||
return _pendingToken == ((DeliveryStatusMessage)inMsg).getMessageId();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after we get a confirmation that the message was delivered safely
|
||||
* (hoo-ray!)
|
||||
*
|
||||
*/
|
||||
private class SendSuccessJob extends JobImpl implements ReplyJob {
|
||||
private SessionKey _key;
|
||||
private Set _tags;
|
||||
|
||||
/**
|
||||
* Create a new success job that will be fired when the message encrypted with
|
||||
* the given session key and bearing the specified tags are confirmed delivered.
|
||||
*
|
||||
*/
|
||||
public SendSuccessJob(SessionKey key, Set tags) {
|
||||
super(OutboundClientMessageOneShotJob.this.getContext());
|
||||
_key = key;
|
||||
_tags = tags;
|
||||
}
|
||||
|
||||
public String getName() { return "Send client message successful to a lease"; }
|
||||
public void runJob() {
|
||||
if (_finished) return;
|
||||
_finished = true;
|
||||
long sendTime = getContext().clock().now() - _start;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(OutboundClientMessageOneShotJob.this.getJobId()
|
||||
+ ": SUCCESS! msg " + _clientMessageId
|
||||
+ " sent after " + sendTime + "ms");
|
||||
|
||||
if ( (_key != null) && (_tags != null) && (_tags.size() > 0) ) {
|
||||
if (_leaseSet != null)
|
||||
getContext().sessionKeyManager().tagsDelivered(_leaseSet.getEncryptionKey(),
|
||||
_key, _tags);
|
||||
}
|
||||
|
||||
long dataMsgId = _cloveId;
|
||||
getContext().messageHistory().sendPayloadMessage(dataMsgId, true, sendTime);
|
||||
getContext().clientManager().messageDeliveryStatusUpdate(_from, _clientMessageId, true);
|
||||
_lease.setNumSuccess(_lease.getNumSuccess()+1);
|
||||
|
||||
getContext().statManager().addRateData("client.sendAckTime", sendTime, 0);
|
||||
getContext().statManager().addRateData("client.sendMessageSize", _clientMessageSize, sendTime);
|
||||
}
|
||||
|
||||
public void setMessage(I2NPMessage msg) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired after the basic timeout for sending through the given tunnel has been reached.
|
||||
* We'll accept successes later, but won't expect them
|
||||
*
|
||||
*/
|
||||
private class SendTimeoutJob extends JobImpl {
|
||||
public SendTimeoutJob() {
|
||||
super(OutboundClientMessageOneShotJob.this.getContext());
|
||||
}
|
||||
|
||||
public String getName() { return "Send client message timed out through a lease"; }
|
||||
public void runJob() {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(OutboundClientMessageOneShotJob.this.getJobId()
|
||||
+ ": Soft timeout through the lease " + _lease);
|
||||
|
||||
_lease.setNumFailure(_lease.getNumFailure()+1);
|
||||
dieFatal();
|
||||
}
|
||||
}
|
||||
}
|
@ -147,8 +147,10 @@ class PersistentDataStore extends TransientDataStore {
|
||||
}
|
||||
|
||||
private class ReadJob extends JobImpl {
|
||||
private boolean _alreadyWarned;
|
||||
public ReadJob() {
|
||||
super(PersistentDataStore.this._context);
|
||||
_alreadyWarned = false;
|
||||
}
|
||||
public String getName() { return "DB Read Job"; }
|
||||
public void runJob() {
|
||||
@ -158,6 +160,7 @@ class PersistentDataStore extends TransientDataStore {
|
||||
}
|
||||
|
||||
private void readFiles() {
|
||||
int routerCount = 0;
|
||||
try {
|
||||
File dbDir = getDbDir();
|
||||
File leaseSetFiles[] = dbDir.listFiles(LeaseSetFilter.getInstance());
|
||||
@ -170,6 +173,9 @@ class PersistentDataStore extends TransientDataStore {
|
||||
}
|
||||
File routerInfoFiles[] = dbDir.listFiles(RouterInfoFilter.getInstance());
|
||||
if (routerInfoFiles != null) {
|
||||
routerCount += routerInfoFiles.length;
|
||||
if (routerInfoFiles.length > 5)
|
||||
_alreadyWarned = false;
|
||||
for (int i = 0; i < routerInfoFiles.length; i++) {
|
||||
Hash key = getRouterInfoHash(routerInfoFiles[i].getName());
|
||||
if ( (key != null) && (!isKnown(key)) )
|
||||
@ -179,6 +185,11 @@ class PersistentDataStore extends TransientDataStore {
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error reading files in the db dir", ioe);
|
||||
}
|
||||
|
||||
if ( (routerCount <= 5) && (!_alreadyWarned) ) {
|
||||
_log.error("Very few routerInfo files remaining - please reseed");
|
||||
_alreadyWarned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,18 +45,25 @@ public class CapacityCalculator extends Calculator {
|
||||
private static long ESTIMATE_PERIOD = 60*60*1000;
|
||||
|
||||
public double calc(PeerProfile profile) {
|
||||
double capacity = 0;
|
||||
|
||||
RateStat acceptStat = profile.getTunnelCreateResponseTime();
|
||||
RateStat rejectStat = profile.getTunnelHistory().getRejectionRate();
|
||||
RateStat failedStat = profile.getTunnelHistory().getFailedRate();
|
||||
|
||||
capacity += estimatePartial(acceptStat, rejectStat, failedStat, 10*60*1000);
|
||||
capacity += estimatePartial(acceptStat, rejectStat, failedStat, 30*60*1000);
|
||||
capacity += estimatePartial(acceptStat, rejectStat, failedStat, 60*60*1000);
|
||||
capacity += estimatePartial(acceptStat, rejectStat, failedStat, 24*60*60*1000);
|
||||
double capacity10m = estimateCapacity(acceptStat, rejectStat, failedStat, 10*60*1000);
|
||||
double capacity30m = estimateCapacity(acceptStat, rejectStat, failedStat, 30*60*1000);
|
||||
double capacity60m = estimateCapacity(acceptStat, rejectStat, failedStat, 60*60*1000);
|
||||
double capacity1d = estimateCapacity(acceptStat, rejectStat, failedStat, 24*60*60*1000);
|
||||
|
||||
if (tooOld(profile))
|
||||
double capacity = capacity10m * periodWeight(10*60*1000) +
|
||||
capacity30m * periodWeight(30*60*1000) +
|
||||
capacity60m * periodWeight(60*60*1000) +
|
||||
capacity1d * periodWeight(24*60*60*1000);
|
||||
|
||||
// if we actively know they're bad, who cares if they used to be good?
|
||||
if (capacity10m <= 0)
|
||||
capacity = 0;
|
||||
|
||||
if (tooOld(profile))
|
||||
capacity = 1;
|
||||
|
||||
capacity += profile.getReliabilityBonus();
|
||||
@ -74,7 +81,7 @@ public class CapacityCalculator extends Calculator {
|
||||
return true;
|
||||
}
|
||||
|
||||
private double estimatePartial(RateStat acceptStat, RateStat rejectStat, RateStat failedStat, int period) {
|
||||
private double estimateCapacity(RateStat acceptStat, RateStat rejectStat, RateStat failedStat, int period) {
|
||||
Rate curAccepted = acceptStat.getRate(period);
|
||||
Rate curRejected = rejectStat.getRate(period);
|
||||
Rate curFailed = failedStat.getRate(period);
|
||||
@ -82,27 +89,29 @@ public class CapacityCalculator extends Calculator {
|
||||
long eventCount = 0;
|
||||
if (curAccepted != null)
|
||||
eventCount = curAccepted.getCurrentEventCount() + curAccepted.getLastEventCount();
|
||||
double stretch = ESTIMATE_PERIOD / period;
|
||||
double stretch = ((double)ESTIMATE_PERIOD) / period;
|
||||
double val = eventCount * stretch;
|
||||
long failed = 0;
|
||||
if (curFailed != null)
|
||||
failed = curFailed.getCurrentEventCount() + curFailed.getLastEventCount();
|
||||
if (failed > 0) {
|
||||
if ( (period == 10*60*1000) && (curFailed.getCurrentEventCount() > 0) )
|
||||
return 0.0d; // their tunnels have failed in the last 0-10 minutes
|
||||
else
|
||||
val -= failed * stretch;
|
||||
//if ( (period <= 10*60*1000) && (curFailed.getCurrentEventCount() > 0) )
|
||||
// return 0.0d; // their tunnels have failed in the last 0-10 minutes
|
||||
//else
|
||||
val -= failed * stretch;
|
||||
}
|
||||
|
||||
if ( (period == 10*60*1000) && (curRejected.getCurrentEventCount() + curRejected.getLastEventCount() > 0) )
|
||||
return 0.0d;
|
||||
else
|
||||
//if ( (period <= 10*60*1000) && (curRejected.getCurrentEventCount() + curRejected.getLastEventCount() > 0) ) {
|
||||
// //System.out.println("10m period has rejected " + (curRejected.getCurrentEventCount() + curRejected.getLastEventCount()) + " times");
|
||||
// return 0.0d;
|
||||
//} else
|
||||
val -= stretch * (curRejected.getCurrentEventCount() + curRejected.getLastEventCount());
|
||||
|
||||
val += GROWTH_FACTOR;
|
||||
|
||||
if (val >= 0) {
|
||||
return (val + GROWTH_FACTOR) * periodWeight(period);
|
||||
return val;
|
||||
} else {
|
||||
// failed too much, don't grow
|
||||
return 0.0d;
|
||||
}
|
||||
}
|
||||
|
@ -248,7 +248,7 @@ public class PeerProfile {
|
||||
if (_dbResponseTime == null)
|
||||
_dbResponseTime = new RateStat("dbResponseTime", "how long it takes to get a db response from the peer (in milliseconds)", group, new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000 } );
|
||||
if (_tunnelCreateResponseTime == null)
|
||||
_tunnelCreateResponseTime = new RateStat("tunnelCreateResponseTime", "how long it takes to get a tunnel create response from the peer (in milliseconds)", group, new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000 } );
|
||||
_tunnelCreateResponseTime = new RateStat("tunnelCreateResponseTime", "how long it takes to get a tunnel create response from the peer (in milliseconds)", group, new long[] { 10*60*1000l, 30*60*1000l, 60*60*1000l, 24*60*60*1000 } );
|
||||
if (_tunnelTestResponseTime == null)
|
||||
_tunnelTestResponseTime = new RateStat("tunnelTestResponseTime", "how long it takes to successfully test a tunnel this peer participates in (in milliseconds)", group, new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000 } );
|
||||
if (_commError == null)
|
||||
|
@ -109,7 +109,8 @@ class ProfileOrganizerRenderer {
|
||||
buf.append("<td align=\"right\">").append(prof.getIsFailing()).append("</td>");
|
||||
//buf.append("<td><a href=\"/profile/").append(prof.getPeer().toBase64().substring(0, 32)).append("\">profile.txt</a> ");
|
||||
//buf.append(" <a href=\"#").append(prof.getPeer().toBase64().substring(0, 32)).append("\">netDb</a></td>");
|
||||
buf.append("<td><a href=\"netdb.jsp#").append(peer.toBase64().substring(0,6)).append("\">netDb</a></td>\n");
|
||||
buf.append("<td nowrap><a href=\"netdb.jsp#").append(peer.toBase64().substring(0,6)).append("\">netDb</a>");
|
||||
buf.append("/<a href=\"dumpprofile.jsp?peer=").append(peer.toBase64().substring(0,6)).append("\">profile</a></td>\n");
|
||||
buf.append("</tr>");
|
||||
}
|
||||
buf.append("</table>");
|
||||
|
@ -70,9 +70,9 @@ class ProfilePersistenceHelper {
|
||||
groups = "not failing";
|
||||
} else {
|
||||
if (_context.profileOrganizer().isFast(profile.getPeer()))
|
||||
groups = "fast and reliable";
|
||||
groups = "fast and high capacity";
|
||||
else
|
||||
groups = "reliable";
|
||||
groups = "high capacity";
|
||||
|
||||
if (_context.profileOrganizer().isWellIntegrated(profile.getPeer()))
|
||||
groups = groups + ", well integrated";
|
||||
@ -85,6 +85,7 @@ class ProfilePersistenceHelper {
|
||||
buf.append("# as calculated by ").append(_us.toBase64()).append(NL);
|
||||
buf.append("#").append(NL);
|
||||
buf.append("# reliability: ").append(profile.getReliabilityValue()).append(NL);
|
||||
buf.append("# capacity: ").append(profile.getCapacityValue()).append(NL);
|
||||
buf.append("# integration: ").append(profile.getIntegrationValue()).append(NL);
|
||||
buf.append("# speedValue: ").append(profile.getSpeedValue()).append(NL);
|
||||
buf.append("#").append(NL);
|
||||
|
@ -1,87 +0,0 @@
|
||||
package net.i2p.router.startup;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
/**
|
||||
* Make sure that if there is a wrapper.config file, it includes
|
||||
* all of the jar files necessary for the current build.
|
||||
* HOLY CRAP THIS IS UGLY.
|
||||
*
|
||||
*/
|
||||
public class VerifyClasspath {
|
||||
private static final String NL = System.getProperty("line.separator");
|
||||
private static final Set _jars = new HashSet();
|
||||
|
||||
static {
|
||||
_jars.add("lib/ant.jar");
|
||||
_jars.add("lib/heartbeat.jar");
|
||||
_jars.add("lib/i2p.jar");
|
||||
_jars.add("lib/i2ptunnel.jar");
|
||||
_jars.add("lib/jasper-compiler.jar");
|
||||
_jars.add("lib/jasper-runtime.jar");
|
||||
_jars.add("lib/javax.servlet.jar");
|
||||
_jars.add("lib/jnet.jar");
|
||||
_jars.add("lib/mstreaming.jar");
|
||||
_jars.add("lib/netmonitor.jar");
|
||||
_jars.add("lib/org.mortbay.jetty.jar");
|
||||
_jars.add("lib/router.jar");
|
||||
_jars.add("lib/routerconsole.jar");
|
||||
_jars.add("lib/sam.jar");
|
||||
_jars.add("lib/wrapper.jar");
|
||||
_jars.add("lib/xercesImpl.jar");
|
||||
_jars.add("lib/xml-apis.jar");
|
||||
_jars.add("lib/jbigi.jar");
|
||||
_jars.add("lib/systray.jar");
|
||||
_jars.add("lib/systray4j.jar");
|
||||
_jars.add("lib/streaming.jar");
|
||||
}
|
||||
|
||||
/**
|
||||
* update the wrapper.config
|
||||
*
|
||||
* @return true if the classpath was updated and a restart is
|
||||
* required, false otherwise.
|
||||
*/
|
||||
public static boolean updateClasspath() {
|
||||
Properties p = new Properties();
|
||||
File configFile = new File("wrapper.config");
|
||||
Set needed = new HashSet(_jars);
|
||||
try {
|
||||
DataHelper.loadProps(p, configFile);
|
||||
Set toAdd = new HashSet();
|
||||
int entry = 1;
|
||||
while (true) {
|
||||
String value = p.getProperty("wrapper.java.classpath." + entry);
|
||||
if (value == null) break;
|
||||
needed.remove(value);
|
||||
entry++;
|
||||
}
|
||||
if (needed.size() <= 0) {
|
||||
// we have everything we need
|
||||
return false;
|
||||
} else {
|
||||
// add on some new lines
|
||||
FileWriter out = new FileWriter(configFile, true);
|
||||
out.write(NL + "# Adding new libs as required by the update" + NL);
|
||||
for (Iterator iter = needed.iterator(); iter.hasNext(); ) {
|
||||
String name = (String)iter.next();
|
||||
out.write("wrapper.java.classpath." + entry + "=" + name + NL);
|
||||
}
|
||||
out.close();
|
||||
return true;
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -364,5 +364,5 @@ public abstract class TransportImpl implements Transport {
|
||||
/** Make this stuff pretty (only used in the old console) */
|
||||
public String renderStatusHTML() { return null; }
|
||||
|
||||
protected RouterContext getContext() { return _context; }
|
||||
public RouterContext getContext() { return _context; }
|
||||
}
|
||||
|
@ -177,6 +177,8 @@ public class TransportManager implements TransportEventListener {
|
||||
}
|
||||
|
||||
private List orderBids(HashSet bids, OutNetMessage msg) {
|
||||
if (bids.size() <= 1)
|
||||
return new ArrayList(bids);
|
||||
// db messages should go as fast as possible, while the others
|
||||
// should use as little bandwidth as possible.
|
||||
I2NPMessage message = msg.getMessage();
|
||||
|
@ -142,6 +142,7 @@ public class ConnectionBuilder {
|
||||
con.setRemoteRouterIdentity(_actualPeer.getIdentity());
|
||||
con.setRemoteAddress(_remoteAddress);
|
||||
con.setAttemptedPeer(_target.getIdentity().getHash());
|
||||
con.setShownAddress(_localIP);
|
||||
if (_error == null) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Establishment successful! returning the con");
|
||||
@ -409,12 +410,13 @@ public class ConnectionBuilder {
|
||||
RouterInfo peer = new RouterInfo();
|
||||
peer.readBytes(_rawIn);
|
||||
int status = (int)_rawIn.read() & 0xFF;
|
||||
boolean ok = validateStatus(status);
|
||||
if (!ok) return false;
|
||||
|
||||
Properties props = DataHelper.readProperties(_rawIn);
|
||||
// ignore these now
|
||||
|
||||
boolean ok = validateStatus(status, props);
|
||||
if (!ok) return false;
|
||||
|
||||
Hash readHash = new Hash();
|
||||
readHash.readBytes(_rawIn);
|
||||
|
||||
@ -563,12 +565,13 @@ public class ConnectionBuilder {
|
||||
RouterInfo peer = new RouterInfo();
|
||||
peer.readBytes(_rawIn);
|
||||
int status = (int)_rawIn.read() & 0xFF;
|
||||
boolean ok = validateStatus(status);
|
||||
if (!ok) return false;
|
||||
|
||||
Properties props = DataHelper.readProperties(_rawIn);
|
||||
// ignore these now
|
||||
|
||||
boolean ok = validateStatus(status, props);
|
||||
if (!ok) return false;
|
||||
|
||||
Signature sig = new Signature();
|
||||
sig.readBytes(_rawIn);
|
||||
|
||||
@ -619,7 +622,7 @@ public class ConnectionBuilder {
|
||||
*
|
||||
* @return true if ok, false if fail()ed
|
||||
*/
|
||||
private boolean validateStatus(int status) {
|
||||
private boolean validateStatus(int status, Properties props) {
|
||||
switch (status) {
|
||||
case -1: // EOF
|
||||
fail("Error reading the status from "
|
||||
@ -635,7 +638,7 @@ public class ConnectionBuilder {
|
||||
case ConnectionHandler.STATUS_SKEWED:
|
||||
fail("According to "
|
||||
+ _target.getIdentity().calculateHash().toBase64().substring(0,6)
|
||||
+ ", our clock is off");
|
||||
+ ", our clock is off (they think it is " + props.getProperty("SKEW") + ")");
|
||||
return false;
|
||||
case ConnectionHandler.STATUS_SIGNATURE_FAILED: // (only for new sessions)
|
||||
fail("Signature failure talking to "
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user