forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p.zzz.test' (head 128a31611abc6a88e58133f3bf6a577fe6dd5b1c)
to branch 'i2p.i2p.zzz.test4' (head fa9a871892517271eb2531b433fe80a2a713be9c)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project basedir="." default="all" name="i2ptunnel">
|
||||
<target name="all" depends="clean, build" />
|
||||
<target name="build" depends="builddep, jar" />
|
||||
<target name="build" depends="builddep, jar, war" />
|
||||
<target name="builddep">
|
||||
<ant dir="../../ministreaming/java/" target="build" />
|
||||
<ant dir="../../jetty/" target="build" />
|
||||
@@ -34,6 +34,10 @@
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<!-- TODO: Move the web classes from the jar to the war - they are not part of the API
|
||||
- This will require sponge to rewrite some seedless stuff that uses it.
|
||||
-->
|
||||
<target name="jar" depends="builddep, compile">
|
||||
<jar destfile="./build/i2ptunnel.jar" basedir="./build/obj" includes="**/*.class">
|
||||
<manifest>
|
||||
@@ -41,8 +45,6 @@
|
||||
<attribute name="Class-Path" value="i2p.jar mstreaming.jar" />
|
||||
</manifest>
|
||||
</jar>
|
||||
<ant target="bundle" />
|
||||
<ant target="war" />
|
||||
</target>
|
||||
|
||||
<target name="bundle" depends="compile, precompilejsp">
|
||||
@@ -77,13 +79,13 @@
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="war" depends="precompilejsp">
|
||||
<target name="war" depends="precompilejsp, bundle">
|
||||
<war destfile="build/i2ptunnel.war" webxml="../jsp/web-out.xml"
|
||||
basedir="../jsp/" excludes="web.xml, **/*.java, *.jsp">
|
||||
</war>
|
||||
</target>
|
||||
|
||||
<target name="precompilejsp" unless="precompilejsp.uptodate">
|
||||
<target name="precompilejsp" depends="jar" unless="precompilejsp.uptodate">
|
||||
<delete dir="../jsp/WEB-INF/" />
|
||||
<delete file="../jsp/web-fragment.xml" />
|
||||
<delete file="../jsp/web-out.xml" />
|
||||
|
||||
@@ -24,6 +24,9 @@ import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* This does the transparent gzip decompression on the client side.
|
||||
* Extended in I2PTunnelHTTPServer to do the compression on the server side.
|
||||
*
|
||||
* Simple stream for delivering an HTTP response to
|
||||
* the client, trivially filtered to make sure "Connection: close"
|
||||
* is always in the response. Perhaps add transparent handling of the
|
||||
@@ -33,29 +36,27 @@ import net.i2p.util.Log;
|
||||
*
|
||||
*/
|
||||
class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private ByteCache _cache;
|
||||
private final I2PAppContext _context;
|
||||
private final Log _log;
|
||||
protected ByteArray _headerBuffer;
|
||||
private boolean _headerWritten;
|
||||
private byte _buf1[];
|
||||
private final byte _buf1[];
|
||||
protected boolean _gzip;
|
||||
private long _dataWritten;
|
||||
private InternalGZIPInputStream _in;
|
||||
private static final int CACHE_SIZE = 8*1024;
|
||||
private static final ByteCache _cache = ByteCache.getInstance(8, CACHE_SIZE);
|
||||
// OOM DOS prevention
|
||||
private static final int MAX_HEADER_SIZE = 64*1024;
|
||||
|
||||
public HTTPResponseOutputStream(OutputStream raw) {
|
||||
super(raw);
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
_context.statManager().createRateStat("i2ptunnel.httpCompressionRatio", "ratio of compressed size to decompressed size after transfer", "I2PTunnel", new long[] { 60*1000, 30*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.httpCompressed", "compressed size transferred", "I2PTunnel", new long[] { 60*1000, 30*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.httpExpanded", "size transferred after expansion", "I2PTunnel", new long[] { 60*1000, 30*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.httpCompressionRatio", "ratio of compressed size to decompressed size after transfer", "I2PTunnel", new long[] { 60*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.httpCompressed", "compressed size transferred", "I2PTunnel", new long[] { 60*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.httpExpanded", "size transferred after expansion", "I2PTunnel", new long[] { 60*60*1000 });
|
||||
_log = _context.logManager().getLog(getClass());
|
||||
_cache = ByteCache.getInstance(8, CACHE_SIZE);
|
||||
_headerBuffer = _cache.acquire();
|
||||
_headerWritten = false;
|
||||
_gzip = false;
|
||||
_dataWritten = 0;
|
||||
_buf1 = new byte[1];
|
||||
}
|
||||
|
||||
@@ -96,14 +97,20 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
}
|
||||
}
|
||||
|
||||
/** grow (and free) the buffer as necessary */
|
||||
private void ensureCapacity() {
|
||||
/**
|
||||
* grow (and free) the buffer as necessary
|
||||
* @throws IOException if the headers are too big
|
||||
*/
|
||||
private void ensureCapacity() throws IOException {
|
||||
if (_headerBuffer.getValid() >= MAX_HEADER_SIZE)
|
||||
throw new IOException("Max header size exceeded: " + MAX_HEADER_SIZE);
|
||||
if (_headerBuffer.getValid() + 1 >= _headerBuffer.getData().length) {
|
||||
int newSize = (int)(_headerBuffer.getData().length * 1.5);
|
||||
ByteArray newBuf = new ByteArray(new byte[newSize]);
|
||||
System.arraycopy(_headerBuffer.getData(), 0, newBuf.getData(), 0, _headerBuffer.getValid());
|
||||
newBuf.setValid(_headerBuffer.getValid());
|
||||
newBuf.setOffset(0);
|
||||
// if we changed the ByteArray size, don't put it back in the cache
|
||||
if (_headerBuffer.getData().length == CACHE_SIZE)
|
||||
_cache.release(_headerBuffer);
|
||||
_headerBuffer = newBuf;
|
||||
@@ -219,7 +226,7 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
//out.flush();
|
||||
PipedInputStream pi = new PipedInputStream();
|
||||
PipedOutputStream po = new PipedOutputStream(pi);
|
||||
new I2PAppThread(new Pusher(pi, out), "HTTP decompresser").start();
|
||||
new I2PAppThread(new Pusher(pi, out), "HTTP decompressor").start();
|
||||
out = po;
|
||||
}
|
||||
|
||||
@@ -231,13 +238,13 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
_out = out;
|
||||
}
|
||||
public void run() {
|
||||
OutputStream to = null;
|
||||
_in = null;
|
||||
long start = System.currentTimeMillis();
|
||||
long written = 0;
|
||||
ByteArray ba = null;
|
||||
try {
|
||||
_in = new InternalGZIPInputStream(_inRaw);
|
||||
byte buf[] = new byte[8192];
|
||||
ba = _cache.acquire();
|
||||
byte buf[] = ba.getData();
|
||||
int read = -1;
|
||||
while ( (read = _in.read(buf)) != -1) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -251,6 +258,8 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error decompressing: " + written + ", " + (_in != null ? _in.getTotalRead() + "/" + _in.getTotalExpanded() : ""), ioe);
|
||||
} catch (OutOfMemoryError oom) {
|
||||
_log.error("OOM in HTTP Decompressor", oom);
|
||||
} finally {
|
||||
if (_log.shouldLog(Log.WARN) && (_in != null))
|
||||
_log.warn("After decompression, written=" + written +
|
||||
@@ -259,23 +268,26 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
+ ", expanded=" + _in.getTotalExpanded() + ", remaining=" + _in.getRemaining()
|
||||
+ ", finished=" + _in.getFinished()
|
||||
: ""));
|
||||
if (ba != null)
|
||||
_cache.release(ba);
|
||||
if (_out != null) try {
|
||||
_out.close();
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
|
||||
double compressed = (_in != null ? _in.getTotalRead() : 0);
|
||||
double expanded = (_in != null ? _in.getTotalExpanded() : 0);
|
||||
double ratio = 0;
|
||||
if (expanded > 0)
|
||||
ratio = compressed/expanded;
|
||||
|
||||
_context.statManager().addRateData("i2ptunnel.httpCompressionRatio", (int)(100d*ratio), end-start);
|
||||
_context.statManager().addRateData("i2ptunnel.httpCompressed", (long)compressed, end-start);
|
||||
_context.statManager().addRateData("i2ptunnel.httpExpanded", (long)expanded, end-start);
|
||||
if (compressed > 0 && expanded > 0) {
|
||||
// only update the stats if we did something
|
||||
double ratio = compressed/expanded;
|
||||
_context.statManager().addRateData("i2ptunnel.httpCompressionRatio", (int)(100d*ratio), 0);
|
||||
_context.statManager().addRateData("i2ptunnel.httpCompressed", (long)compressed, 0);
|
||||
_context.statManager().addRateData("i2ptunnel.httpExpanded", (long)expanded, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** just a wrapper to provide stats for debugging */
|
||||
private static class InternalGZIPInputStream extends GZIPInputStream {
|
||||
public InternalGZIPInputStream(InputStream in) throws IOException {
|
||||
super(in);
|
||||
@@ -294,6 +306,12 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* From Inflater javadoc:
|
||||
* Returns the total number of bytes remaining in the input buffer. This can be used to find out
|
||||
* what bytes still remain in the input buffer after decompression has finished.
|
||||
*/
|
||||
public long getRemaining() {
|
||||
try {
|
||||
return super.inf.getRemaining();
|
||||
|
||||
@@ -69,6 +69,9 @@ import net.i2p.util.EventDispatcher;
|
||||
import net.i2p.util.EventDispatcherImpl;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Todo: Most events are not listened to elsewhere, so error propagation is poor
|
||||
*/
|
||||
public class I2PTunnel implements Logging, EventDispatcher {
|
||||
private Log _log;
|
||||
private EventDispatcherImpl _event;
|
||||
@@ -163,7 +166,12 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
System.out.print("I2PTunnel>");
|
||||
String cmd = r.readLine();
|
||||
if (cmd == null) break;
|
||||
runCommand(cmd, this);
|
||||
if (cmd.length() <= 0) continue;
|
||||
try {
|
||||
runCommand(cmd, this);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
@@ -180,6 +188,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
/** @return non-null */
|
||||
List<I2PSession> getSessions() {
|
||||
synchronized (_sessions) {
|
||||
return new ArrayList(_sessions);
|
||||
@@ -351,6 +360,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
*
|
||||
* @param args {hostname, portNumber, privKeyFilename}
|
||||
* @param l logger to receive events and output
|
||||
* @throws IllegalArgumentException on config problem
|
||||
*/
|
||||
public void runServer(String args[], Logging l) {
|
||||
if (args.length == 3) {
|
||||
@@ -363,7 +373,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("unknown host");
|
||||
_log.error(getPrefix() + "Error resolving " + args[0], uhe);
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
throw new IllegalArgumentException(getPrefix() + "Error resolving " + args[0] + uhe.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -372,17 +382,18 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[1], nfe);
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
}
|
||||
if (portNum <= 0)
|
||||
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[1]);
|
||||
|
||||
privKeyFile = new File(args[2]);
|
||||
if (!privKeyFile.isAbsolute())
|
||||
privKeyFile = new File(_context.getConfigDir(), args[2]);
|
||||
if (!privKeyFile.canRead()) {
|
||||
l.log("private key file does not exist");
|
||||
l.log(getPrefix() + "Private key file does not exist or is not readable: " + args[2]);
|
||||
_log.error(getPrefix() + "Private key file does not exist or is not readable: " + args[2]);
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
throw new IllegalArgumentException(getPrefix() + "Cannot open private key file " + args[2]);
|
||||
}
|
||||
I2PTunnelServer serv = new I2PTunnelServer(serverHost, portNum, privKeyFile, args[2], l, (EventDispatcher) this, this);
|
||||
serv.setReadTimeout(readTimeout);
|
||||
@@ -400,6 +411,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
/**
|
||||
* Same args as runServer
|
||||
* (we should stop duplicating all this code...)
|
||||
* @throws IllegalArgumentException on config problem
|
||||
*/
|
||||
public void runIrcServer(String args[], Logging l) {
|
||||
if (args.length == 3) {
|
||||
@@ -412,7 +424,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("unknown host");
|
||||
_log.error(getPrefix() + "Error resolving " + args[0], uhe);
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
throw new IllegalArgumentException(getPrefix() + "Error resolving " + args[0] + uhe.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -421,17 +433,18 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[1], nfe);
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
}
|
||||
if (portNum <= 0)
|
||||
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[1]);
|
||||
|
||||
privKeyFile = new File(args[2]);
|
||||
if (!privKeyFile.isAbsolute())
|
||||
privKeyFile = new File(_context.getConfigDir(), args[2]);
|
||||
if (!privKeyFile.canRead()) {
|
||||
l.log("private key file does not exist");
|
||||
l.log(getPrefix() + "Private key file does not exist or is not readable: " + args[2]);
|
||||
_log.error(getPrefix() + "Private key file does not exist or is not readable: " + args[2]);
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
throw new IllegalArgumentException(getPrefix() + "Cannot open private key file " + args[2]);
|
||||
}
|
||||
I2PTunnelServer serv = new I2PTunnelIRCServer(serverHost, portNum, privKeyFile, args[2], l, (EventDispatcher) this, this);
|
||||
serv.setReadTimeout(readTimeout);
|
||||
@@ -457,6 +470,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
*
|
||||
* @param args {hostname, portNumber, spoofedHost, privKeyFilename}
|
||||
* @param l logger to receive events and output
|
||||
* @throws IllegalArgumentException on config problem
|
||||
*/
|
||||
public void runHttpServer(String args[], Logging l) {
|
||||
if (args.length == 4) {
|
||||
@@ -469,7 +483,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("unknown host");
|
||||
_log.error(getPrefix() + "Error resolving " + args[0], uhe);
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
throw new IllegalArgumentException(getPrefix() + "Error resolving " + args[0] + uhe.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -478,8 +492,9 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[1], nfe);
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
}
|
||||
if (portNum <= 0)
|
||||
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[1]);
|
||||
|
||||
String spoofedHost = args[2];
|
||||
|
||||
@@ -487,10 +502,10 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
if (!privKeyFile.isAbsolute())
|
||||
privKeyFile = new File(_context.getConfigDir(), args[3]);
|
||||
if (!privKeyFile.canRead()) {
|
||||
l.log("private key file does not exist");
|
||||
l.log(getPrefix() + "Private key file does not exist or is not readable: " + args[3]);
|
||||
_log.error(getPrefix() + "Private key file does not exist or is not readable: " + args[3]);
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
throw new IllegalArgumentException(getPrefix() + "Cannot open private key file " + args[3]);
|
||||
}
|
||||
I2PTunnelHTTPServer serv = new I2PTunnelHTTPServer(serverHost, portNum, privKeyFile, args[3], spoofedHost, l, (EventDispatcher) this, this);
|
||||
serv.setReadTimeout(readTimeout);
|
||||
@@ -519,6 +534,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
*
|
||||
* @param args {hostname, portNumber, proxyPortNumber, spoofedHost, privKeyFilename}
|
||||
* @param l logger to receive events and output
|
||||
* @throws IllegalArgumentException on config problem
|
||||
*/
|
||||
public void runHttpBidirServer(String args[], Logging l) {
|
||||
if (args.length == 5) {
|
||||
@@ -532,7 +548,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("unknown host");
|
||||
_log.error(getPrefix() + "Error resolving " + args[0], uhe);
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
throw new IllegalArgumentException(getPrefix() + "Error resolving " + args[0] + uhe.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -541,7 +557,6 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[1], nfe);
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -550,8 +565,11 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[2], nfe);
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
}
|
||||
if (portNum <= 0)
|
||||
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[1]);
|
||||
if (port2Num <= 0)
|
||||
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[2]);
|
||||
|
||||
String spoofedHost = args[3];
|
||||
|
||||
@@ -559,10 +577,10 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
if (!privKeyFile.isAbsolute())
|
||||
privKeyFile = new File(_context.getConfigDir(), args[4]);
|
||||
if (!privKeyFile.canRead()) {
|
||||
l.log("private key file does not exist");
|
||||
l.log(getPrefix() + "Private key file does not exist or is not readable: " + args[4]);
|
||||
_log.error(getPrefix() + "Private key file does not exist or is not readable: " + args[4]);
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
throw new IllegalArgumentException(getPrefix() + "Cannot open private key file " + args[4]);
|
||||
}
|
||||
|
||||
I2PTunnelHTTPBidirServer serv = new I2PTunnelHTTPBidirServer(serverHost, portNum, port2Num, privKeyFile, args[3], spoofedHost, l, (EventDispatcher) this, this);
|
||||
@@ -585,12 +603,16 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
* Run the server pointing at the host and port specified using the private i2p
|
||||
* destination loaded from the given base64 stream. <p />
|
||||
*
|
||||
* Deprecated? Why run a server with a private destination?
|
||||
* Not available from the war GUI
|
||||
*
|
||||
* Sets the event "serverTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error)
|
||||
* Also sets the event "openServerResult" = "ok" or "error" (displaying "Ready!" on the logger after
|
||||
* 'ok'). So, success = serverTaskId != -1 and openServerResult = ok.
|
||||
*
|
||||
* @param args {hostname, portNumber, privKeyBase64}
|
||||
* @param l logger to receive events and output
|
||||
* @throws IllegalArgumentException on config problem
|
||||
*/
|
||||
public void runTextServer(String args[], Logging l) {
|
||||
if (args.length == 3) {
|
||||
@@ -602,7 +624,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("unknown host");
|
||||
_log.error(getPrefix() + "Error resolving " + args[0], uhe);
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
throw new IllegalArgumentException(getPrefix() + "Error resolving " + args[0] + uhe.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -611,8 +633,9 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[1], nfe);
|
||||
notifyEvent("serverTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
}
|
||||
if (portNum <= 0)
|
||||
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[1]);
|
||||
|
||||
I2PTunnelServer serv = new I2PTunnelServer(serverHost, portNum, args[2], l, (EventDispatcher) this, this);
|
||||
serv.setReadTimeout(readTimeout);
|
||||
@@ -638,6 +661,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
*
|
||||
* @param args {portNumber, destinationBase64 or "file:filename"[, sharedClient [, privKeyFile]]}
|
||||
* @param l logger to receive events and output
|
||||
* @throws IllegalArgumentException on config problem
|
||||
*/
|
||||
public void runClient(String args[], Logging l) {
|
||||
boolean isShared = true;
|
||||
@@ -651,8 +675,10 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
|
||||
notifyEvent("clientTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
}
|
||||
if (portNum <= 0)
|
||||
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[0]);
|
||||
|
||||
I2PTunnelTask task;
|
||||
ownDest = !isShared;
|
||||
try {
|
||||
@@ -666,6 +692,11 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
_log.error(getPrefix() + "Invalid I2PTunnel config to create a client [" + host + ":"+ port + "]", iae);
|
||||
l.log("Invalid I2PTunnel configuration [" + host + ":" + port + "]");
|
||||
notifyEvent("clientTaskId", Integer.valueOf(-1));
|
||||
// Since nothing listens to TaskID events, use this to propagate the error to TunnelController
|
||||
// Otherwise, the tunnel stays up even though the port is down
|
||||
// This doesn't work for CLI though... and the tunnel doesn't close itself after error,
|
||||
// so this probably leaves the tunnel open if called from the CLI
|
||||
throw iae;
|
||||
}
|
||||
} else {
|
||||
l.log("client <port> <pubkey>[,<pubkey>]|file:<pubkeyfile>[ <sharedClient>] [<privKeyFile>]");
|
||||
@@ -687,6 +718,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
*
|
||||
* @param args {portNumber[, sharedClient][, proxy to be used for the WWW]}
|
||||
* @param l logger to receive events and output
|
||||
* @throws IllegalArgumentException on config problem
|
||||
*/
|
||||
public void runHttpClient(String args[], Logging l) {
|
||||
if (args.length >= 1 && args.length <= 3) {
|
||||
@@ -697,8 +729,9 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
|
||||
notifyEvent("httpclientTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
}
|
||||
if (clientPort <= 0)
|
||||
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[0]);
|
||||
|
||||
String proxy = "";
|
||||
boolean isShared = true;
|
||||
@@ -733,6 +766,11 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
_log.error(getPrefix() + "Invalid I2PTunnel config to create an httpclient [" + host + ":"+ clientPort + "]", iae);
|
||||
l.log("Invalid I2PTunnel configuration [" + host + ":" + clientPort + "]");
|
||||
notifyEvent("httpclientTaskId", Integer.valueOf(-1));
|
||||
// Since nothing listens to TaskID events, use this to propagate the error to TunnelController
|
||||
// Otherwise, the tunnel stays up even though the port is down
|
||||
// This doesn't work for CLI though... and the tunnel doesn't close itself after error,
|
||||
// so this probably leaves the tunnel open if called from the CLI
|
||||
throw iae;
|
||||
}
|
||||
} else {
|
||||
l.log("httpclient <port> [<sharedClient>] [<proxy>]");
|
||||
@@ -749,6 +787,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
*
|
||||
* @param args {portNumber[, sharedClient][, proxy to be used for the WWW]}
|
||||
* @param l logger to receive events and output
|
||||
* @throws IllegalArgumentException on config problem
|
||||
*/
|
||||
public void runConnectClient(String args[], Logging l) {
|
||||
if (args.length >= 1 && args.length <= 3) {
|
||||
@@ -757,8 +796,9 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
_port = Integer.parseInt(args[0]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
|
||||
return;
|
||||
}
|
||||
if (_port <= 0)
|
||||
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[0]);
|
||||
|
||||
String proxy = "";
|
||||
boolean isShared = true;
|
||||
@@ -789,7 +829,12 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
task = new I2PTunnelConnectClient(_port, l, ownDest, proxy, (EventDispatcher) this, this);
|
||||
addtask(task);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
_log.error(getPrefix() + "Invalid I2PTunnel config to create an httpclient [" + host + ":"+ _port + "]", iae);
|
||||
_log.error(getPrefix() + "Invalid I2PTunnel config to create a connect client [" + host + ":"+ _port + "]", iae);
|
||||
// Since nothing listens to TaskID events, use this to propagate the error to TunnelController
|
||||
// Otherwise, the tunnel stays up even though the port is down
|
||||
// This doesn't work for CLI though... and the tunnel doesn't close itself after error,
|
||||
// so this probably leaves the tunnel open if called from the CLI
|
||||
throw iae;
|
||||
}
|
||||
} else {
|
||||
l.log("connectclient <port> [<sharedClient>] [<proxy>]");
|
||||
@@ -809,6 +854,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
*
|
||||
* @param args {portNumber,destinationBase64 or "file:filename" [, sharedClient [, privKeyFile]]}
|
||||
* @param l logger to receive events and output
|
||||
* @throws IllegalArgumentException on config problem
|
||||
*/
|
||||
public void runIrcClient(String args[], Logging l) {
|
||||
if (args.length >= 2) {
|
||||
@@ -819,8 +865,9 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
|
||||
notifyEvent("ircclientTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
}
|
||||
if (_port <= 0)
|
||||
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[0]);
|
||||
|
||||
boolean isShared = true;
|
||||
if (args.length > 2) {
|
||||
@@ -848,6 +895,11 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
_log.error(getPrefix() + "Invalid I2PTunnel config to create an ircclient [" + host + ":"+ _port + "]", iae);
|
||||
l.log("Invalid I2PTunnel configuration [" + host + ":" + _port + "]");
|
||||
notifyEvent("ircclientTaskId", Integer.valueOf(-1));
|
||||
// Since nothing listens to TaskID events, use this to propagate the error to TunnelController
|
||||
// Otherwise, the tunnel stays up even though the port is down
|
||||
// This doesn't work for CLI though... and the tunnel doesn't close itself after error,
|
||||
// so this probably leaves the tunnel open if called from the CLI
|
||||
throw iae;
|
||||
}
|
||||
} else {
|
||||
l.log("ircclient <port> [<sharedClient> [<privKeyFile>]]");
|
||||
@@ -867,6 +919,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
*
|
||||
* @param args {portNumber [, sharedClient]}
|
||||
* @param l logger to receive events and output
|
||||
* @throws IllegalArgumentException on config problem
|
||||
*/
|
||||
public void runSOCKSTunnel(String args[], Logging l) {
|
||||
if (args.length >= 1 && args.length <= 2) {
|
||||
@@ -877,8 +930,9 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
|
||||
notifyEvent("sockstunnelTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
}
|
||||
if (_port <= 0)
|
||||
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[0]);
|
||||
|
||||
boolean isShared = false;
|
||||
if (args.length > 1)
|
||||
@@ -900,6 +954,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
/**
|
||||
* Run an SOCKS IRC tunnel on the given port number
|
||||
* @param args {portNumber [, sharedClient]} or (portNumber, ignored (false), privKeyFile)
|
||||
* @throws IllegalArgumentException on config problem
|
||||
* @since 0.7.12
|
||||
*/
|
||||
public void runSOCKSIRCTunnel(String args[], Logging l) {
|
||||
@@ -911,8 +966,9 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
|
||||
notifyEvent("sockstunnelTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
}
|
||||
if (_port <= 0)
|
||||
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[0]);
|
||||
|
||||
boolean isShared = false;
|
||||
if (args.length == 2)
|
||||
@@ -938,6 +994,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
*
|
||||
* @param args {targethost, targetport, destinationString}
|
||||
* @param l logger to receive events and output
|
||||
* @throws IllegalArgumentException on config problem
|
||||
*/
|
||||
public void runStreamrClient(String args[], Logging l) {
|
||||
if (args.length == 3) {
|
||||
@@ -958,8 +1015,9 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
|
||||
notifyEvent("streamrtunnelTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
}
|
||||
if (_port <= 0)
|
||||
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[0]);
|
||||
|
||||
StreamrConsumer task = new StreamrConsumer(_host, _port, args[2], l, (EventDispatcher) this, this);
|
||||
task.startRunning();
|
||||
@@ -977,6 +1035,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
*
|
||||
* @param args {port, privkeyfile}
|
||||
* @param l logger to receive events and output
|
||||
* @throws IllegalArgumentException on config problem
|
||||
*/
|
||||
public void runStreamrServer(String args[], Logging l) {
|
||||
if (args.length == 2) {
|
||||
@@ -987,8 +1046,9 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
|
||||
notifyEvent("streamrtunnelTaskId", Integer.valueOf(-1));
|
||||
return;
|
||||
}
|
||||
if (_port <= 0)
|
||||
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[0]);
|
||||
|
||||
File privKeyFile = new File(args[1]);
|
||||
if (!privKeyFile.isAbsolute())
|
||||
@@ -1403,6 +1463,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
/**
|
||||
* Create a new destination, storing the destination and its private keys where
|
||||
* instructed
|
||||
* Deprecated - only used by CLI
|
||||
*
|
||||
* @param writeTo location to store the private keys
|
||||
* @param pubDest location to store the destination
|
||||
@@ -1427,6 +1488,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
|
||||
/**
|
||||
* Read in the given destination, display it, and write it to the given location
|
||||
* Deprecated - only used by CLI
|
||||
*
|
||||
* @param readFrom stream to read the destination from
|
||||
* @param pubDest stream to write the destination to
|
||||
@@ -1448,6 +1510,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
|
||||
/**
|
||||
* Write out the destination to the stream
|
||||
* Deprecated - only used by CLI
|
||||
*
|
||||
* @param d Destination to write
|
||||
* @param o stream to write the destination to
|
||||
@@ -1465,6 +1528,9 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
* also supported, where filename is a file that either contains a
|
||||
* binary Destination structure or the Base64 encoding of that
|
||||
* structure.
|
||||
*
|
||||
* Since file:<filename> isn't really used, this method is deprecated,
|
||||
* just call context.namingService.lookup() directly.
|
||||
*/
|
||||
public static Destination destFromName(String name) throws DataFormatException {
|
||||
|
||||
@@ -1529,7 +1595,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
private String getPrefix() { return '[' + _tunnelId + "]: "; }
|
||||
private String getPrefix() { return "[" + _tunnelId + "]: "; }
|
||||
|
||||
public I2PAppContext getContext() { return _context; }
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
|
||||
private static final Log _log = new Log(I2PTunnelClient.class);
|
||||
|
||||
/** list of Destination objects that we point at */
|
||||
protected List dests;
|
||||
protected List<Destination> dests;
|
||||
private static final long DEFAULT_READ_TIMEOUT = 5*60*1000; // -1
|
||||
protected long readTimeout = DEFAULT_READ_TIMEOUT;
|
||||
|
||||
@@ -55,9 +55,20 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
|
||||
}
|
||||
|
||||
if (dests.isEmpty()) {
|
||||
l.log("No target destinations found");
|
||||
l.log("No valid target destinations found");
|
||||
notifyEvent("openClientResult", "error");
|
||||
return;
|
||||
// Nothing is listening for the above event, so it's useless
|
||||
// Maybe figure out where to put a waitEventValue("openClientResult") ??
|
||||
// In the meantime, let's do this the easy way
|
||||
// Note that b32 dests will often not be resolvable at instantiation time;
|
||||
// a delayed resolution system would be even better.
|
||||
|
||||
// Don't close() here, because it does a removeSession() and then
|
||||
// TunnelController can't acquire() it to release() it.
|
||||
//close(true);
|
||||
// Unfortunately, super() built the whole tunnel before we get here.
|
||||
throw new IllegalArgumentException("No valid target destinations found");
|
||||
//return;
|
||||
}
|
||||
|
||||
setName(getLocalPort() + " -> " + destinations);
|
||||
@@ -98,8 +109,8 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
|
||||
return null;
|
||||
}
|
||||
if (size == 1) // skip the rand in the most common case
|
||||
return (Destination)dests.get(0);
|
||||
return dests.get(0);
|
||||
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
|
||||
return (Destination)dests.get(index);
|
||||
return dests.get(index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -583,6 +583,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
}
|
||||
|
||||
public boolean close(boolean forced) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("close() called: forced = " + forced + " open = " + open + " sockMgr = " + sockMgr);
|
||||
if (!open) return true;
|
||||
// FIXME: here we might have to wait quite a long time if
|
||||
// there is a connection attempt atm. But without waiting we
|
||||
|
||||
@@ -26,6 +26,7 @@ import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Supports the following:
|
||||
*<pre>
|
||||
* (where protocol is generally HTTP/1.1 but is ignored)
|
||||
* (where host is one of:
|
||||
* example.i2p
|
||||
@@ -39,16 +40,19 @@ import net.i2p.util.Log;
|
||||
* CONNECT host protocol
|
||||
* CONNECT host:port
|
||||
* CONNECT host:port protocol (this is the standard)
|
||||
*</pre>
|
||||
*
|
||||
* Additional lines after the CONNECT line but before the blank line are ignored and stripped.
|
||||
* The CONNECT line is removed for .i2p accesses
|
||||
* but passed along for outproxy accesses.
|
||||
*
|
||||
* Ref:
|
||||
*<pre>
|
||||
* INTERNET-DRAFT Ari Luotonen
|
||||
* Expires: September 26, 1997 Netscape Communications Corporation
|
||||
* <draft-luotonen-ssl-tunneling-03.txt> March 26, 1997
|
||||
* Tunneling SSL Through a WWW Proxy
|
||||
*</pre>
|
||||
*
|
||||
* @author zzz a stripped-down I2PTunnelHTTPClient
|
||||
*/
|
||||
|
||||
@@ -33,7 +33,6 @@ import net.i2p.data.Destination;
|
||||
import net.i2p.util.EventDispatcher;
|
||||
import net.i2p.util.FileUtil;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import net.i2p.util.Translate;
|
||||
|
||||
/**
|
||||
@@ -646,11 +645,11 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
if (!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_USER_AGENT)).booleanValue()) {
|
||||
// let's not advertise to external sites that we are from I2P
|
||||
if (usingWWWProxy)
|
||||
newRequest.append("User-Agent: Wget/1.11.4\r\n");
|
||||
newRequest.append("User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6\r\n");
|
||||
else
|
||||
newRequest.append("User-Agent: MYOB/6.66 (AN/ON)\r\n");
|
||||
}
|
||||
newRequest.append("Connection: close\r\n\r\n");
|
||||
newRequest.append("Connection: close\r\n\r\n");
|
||||
break;
|
||||
} else {
|
||||
newRequest.append(line).append("\r\n"); // HTTP spec
|
||||
@@ -772,11 +771,12 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
_s = s;
|
||||
}
|
||||
String readLine(String method) throws IOException {
|
||||
if (method == null || "POST".equals(method))
|
||||
// Use unbuffered until we can find a BufferedReader that limits line length
|
||||
//if (method == null || "POST".equals(method))
|
||||
return DataHelper.readLine(_s);
|
||||
if (_br == null)
|
||||
_br = new BufferedReader(new InputStreamReader(_s, "ISO-8859-1"));
|
||||
return _br.readLine();
|
||||
//if (_br == null)
|
||||
// _br = new BufferedReader(new InputStreamReader(_s, "ISO-8859-1"));
|
||||
//return _br.readLine();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: " + (afterSocket-afterAccept) + "]");
|
||||
}
|
||||
|
||||
private class CompressedRequestor implements Runnable {
|
||||
private static class CompressedRequestor implements Runnable {
|
||||
private Socket _webserver;
|
||||
private I2PSocket _browser;
|
||||
private String _headers;
|
||||
@@ -214,7 +214,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
}
|
||||
}
|
||||
}
|
||||
private class Sender implements Runnable {
|
||||
|
||||
private static class Sender implements Runnable {
|
||||
private OutputStream _out;
|
||||
private InputStream _in;
|
||||
private String _name;
|
||||
@@ -248,7 +249,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
}
|
||||
}
|
||||
}
|
||||
private class CompressedResponseOutputStream extends HTTPResponseOutputStream {
|
||||
|
||||
private static class CompressedResponseOutputStream extends HTTPResponseOutputStream {
|
||||
private InternalGZIPOutputStream _gzipOut;
|
||||
public CompressedResponseOutputStream(OutputStream o) {
|
||||
super(o);
|
||||
@@ -287,7 +289,9 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
private class InternalGZIPOutputStream extends GZIPOutputStream {
|
||||
|
||||
/** just a wrapper to provide stats for debugging */
|
||||
private static class InternalGZIPOutputStream extends GZIPOutputStream {
|
||||
public InternalGZIPOutputStream(OutputStream target) throws IOException {
|
||||
super(target);
|
||||
}
|
||||
@@ -309,7 +313,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
}
|
||||
}
|
||||
|
||||
private String formatHeaders(Properties headers, StringBuilder command) {
|
||||
private static String formatHeaders(Properties headers, StringBuilder command) {
|
||||
StringBuilder buf = new StringBuilder(command.length() + headers.size() * 64);
|
||||
buf.append(command.toString().trim()).append("\r\n");
|
||||
for (Iterator iter = headers.keySet().iterator(); iter.hasNext(); ) {
|
||||
|
||||
@@ -17,6 +17,9 @@ import net.i2p.util.EventDispatcher;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Todo: Can we extend I2PTunnelClient instead and remove some duplicated code?
|
||||
*/
|
||||
public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable {
|
||||
|
||||
private static final Log _log = new Log(I2PTunnelIRCClient.class);
|
||||
@@ -25,7 +28,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
private static volatile long __clientId = 0;
|
||||
|
||||
/** list of Destination objects that we point at */
|
||||
protected List dests;
|
||||
protected List<Destination> dests;
|
||||
private static final long DEFAULT_READ_TIMEOUT = 5*60*1000; // -1
|
||||
protected long readTimeout = DEFAULT_READ_TIMEOUT;
|
||||
|
||||
@@ -47,7 +50,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
"IRCHandler " + (++__clientId), tunnel, pkf);
|
||||
|
||||
StringTokenizer tok = new StringTokenizer(destinations, ", ");
|
||||
dests = new ArrayList(1);
|
||||
dests = new ArrayList(2);
|
||||
while (tok.hasMoreTokens()) {
|
||||
String destination = tok.nextToken();
|
||||
try {
|
||||
@@ -64,7 +67,18 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
if (dests.isEmpty()) {
|
||||
l.log("No target destinations found");
|
||||
notifyEvent("openClientResult", "error");
|
||||
return;
|
||||
// Nothing is listening for the above event, so it's useless
|
||||
// Maybe figure out where to put a waitEventValue("openClientResult") ??
|
||||
// In the meantime, let's do this the easy way
|
||||
// Note that b32 dests will often not be resolvable at instantiation time;
|
||||
// a delayed resolution system would be even better.
|
||||
|
||||
// Don't close() here, because it does a removeSession() and then
|
||||
// TunnelController can't acquire() it to release() it.
|
||||
//close(true);
|
||||
// Unfortunately, super() built the whole tunnel before we get here.
|
||||
throw new IllegalArgumentException("No valid target destinations found");
|
||||
//return;
|
||||
}
|
||||
|
||||
setName(getLocalPort() + " -> IRCClient");
|
||||
@@ -109,9 +123,9 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
return null;
|
||||
}
|
||||
if (size == 1) // skip the rand in the most common case
|
||||
return (Destination)dests.get(0);
|
||||
return dests.get(0);
|
||||
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
|
||||
return (Destination)dests.get(index);
|
||||
return dests.get(index);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
@@ -130,6 +144,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
|
||||
public void run() {
|
||||
// Todo: Don't use BufferedReader - IRC spec limits line length to 512 but...
|
||||
BufferedReader in;
|
||||
OutputStream output;
|
||||
try {
|
||||
@@ -204,6 +219,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
|
||||
public void run() {
|
||||
// Todo: Don't use BufferedReader - IRC spec limits line length to 512 but...
|
||||
BufferedReader in;
|
||||
OutputStream output;
|
||||
try {
|
||||
@@ -371,7 +387,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
// "QUIT", // replace with a filtered QUIT to hide client quit messages
|
||||
"SILENCE",
|
||||
"MAP", // seems safe enough, the ircd should protect themselves though
|
||||
"PART",
|
||||
// "PART", // replace with filtered PART to hide client part messages
|
||||
"OPER",
|
||||
// "PONG", // replaced with a filtered PING/PONG since some clients send the server IP (thanks aardvax!)
|
||||
// "PING",
|
||||
@@ -475,6 +491,11 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ("PART".equals(command)) {
|
||||
// hide client message
|
||||
return "PART " + field[1] + " :leaving";
|
||||
}
|
||||
|
||||
if ("QUIT".equals(command)) {
|
||||
return "QUIT :leaving";
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
|
||||
}
|
||||
|
||||
/** keep reading until we see USER or SERVER */
|
||||
private String filterRegistration(InputStream in, String newHostname) throws IOException {
|
||||
private static String filterRegistration(InputStream in, String newHostname) throws IOException {
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
int lineCount = 0;
|
||||
|
||||
|
||||
@@ -127,6 +127,8 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
|
||||
// this does not increment totalSent
|
||||
i2pout.write(initialI2PData);
|
||||
// do NOT flush here, it will block and then onTimeout.run() won't happen on fail.
|
||||
// But if we don't flush, then we have to wait for the connectDelay timer to fire
|
||||
// in i2p socket? To be researched and/or fixed.
|
||||
//i2pout.flush();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ public class TunnelController implements Logging {
|
||||
private Log _log;
|
||||
private Properties _config;
|
||||
private I2PTunnel _tunnel;
|
||||
private List _messages;
|
||||
private List _sessions;
|
||||
private List<String> _messages;
|
||||
private List<I2PSession> _sessions;
|
||||
private boolean _running;
|
||||
private boolean _starting;
|
||||
|
||||
@@ -47,6 +47,7 @@ public class TunnelController implements Logging {
|
||||
public TunnelController(Properties config, String prefix) {
|
||||
this(config, prefix, true);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param createKey for servers, whether we want to create a brand new destination
|
||||
@@ -59,17 +60,21 @@ public class TunnelController implements Logging {
|
||||
setConfig(config, prefix);
|
||||
_messages = new ArrayList(4);
|
||||
_running = false;
|
||||
boolean keyOK = true;
|
||||
if (createKey && (getType().endsWith("server") || getPersistentClientKey()))
|
||||
createPrivateKey();
|
||||
_starting = getStartOnLoad();
|
||||
keyOK = createPrivateKey();
|
||||
_starting = keyOK && getStartOnLoad();
|
||||
}
|
||||
|
||||
private void createPrivateKey() {
|
||||
/**
|
||||
* @return success
|
||||
*/
|
||||
private boolean createPrivateKey() {
|
||||
I2PClient client = I2PClientFactory.createClient();
|
||||
String filename = getPrivKeyFile();
|
||||
if ( (filename == null) || (filename.trim().length() <= 0) ) {
|
||||
log("No filename specified for the private key");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
File keyFile = new File(getPrivKeyFile());
|
||||
@@ -77,7 +82,7 @@ public class TunnelController implements Logging {
|
||||
keyFile = new File(I2PAppContext.getGlobalContext().getConfigDir(), getPrivKeyFile());
|
||||
if (keyFile.exists()) {
|
||||
//log("Not overwriting existing private keys in " + keyFile.getAbsolutePath());
|
||||
return;
|
||||
return true;
|
||||
} else {
|
||||
File parent = keyFile.getParentFile();
|
||||
if ( (parent != null) && (!parent.exists()) )
|
||||
@@ -95,13 +100,16 @@ public class TunnelController implements Logging {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error creating new destination", ie);
|
||||
log("Error creating new destination: " + ie.getMessage());
|
||||
return false;
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error creating writing the destination to " + keyFile.getAbsolutePath(), ioe);
|
||||
log("Error writing the keys to " + keyFile.getAbsolutePath());
|
||||
return false;
|
||||
} finally {
|
||||
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void startTunnelBackground() {
|
||||
@@ -121,9 +129,16 @@ public class TunnelController implements Logging {
|
||||
} catch (Exception e) {
|
||||
_log.error("Error starting up the tunnel", e);
|
||||
log("Error starting up the tunnel - " + e.getMessage());
|
||||
// if we don't acquire() then the release() in stopTunnel() won't work
|
||||
acquire();
|
||||
stopTunnel();
|
||||
}
|
||||
_starting = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws IllegalArgumentException via methods in I2PTunnel
|
||||
*/
|
||||
private void doStartTunnel() {
|
||||
if (_running) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
@@ -257,15 +272,18 @@ public class TunnelController implements Logging {
|
||||
* closed by some other tunnels
|
||||
*/
|
||||
private void acquire() {
|
||||
List sessions = _tunnel.getSessions();
|
||||
if (sessions != null) {
|
||||
List<I2PSession> sessions = _tunnel.getSessions();
|
||||
if (!sessions.isEmpty()) {
|
||||
for (int i = 0; i < sessions.size(); i++) {
|
||||
I2PSession session = (I2PSession)sessions.get(i);
|
||||
I2PSession session = sessions.get(i);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Acquiring session " + session);
|
||||
TunnelControllerGroup.getInstance().acquire(this, session);
|
||||
}
|
||||
_sessions = sessions;
|
||||
} else {
|
||||
_log.error("No sessions to acquire?");
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("No sessions to acquire? for " + getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,13 +292,16 @@ public class TunnelController implements Logging {
|
||||
* no other tunnels are using them, close them.
|
||||
*/
|
||||
private void release() {
|
||||
if (_sessions != null) {
|
||||
if (_sessions != null && !_sessions.isEmpty()) {
|
||||
for (int i = 0; i < _sessions.size(); i++) {
|
||||
I2PSession s = (I2PSession)_sessions.get(i);
|
||||
I2PSession s = _sessions.get(i);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Releasing session " + s);
|
||||
TunnelControllerGroup.getInstance().release(this, s);
|
||||
}
|
||||
} else {
|
||||
_log.error("No sessions to release?");
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("No sessions to release? for " + getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,7 +392,7 @@ public class TunnelController implements Logging {
|
||||
_tunnel.port = "7654";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void stopTunnel() {
|
||||
_tunnel.runClose(new String[] { "forced", "all" }, this);
|
||||
release();
|
||||
@@ -440,9 +461,9 @@ public class TunnelController implements Logging {
|
||||
public boolean getPersistentClientKey() { return Boolean.valueOf(_config.getProperty("option.persistentClientKey")).booleanValue(); }
|
||||
public String getMyDestination() {
|
||||
if (_tunnel != null) {
|
||||
List sessions = _tunnel.getSessions();
|
||||
List<I2PSession> sessions = _tunnel.getSessions();
|
||||
for (int i = 0; i < sessions.size(); i++) {
|
||||
I2PSession session = (I2PSession)sessions.get(i);
|
||||
I2PSession session = sessions.get(i);
|
||||
Destination dest = session.getMyDestination();
|
||||
if (dest != null)
|
||||
return dest.toBase64();
|
||||
@@ -453,9 +474,9 @@ public class TunnelController implements Logging {
|
||||
|
||||
public String getMyDestHashBase32() {
|
||||
if (_tunnel != null) {
|
||||
List sessions = _tunnel.getSessions();
|
||||
List<I2PSession> sessions = _tunnel.getSessions();
|
||||
for (int i = 0; i < sessions.size(); i++) {
|
||||
I2PSession session = (I2PSession)sessions.get(i);
|
||||
I2PSession session = sessions.get(i);
|
||||
Destination dest = session.getMyDestination();
|
||||
if (dest != null)
|
||||
return Base32.encode(dest.calculateHash().getData());
|
||||
@@ -557,9 +578,9 @@ public class TunnelController implements Logging {
|
||||
if ( (opts != null) && (opts.length() > 0) )
|
||||
buf.append("Network options: ").append(opts).append("<br />\n");
|
||||
if (_running) {
|
||||
List sessions = _tunnel.getSessions();
|
||||
List<I2PSession> sessions = _tunnel.getSessions();
|
||||
for (int i = 0; i < sessions.size(); i++) {
|
||||
I2PSession session = (I2PSession)sessions.get(i);
|
||||
I2PSession session = sessions.get(i);
|
||||
Destination dest = session.getMyDestination();
|
||||
if (dest != null) {
|
||||
buf.append("Destination hash: ").append(dest.calculateHash().toBase64()).append("<br />\n");
|
||||
@@ -598,7 +619,7 @@ public class TunnelController implements Logging {
|
||||
*
|
||||
* @return list of messages pulled off (each is a String, earliest first)
|
||||
*/
|
||||
public List clearMessages() {
|
||||
public List<String> clearMessages() {
|
||||
List rv = null;
|
||||
synchronized (this) {
|
||||
rv = new ArrayList(_messages);
|
||||
|
||||
@@ -26,13 +26,14 @@ import net.i2p.util.SecureFileOutputStream;
|
||||
* Coordinate a set of tunnels within the JVM, loading and storing their config
|
||||
* to disk, and building new ones as requested.
|
||||
*
|
||||
* Warning - this is a singleton. Todo: fix
|
||||
*/
|
||||
public class TunnelControllerGroup {
|
||||
private Log _log;
|
||||
private final Log _log;
|
||||
private static TunnelControllerGroup _instance;
|
||||
static final String DEFAULT_CONFIG_FILE = "i2ptunnel.config";
|
||||
|
||||
private List _controllers;
|
||||
private final List<TunnelController> _controllers;
|
||||
private String _configFile = DEFAULT_CONFIG_FILE;
|
||||
|
||||
/**
|
||||
@@ -41,7 +42,7 @@ public class TunnelControllerGroup {
|
||||
* no more tunnels are using it)
|
||||
*
|
||||
*/
|
||||
private final Map _sessions;
|
||||
private final Map<I2PSession, Set<TunnelController>> _sessions;
|
||||
|
||||
public static TunnelControllerGroup getInstance() {
|
||||
synchronized (TunnelControllerGroup.class) {
|
||||
@@ -105,7 +106,7 @@ public class TunnelControllerGroup {
|
||||
private class StartControllers implements Runnable {
|
||||
public void run() {
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
TunnelController controller = _controllers.get(i);
|
||||
if (controller.getStartOnLoad())
|
||||
controller.startTunnel();
|
||||
}
|
||||
@@ -142,10 +143,10 @@ public class TunnelControllerGroup {
|
||||
*
|
||||
* @return list of messages from the controller as it is stopped
|
||||
*/
|
||||
public List removeController(TunnelController controller) {
|
||||
public List<String> removeController(TunnelController controller) {
|
||||
if (controller == null) return new ArrayList();
|
||||
controller.stopTunnel();
|
||||
List msgs = controller.clearMessages();
|
||||
List<String> msgs = controller.clearMessages();
|
||||
_controllers.remove(controller);
|
||||
msgs.add("Tunnel " + controller.getName() + " removed");
|
||||
return msgs;
|
||||
@@ -156,10 +157,10 @@ public class TunnelControllerGroup {
|
||||
*
|
||||
* @return list of messages the tunnels generate when stopped
|
||||
*/
|
||||
public List stopAllControllers() {
|
||||
List msgs = new ArrayList();
|
||||
public List<String> stopAllControllers() {
|
||||
List<String> msgs = new ArrayList();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
TunnelController controller = _controllers.get(i);
|
||||
controller.stopTunnel();
|
||||
msgs.addAll(controller.clearMessages());
|
||||
}
|
||||
@@ -173,10 +174,10 @@ public class TunnelControllerGroup {
|
||||
*
|
||||
* @return list of messages the tunnels generate when started
|
||||
*/
|
||||
public List startAllControllers() {
|
||||
List msgs = new ArrayList();
|
||||
public List<String> startAllControllers() {
|
||||
List<String> msgs = new ArrayList();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
TunnelController controller = _controllers.get(i);
|
||||
controller.startTunnelBackground();
|
||||
msgs.addAll(controller.clearMessages());
|
||||
}
|
||||
@@ -191,10 +192,10 @@ public class TunnelControllerGroup {
|
||||
*
|
||||
* @return list of messages the tunnels generate when restarted
|
||||
*/
|
||||
public List restartAllControllers() {
|
||||
List msgs = new ArrayList();
|
||||
public List<String> restartAllControllers() {
|
||||
List<String> msgs = new ArrayList();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
TunnelController controller = _controllers.get(i);
|
||||
controller.restartTunnel();
|
||||
msgs.addAll(controller.clearMessages());
|
||||
}
|
||||
@@ -208,10 +209,10 @@ public class TunnelControllerGroup {
|
||||
*
|
||||
* @return list of messages the tunnels have generated
|
||||
*/
|
||||
public List clearAllMessages() {
|
||||
List msgs = new ArrayList();
|
||||
public List<String> clearAllMessages() {
|
||||
List<String> msgs = new ArrayList();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
TunnelController controller = _controllers.get(i);
|
||||
msgs.addAll(controller.clearMessages());
|
||||
}
|
||||
return msgs;
|
||||
@@ -241,7 +242,7 @@ public class TunnelControllerGroup {
|
||||
|
||||
TreeMap map = new TreeMap();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
TunnelController controller = _controllers.get(i);
|
||||
Properties cur = controller.getConfig("tunnel." + i + ".");
|
||||
map.putAll(cur);
|
||||
}
|
||||
@@ -297,7 +298,7 @@ public class TunnelControllerGroup {
|
||||
*
|
||||
* @return list of TunnelController objects
|
||||
*/
|
||||
public List getControllers() { return _controllers; }
|
||||
public List<TunnelController> getControllers() { return _controllers; }
|
||||
|
||||
|
||||
/**
|
||||
@@ -307,7 +308,7 @@ public class TunnelControllerGroup {
|
||||
*/
|
||||
void acquire(TunnelController controller, I2PSession session) {
|
||||
synchronized (_sessions) {
|
||||
Set owners = (Set)_sessions.get(session);
|
||||
Set<TunnelController> owners = _sessions.get(session);
|
||||
if (owners == null) {
|
||||
owners = new HashSet(1);
|
||||
_sessions.put(session, owners);
|
||||
@@ -327,7 +328,7 @@ public class TunnelControllerGroup {
|
||||
void release(TunnelController controller, I2PSession session) {
|
||||
boolean shouldClose = false;
|
||||
synchronized (_sessions) {
|
||||
Set owners = (Set)_sessions.get(session);
|
||||
Set<TunnelController> owners = _sessions.get(session);
|
||||
if (owners != null) {
|
||||
owners.remove(controller);
|
||||
if (owners.isEmpty()) {
|
||||
|
||||
@@ -13,11 +13,20 @@ import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.PrivateKeyFile;
|
||||
import net.i2p.data.Signature;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.i2ptunnel.TunnelController;
|
||||
import net.i2p.i2ptunnel.TunnelControllerGroup;
|
||||
|
||||
/**
|
||||
* Ugly little accessor for the edit page
|
||||
*
|
||||
* Warning - This class is not part of the i2ptunnel API, and at some point
|
||||
* it will be moved from the jar to the war.
|
||||
* Usage by classes outside of i2ptunnel.war is deprecated.
|
||||
*/
|
||||
public class EditBean extends IndexBean {
|
||||
public EditBean() { super(); }
|
||||
@@ -64,6 +73,31 @@ public class EditBean extends IndexBean {
|
||||
return "i2ptunnel" + tunnel + "-privKeys.dat";
|
||||
}
|
||||
|
||||
public String getNameSignature(int tunnel) {
|
||||
String spoof = getSpoofedHost(tunnel);
|
||||
if (spoof.length() <= 0)
|
||||
return "";
|
||||
TunnelController tun = getController(tunnel);
|
||||
if (tun == null)
|
||||
return "";
|
||||
String keyFile = tun.getPrivKeyFile();
|
||||
if (keyFile != null && keyFile.trim().length() > 0) {
|
||||
PrivateKeyFile pkf = new PrivateKeyFile(keyFile);
|
||||
try {
|
||||
Destination d = pkf.getDestination();
|
||||
if (d == null)
|
||||
return "";
|
||||
SigningPrivateKey privKey = pkf.getSigningPrivKey();
|
||||
if (privKey == null)
|
||||
return "";
|
||||
//System.err.println("Signing " + spoof + " with " + Base64.encode(privKey.getData()));
|
||||
Signature sig = _context.dsa().sign(spoof.getBytes("UTF-8"), privKey);
|
||||
return Base64.encode(sig.getData());
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public boolean startAutomatically(int tunnel) {
|
||||
TunnelController tun = getController(tunnel);
|
||||
if (tun != null)
|
||||
|
||||
@@ -9,6 +9,7 @@ package net.i2p.i2ptunnel.web;
|
||||
*/
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -31,6 +32,9 @@ import net.i2p.util.Log;
|
||||
/**
|
||||
* Simple accessor for exposing tunnel info, but also an ugly form handler
|
||||
*
|
||||
* Warning - This class is not part of the i2ptunnel API, and at some point
|
||||
* it will be moved from the jar to the war.
|
||||
* Usage by classes outside of i2ptunnel.war is deprecated.
|
||||
*/
|
||||
public class IndexBean {
|
||||
protected I2PAppContext _context;
|
||||
@@ -38,10 +42,10 @@ public class IndexBean {
|
||||
protected TunnelControllerGroup _group;
|
||||
private String _action;
|
||||
private int _tunnel;
|
||||
private long _prevNonce;
|
||||
private long _prevNonce2;
|
||||
private long _curNonce;
|
||||
private long _nextNonce;
|
||||
//private long _prevNonce;
|
||||
//private long _prevNonce2;
|
||||
private String _curNonce;
|
||||
//private long _nextNonce;
|
||||
|
||||
private String _type;
|
||||
private String _name;
|
||||
@@ -82,10 +86,14 @@ public class IndexBean {
|
||||
/** deprecated unimplemented, now using routerconsole realm */
|
||||
//public static final String PROP_TUNNEL_PASSPHRASE = "i2ptunnel.passphrase";
|
||||
public static final String PROP_TUNNEL_PASSPHRASE = "consolePassword";
|
||||
static final String PROP_NONCE = IndexBean.class.getName() + ".nonce";
|
||||
static final String PROP_NONCE_OLD = PROP_NONCE + '2';
|
||||
//static final String PROP_NONCE = IndexBean.class.getName() + ".nonce";
|
||||
//static final String PROP_NONCE_OLD = PROP_NONCE + '2';
|
||||
/** 3 wasn't enough for some browsers. They are reloading the page for some reason - maybe HEAD? @since 0.8.1 */
|
||||
private static final int MAX_NONCES = 5;
|
||||
/** store nonces in a static FIFO instead of in System Properties @since 0.8.1 */
|
||||
private static final List<String> _nonces = new ArrayList(MAX_NONCES + 1);
|
||||
|
||||
static final String CLIENT_NICKNAME = "shared clients";
|
||||
|
||||
public static final String PROP_THEME_NAME = "routerconsole.theme";
|
||||
public static final String DEFAULT_THEME = "light";
|
||||
public static final String PROP_CSS_DISABLED = "routerconsole.css.disabled";
|
||||
@@ -95,34 +103,39 @@ public class IndexBean {
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
_log = _context.logManager().getLog(IndexBean.class);
|
||||
_group = TunnelControllerGroup.getInstance();
|
||||
_action = null;
|
||||
_tunnel = -1;
|
||||
_curNonce = -1;
|
||||
_prevNonce = -1;
|
||||
_prevNonce2 = -1;
|
||||
try {
|
||||
String nonce2 = System.getProperty(PROP_NONCE_OLD);
|
||||
if (nonce2 != null)
|
||||
_prevNonce2 = Long.parseLong(nonce2);
|
||||
String nonce = System.getProperty(PROP_NONCE);
|
||||
if (nonce != null) {
|
||||
_prevNonce = Long.parseLong(nonce);
|
||||
System.setProperty(PROP_NONCE_OLD, nonce);
|
||||
}
|
||||
} catch (NumberFormatException nfe) {}
|
||||
_nextNonce = _context.random().nextLong();
|
||||
System.setProperty(PROP_NONCE, Long.toString(_nextNonce));
|
||||
_curNonce = "-1";
|
||||
addNonce();
|
||||
_booleanOptions = new ConcurrentHashSet(4);
|
||||
_otherOptions = new ConcurrentHashMap(4);
|
||||
}
|
||||
|
||||
public long getNextNonce() { return _nextNonce; }
|
||||
public static String getNextNonce() {
|
||||
synchronized (_nonces) {
|
||||
return _nonces.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void setNonce(String nonce) {
|
||||
if ( (nonce == null) || (nonce.trim().length() <= 0) ) return;
|
||||
try {
|
||||
_curNonce = Long.parseLong(nonce);
|
||||
} catch (NumberFormatException nfe) {
|
||||
_curNonce = -1;
|
||||
_curNonce = nonce;
|
||||
}
|
||||
|
||||
/** add a random nonce to the head of the queue @since 0.8.1 */
|
||||
private void addNonce() {
|
||||
String nextNonce = Long.toString(_context.random().nextLong());
|
||||
synchronized (_nonces) {
|
||||
_nonces.add(0, nextNonce);
|
||||
int sz = _nonces.size();
|
||||
if (sz > MAX_NONCES)
|
||||
_nonces.remove(sz - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/** do we know this nonce? @since 0.8.1 */
|
||||
private static boolean haveNonce(String nonce) {
|
||||
synchronized (_nonces) {
|
||||
return _nonces.contains(nonce);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +165,7 @@ public class IndexBean {
|
||||
private String processAction() {
|
||||
if ( (_action == null) || (_action.trim().length() <= 0) || ("Cancel".equals(_action)))
|
||||
return "";
|
||||
if ( (_prevNonce != _curNonce) && (_prevNonce2 != _curNonce) && (!validPassphrase()) )
|
||||
if ( (!haveNonce(_curNonce)) && (!validPassphrase()) )
|
||||
return "Invalid form submission, probably because you used the 'back' or 'reload' button on your browser. Please resubmit.";
|
||||
if ("Stop all".equals(_action))
|
||||
return stopAll();
|
||||
|
||||
Reference in New Issue
Block a user