Compare commits

...

14 Commits

Author SHA1 Message Date
idk
9e64264a03 Merge branch 'master' of i2pgit.org:i2p-hackers/i2p.i2p into i2ptunnel-udptunnel 2022-06-28 11:26:49 -04:00
idk
51497f4135 Fix log message. Also I think I have the local and remote reversed here. 2022-03-29 11:48:23 -04:00
idk
01171ddea6 move send and recieve to synchronized functions. This is definitely wrong but I've got to see it to understand what's wrong. 2022-03-28 19:04:43 -04:00
idk
1c369d54cd Merge branch 'master' of i2pgit.org:i2p-hackers/i2p.i2p into i2ptunnel-udptunnel 2022-03-28 15:50:31 -04:00
idk
6a4cf667a6 Parse arguments for running UDP server 2022-01-31 11:20:51 -05:00
idk
ab4be06740 I think that client side should work, the issue must be with the server side 2022-01-27 15:36:21 -05:00
idk
545d768403 Fix the UI elements which were wrong for UDP clients 2022-01-25 17:51:52 -05:00
idk
2105ffa27f Check in the UI parts 2022-01-25 12:58:45 -05:00
idk
65b8004c00 Check in the UI parts 2022-01-25 12:57:33 -05:00
idk
c796cff025 Merge branch 'master' of i2pgit.org:i2p-hackers/i2p.i2p into i2ptunnel-udptunnel 2022-01-24 14:12:26 -05:00
idk
c9dd63b256 add options for UDP tunnels 2022-01-21 23:07:02 -05:00
idk
e86561c30c more progress on 'Clients' which for now is what I'm calling the side that does not send data over I2P. These terms are not ideal though, maybe this should be 'Server' and 'ServEnt' to borrow a term from an older network? Or Recieve and Send/Recieve to be literal about it? Not quite sure but it's probably important to pick a non-confusing name 2022-01-21 17:47:04 -05:00
idk
49b0575d7a Start looking at where to have sinks 2022-01-12 16:52:35 -05:00
idk
bb81f1a60f Create stubs for new UDPClient and UDPServer 2021-12-23 14:49:06 -05:00
10 changed files with 2554 additions and 1534 deletions

View File

@ -78,6 +78,8 @@ import net.i2p.i2ptunnel.socks.I2PSOCKSIRCTunnel;
import net.i2p.i2ptunnel.socks.I2PSOCKSTunnel;
import net.i2p.i2ptunnel.streamr.StreamrConsumer;
import net.i2p.i2ptunnel.streamr.StreamrProducer;
import net.i2p.i2ptunnel.udpTunnel.I2PTunnelUDPClient;
import net.i2p.i2ptunnel.udpTunnel.I2PTunnelUDPServerClient;
import net.i2p.util.EventDispatcherImpl;
import net.i2p.util.Log;
import net.i2p.util.OrderedProperties;
@ -112,6 +114,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
/**
* Absolute path to filter definition file
*
* @since 0.9.40
*/
public String filterDefinition;
@ -135,7 +138,9 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
};
/** @since 0.9.17 */
private enum CloseMode { NORMAL, FORCED, DESTROY }
private enum CloseMode {
NORMAL, FORCED, DESTROY
}
public static void main(String[] args) {
try {
@ -147,7 +152,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
}
/**
* Standard constructor for embedded, uses args "-nocli -die" to return immediately
* Standard constructor for embedded, uses args "-nocli -die" to return
* immediately
*/
public I2PTunnel() {
this(nocli_args);
@ -155,6 +161,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
/**
* New standard constructor in router, with back ref to tc
*
* @param tc may be null
* @throws IllegalArgumentException
* @since 0.9.48
@ -165,6 +172,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
/**
* See usage() for options
*
* @throws IllegalArgumentException
*/
public I2PTunnel(String[] args) {
@ -173,6 +181,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
/**
* See usage() for options
*
* @param lsnr may be null
* @throws IllegalArgumentException
*/
@ -308,8 +317,10 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
while (true) {
System.out.print("I2PTunnel> ");
String cmd = r.readLine();
if (cmd == null) break;
if (cmd.length() <= 0) continue;
if (cmd == null)
break;
if (cmd.length() <= 0)
continue;
try {
runCommand(cmd, this);
} catch (Throwable t) {
@ -338,8 +349,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
/** with newlines except for last line */
private static String usage() {
// not sure this all makes sense, just documenting what's above
return
"Usage: i2ptunnel [options] [commandFile]\n" +
return "Usage: i2ptunnel [options] [commandFile]\n" +
" Default is to run the GUI.\n" +
" commandFile: run all commands in this file\n" +
" Options:\n" +
@ -366,7 +376,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
* @param session null ok
*/
void addSession(I2PSession session) {
if (session == null) return;
if (session == null)
return;
boolean added = _sessions.add(session);
if (added && _log.shouldLog(Log.INFO))
_log.info(getPrefix() + " session added: " + session, new Exception());
@ -376,7 +387,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
* @param session null ok
*/
void removeSession(I2PSession session) {
if (session == null) return;
if (session == null)
return;
boolean removed = _sessions.remove(session);
if (removed && _log.shouldLog(Log.INFO))
_log.info(getPrefix() + " session removed: " + session, new Exception());
@ -385,16 +397,22 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
/**
* Generic options used for clients and servers.
* NOT a copy, Do NOT modify for per-connection options, make a copy.
*
* @return non-null, NOT a copy, do NOT modify for per-connection options
*/
public Properties getClientOptions() { return _clientOptions; }
public Properties getClientOptions() {
return _clientOptions;
}
/**
* TunnelController that constructed this, or null.
*
* @return controller or null
* @since 0.9.48
*/
TunnelController getController() { return _controller; }
TunnelController getController() {
return _controller;
}
private void addtask(I2PTunnelTask tsk) {
tsk.setTunnel(this);
@ -410,7 +428,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
}
}
/** java 1.3 vs 1.4 :)
/**
* java 1.3 vs 1.4 :)
*/
private static String[] split(String src, String delim) {
StringTokenizer tok = new StringTokenizer(src, delim);
@ -421,7 +440,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
}
public void runCommand(String cmd, Logging l) {
if (cmd.indexOf(' ') == -1) cmd += ' ';
if (cmd.indexOf(' ') == -1)
cmd += ' ';
int iii = cmd.indexOf(' ');
String cmdname = cmd.substring(0, iii).toLowerCase(Locale.US);
String allargs = cmd.substring(iii + 1);
@ -563,7 +583,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
}
for (; i < args.length; i++) {
int index = args[i].indexOf('=');
if (index <= 0) continue;
if (index <= 0)
continue;
String key = args[i].substring(0, index);
String val = args[i].substring(index + 1);
_clientOptions.setProperty(key, val);
@ -590,7 +611,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
* This DOES update a running TunnelTask, but NOT the session.
* A more efficient runClientOptions().
*
* Defaults in opts properties are not recommended, they may or may not be honored.
* Defaults in opts properties are not recommended, they may or may not be
* honored.
*
* @param opts non-null
* @since 0.9.1
@ -610,10 +632,13 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
/**
* Run the server pointing at the host and port specified using the private i2p
* destination loaded from the specified file. <p>
* destination loaded from the specified file.
* <p>
*
* 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
* 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, privKeyFilename}
@ -669,6 +694,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
/**
* Same args as runServer
* (we should stop duplicating all this code...)
*
* @throws IllegalArgumentException on config problem
*/
public void runIrcServer(String args[], Logging l) {
@ -718,12 +744,16 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
}
/**
* Run the HTTP server pointing at the host and port specified using the private i2p
* Run the HTTP server pointing at the host and port specified using the private
* i2p
* destination loaded from the specified file, replacing the HTTP headers
* so that the Host: specified is the one spoofed. <p>
* so that the Host: specified is the one spoofed.
* <p>
*
* 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
* 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, spoofedHost, privKeyFilename}
@ -765,7 +795,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
notifyEvent("serverTaskId", Integer.valueOf(-1));
throw new IllegalArgumentException(getPrefix() + "Cannot open private key file " + args[3]);
}
I2PTunnelHTTPServer serv = new I2PTunnelHTTPServer(serverHost, portNum, privKeyFile, args[3], spoofedHost, l, this, this);
I2PTunnelHTTPServer serv = new I2PTunnelHTTPServer(serverHost, portNum, privKeyFile, args[3], spoofedHost,
l, this, this);
serv.setReadTimeout(readTimeout);
serv.startRunning();
addtask(serv);
@ -781,16 +812,21 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
}
/**
* Run the HTTP server pointing at the host and port specified using the private i2p
* Run the HTTP server pointing at the host and port specified using the private
* i2p
* destination loaded from the specified file, replacing the HTTP headers
* so that the Host: specified is the one spoofed. Also runs an HTTP proxy for
* bidirectional communications on the same tunnel destination.<p>
* bidirectional communications on the same tunnel destination.
* <p>
*
* 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
* 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, proxyPortNumber, spoofedHost, privKeyFilename}
* @param args {hostname, portNumber, proxyPortNumber, spoofedHost,
* privKeyFilename}
* @param l logger to receive events and output
* @throws IllegalArgumentException on config problem
*/
@ -841,7 +877,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
throw new IllegalArgumentException(getPrefix() + "Cannot open private key file " + args[4]);
}
I2PTunnelHTTPBidirServer serv = new I2PTunnelHTTPBidirServer(serverHost, portNum, port2Num, privKeyFile, args[3], spoofedHost, l, this, this);
I2PTunnelHTTPBidirServer serv = new I2PTunnelHTTPBidirServer(serverHost, portNum, port2Num, privKeyFile,
args[3], spoofedHost, l, this, this);
serv.setReadTimeout(readTimeout);
serv.startRunning();
addtask(serv);
@ -859,13 +896,16 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
/**
* Run the server pointing at the host and port specified using the private i2p
* destination loaded from the given base64 stream. <p>
* 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
* 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}
@ -911,13 +951,17 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
* Run the client on the given port number pointing at the specified destination
* (either the base64 of the destination or file:fileNameContainingDestination).
*
* Sets the event "clientTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error)
* Also sets the event "openClientResult" = "error" or "ok" (before setting the value to "ok" it also
* adds "Ready! Port #" to the logger as well). In addition, it will also set "clientLocalPort" =
* Sets the event "clientTaskId" = Integer(taskId) after the tunnel has been
* started (or -1 on error)
* Also sets the event "openClientResult" = "error" or "ok" (before setting the
* value to "ok" it also
* adds "Ready! Port #" to the logger as well). In addition, it will also set
* "clientLocalPort" =
* Integer port number if the client is listening
* sharedClient parameter is a String "true" or "false"
*
* @param args {portNumber, destinationBase64 or "file:filename"[, sharedClient [, privKeyFile]]}
* @param args {portNumber, destinationBase64 or "file:filename"[, sharedClient
* [, privKeyFile]]}
* @param l logger to receive events and output
* @throws IllegalArgumentException on config problem
*/
@ -942,19 +986,23 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
String privateKeyFile = null;
if (args.length >= 4)
privateKeyFile = args[3];
I2PTunnelClientBase task = new I2PTunnelClient(portNum, args[1], l, ownDest, this, this, privateKeyFile);
I2PTunnelClientBase task = new I2PTunnelClient(portNum, args[1], l, ownDest, this, this,
privateKeyFile);
task.startRunning();
addtask(task);
notifyEvent("clientTaskId", Integer.valueOf(task.getId()));
} catch (IllegalArgumentException iae) {
String msg = "Invalid I2PTunnel configuration to create a standard client tunnel connecting to the router at " + host + ':'+ port +
String msg = "Invalid I2PTunnel configuration to create a standard client tunnel connecting to the router at "
+ host + ':' + port +
" and listening on " + listenHost + ':' + portNum;
_log.error(getPrefix() + msg, iae);
l.log(msg);
notifyEvent("clientTaskId", Integer.valueOf(-1));
// Since nothing listens to TaskID events, use this to propagate the error to TunnelController
// 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,
// 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;
}
@ -970,8 +1018,10 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
/**
* Run an HTTP client on the given port number
*
* Sets the event "httpclientTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error).
* Also sets "httpclientStatus" = "ok" or "error" after the client tunnel has started.
* Sets the event "httpclientTaskId" = Integer(taskId) after the tunnel has been
* started (or -1 on error).
* Also sets "httpclientStatus" = "ok" or "error" after the client tunnel has
* started.
* parameter sharedClient is a String, either "true" or "false"
*
* @param args {portNumber[, sharedClient][, proxy to be used for the WWW]}
@ -1019,21 +1069,25 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
addtask(task);
notifyEvent("httpclientTaskId", Integer.valueOf(task.getId()));
} catch (IllegalArgumentException iae) {
String msg = "Invalid I2PTunnel configuration to create an HTTP Proxy connecting to the router at " + host + ':'+ port +
String msg = "Invalid I2PTunnel configuration to create an HTTP Proxy connecting to the router at "
+ host + ':' + port +
" and listening on " + listenHost + ':' + clientPort;
_log.error(getPrefix() + msg, iae);
l.log(msg);
notifyEvent("httpclientTaskId", Integer.valueOf(-1));
// Since nothing listens to TaskID events, use this to propagate the error to TunnelController
// 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,
// 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>]\n" +
" Creates a HTTP client proxy on the specified port.\n" +
" <sharedClient> (optional) Indicates if this client shares tunnels with other clients (true or false)\n" +
" <sharedClient> (optional) Indicates if this client shares tunnels with other clients (true or false)\n"
+
" <proxy> (optional) Indicates a proxy server to be used\n" +
" when trying to access an address out of the .i2p domain");
notifyEvent("httpclientTaskId", Integer.valueOf(-1));
@ -1085,20 +1139,24 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
task.startRunning();
addtask(task);
} catch (IllegalArgumentException iae) {
String msg = "Invalid I2PTunnel configuration to create a CONNECT client connecting to the router at " + host + ':'+ port +
String msg = "Invalid I2PTunnel configuration to create a CONNECT client connecting to the router at "
+ host + ':' + port +
" and listening on " + listenHost + ':' + _port;
_log.error(getPrefix() + msg, iae);
l.log(msg);
// Since nothing listens to TaskID events, use this to propagate the error to TunnelController
// 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,
// 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>]\n" +
" creates a client that for SSL/HTTPS requests.\n" +
" <sharedClient> (optional) indicates if this client shares tunnels with other clients (true or false)\n" +
" <sharedClient> (optional) indicates if this client shares tunnels with other clients (true or false)\n"
+
" <proxy> (optional) indicates a proxy server to be used\n" +
" when trying to access an address out of the .i2p domain\n");
}
@ -1107,11 +1165,14 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
/**
* Run an IRC client on the given port number
*
* Sets the event "ircclientTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error).
* Also sets "ircclientStatus" = "ok" or "error" after the client tunnel has started.
* Sets the event "ircclientTaskId" = Integer(taskId) after the tunnel has been
* started (or -1 on error).
* Also sets "ircclientStatus" = "ok" or "error" after the client tunnel has
* started.
* parameter sharedClient is a String, either "true" or "false"
*
* @param args {portNumber,destinationBase64 or "file:filename" [, sharedClient [, privKeyFile]]}
* @param args {portNumber,destinationBase64 or "file:filename" [, sharedClient
* [, privKeyFile]]}
* @param l logger to receive events and output
* @throws IllegalArgumentException on config problem
*/
@ -1145,19 +1206,23 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
String privateKeyFile = null;
if (args.length >= 4)
privateKeyFile = args[3];
I2PTunnelClientBase task = new I2PTunnelIRCClient(_port, args[1], l, ownDest, this, this, privateKeyFile);
I2PTunnelClientBase task = new I2PTunnelIRCClient(_port, args[1], l, ownDest, this, this,
privateKeyFile);
task.startRunning();
addtask(task);
notifyEvent("ircclientTaskId", Integer.valueOf(task.getId()));
} catch (IllegalArgumentException iae) {
String msg = "Invalid I2PTunnel configuration to create an IRC client connecting to the router at " + host + ':'+ port +
String msg = "Invalid I2PTunnel configuration to create an IRC client connecting to the router at "
+ host + ':' + port +
" and listening on " + listenHost + ':' + _port;
_log.error(getPrefix() + msg, iae);
l.log(msg);
notifyEvent("ircclientTaskId", Integer.valueOf(-1));
// Since nothing listens to TaskID events, use this to propagate the error to TunnelController
// 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,
// 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;
}
@ -1177,7 +1242,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
* "openSOCKSTunnelResult" = "ok" or "error" after the client tunnel has
* started.
*
* @param args {portNumber [, sharedClient]} or (portNumber, ignored (false), privKeyFile)
* @param args {portNumber [, sharedClient]} or (portNumber, ignored (false),
* privKeyFile)
* @param l logger to receive events and output
* @throws IllegalArgumentException on config problem
*/
@ -1208,7 +1274,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
addtask(task);
notifyEvent("sockstunnelTaskId", Integer.valueOf(task.getId()));
} catch (IllegalArgumentException iae) {
String msg = "Invalid I2PTunnel configuration to create a SOCKS Proxy connecting to the router at " + host + ':'+ port +
String msg = "Invalid I2PTunnel configuration to create a SOCKS Proxy connecting to the router at "
+ host + ':' + port +
" and listening on " + listenHost + ':' + _port;
_log.error(getPrefix() + msg, iae);
l.log(msg);
@ -1222,10 +1289,11 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
}
}
/**
* Run an SOCKS IRC tunnel on the given port number
* @param args {portNumber [, sharedClient]} or (portNumber, ignored (false), privKeyFile)
*
* @param args {portNumber [, sharedClient]} or (portNumber, ignored (false),
* privKeyFile)
* @throws IllegalArgumentException on config problem
* @since 0.7.12
*/
@ -1256,7 +1324,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
addtask(task);
notifyEvent("sockstunnelTaskId", Integer.valueOf(task.getId()));
} catch (IllegalArgumentException iae) {
String msg = "Invalid I2PTunnel configuration to create a SOCKS IRC Proxy connecting to the router at " + host + ':'+ port +
String msg = "Invalid I2PTunnel configuration to create a SOCKS IRC Proxy connecting to the router at "
+ host + ':' + port +
" and listening on " + listenHost + ':' + _port;
_log.error(getPrefix() + msg, iae);
l.log(msg);
@ -1306,7 +1375,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
addtask(task);
notifyEvent("streamrtunnelTaskId", Integer.valueOf(task.getId()));
} catch (IllegalArgumentException iae) {
String msg = "Invalid I2PTunnel configuration to create a Streamr Client connecting to the router at " + host + ':'+ port +
String msg = "Invalid I2PTunnel configuration to create a Streamr Client connecting to the router at "
+ host + ':' + port +
" and sending to " + _host + ':' + _port;
_log.error(getPrefix() + msg, iae);
l.log(msg);
@ -1361,11 +1431,153 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
}
}
/**
* UDP client
*
* @param args {targethost, targetport, destinationString}
* @param l logger to receive events and output
* @throws IllegalArgumentException on config problem
*/
public void runUDPClient(String[] args, Logging l) {
if (args.length == 3) {
if (args.length == 3) {
InetAddress _host;
try {
_host = InetAddress.getByName(args[0]);
} catch (UnknownHostException uhe) {
l.log("unknown host");
_log.error(getPrefix() + "Error resolving " + args[0], uhe);
notifyEvent("udpClientTunnelId", Integer.valueOf(-1));
return;
}
int _port = -1;
try {
_port = Integer.parseInt(args[1]);
} catch (NumberFormatException nfe) {
l.log("invalid port");
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
notifyEvent("udpClientTunnelId", Integer.valueOf(-1));
}
if (_port <= 0)
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[0]);
try {
I2PTunnelUDPClient task = new I2PTunnelUDPClient(_host.toString(), _port, args[2], l, this, this);
task.startRunning();
addtask(task);
notifyEvent("udpClientTunnelId", Integer.valueOf(task.getId()));
} catch (IllegalArgumentException iae) {
String msg = "Invalid I2PTunnel configuration to create a UDP Client connecting to the router at "
+ host + ':' + port +
" and sending to " + _host + ':' + _port;
_log.error(getPrefix() + msg, iae);
l.log(msg);
notifyEvent("udpClientTunnelId", Integer.valueOf(-1));
throw iae;
}
} else {
l.log("streamrclient <host> <port> <destination>\n" +
" creates a tunnel that receives streaming data.");
notifyEvent("udpClientTunnelId", Integer.valueOf(-1));
}
}
}
/**
* UDP server
*
* @param args {targethost, targetproxy, transport, privkeyfile}
* @param l logger to receive events and output
* @throws IllegalArgumentException on config problem
*/
public void runUDPServer(String[] args, Logging l) {
if (args.length == 4) {
InetAddress _host;
try {
_host = InetAddress.getByName(args[0]);
} catch (UnknownHostException uhe) {
l.log("unknown host");
_log.error(getPrefix() + "Error resolving " + args[0], uhe);
notifyEvent("udpservertunnelTaskId", Integer.valueOf(-1));
return;
}
int _proxyport = -1;
try {
_proxyport = Integer.parseInt(args[1]);
} catch (NumberFormatException nfe) {
l.log("invalid port");
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
notifyEvent("udpservertunnelTaskId", Integer.valueOf(-1));
}
if (_proxyport <= 0)
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[0]);
int _rproxyport = -1;
try {
_rproxyport = Integer.parseInt(args[2]);
} catch (NumberFormatException nfe) {
l.log("invalid port");
_log.error(getPrefix() + "Port specified is not valid: " + args[2], nfe);
notifyEvent("udpservertunnelTaskId", Integer.valueOf(-1));
}
if (_rproxyport <= 0)
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[2]);
File privKeyFile = new File(args[3]);
if (!privKeyFile.isAbsolute())
privKeyFile = new File(_context.getConfigDir(), args[1]);
if (!privKeyFile.canRead()) {
l.log("private key file does not exist");
_log.error(getPrefix() + "Private key file does not exist or is not readable: " + args[3]);
notifyEvent("udpservertunnelTaskId", Integer.valueOf(-1));
return;
}
// I2PTunnelUDPServerClient(String host, int port, File privkey, String
// privkeyname, Logging l,
// EventDispatcher notifyThis,
// I2PTunnel tunnel)
// EventDispatcher notifyThis,
// I2PTunnel tunnel)
try {
I2PTunnelUDPServerClient task = new I2PTunnelUDPServerClient(_host.toString(), _proxyport, privKeyFile,
privKeyFile.toString(), l, this, this);
task.startRunning();
addtask(task);
notifyEvent("udpServerTunnelId", Integer.valueOf(task.getId()));
} catch (IllegalArgumentException iae) {
String msg = "Invalid I2PTunnel configuration to create a UDP Server connecting to the router at "
+ host + ':' + port +
" and sending to " + _host + ':' + _proxyport;
_log.error(getPrefix() + msg, iae);
l.log(msg);
notifyEvent("udpServerTunnelId", Integer.valueOf(-1));
throw iae;
}
// I2PTunnelUDPServerClient task = new I2PTunnelUDPServerClient(_port,
// privKeyFile, args[1], l, this, this);
// task.startRunning();
// addtask(task);
// notifyEvent("udpservertunnelTaskId", Integer.valueOf(task.getId()));
} else
{
l.log("streamrserver <port> <privkeyfile>\n" +
" creates a tunnel that sends streaming data.");
notifyEvent("udpservertunnelTaskId", Integer.valueOf(-1));
}
}
/**
* Specify the i2cp host and port
* Deprecated - only used by CLI
*
* Sets the event "configResult" = "ok" or "error" after the configuration has been specified
* Sets the event "configResult" = "ok" or "error" after the configuration has
* been specified
*
* @param args {hostname, portNumber}
* @param l logger to receive events and output
@ -1420,7 +1632,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
* Specify whether to use its own destination for each outgoing tunnel
* Deprecated - only used by CLI
*
* Sets the event "owndestResult" = "ok" or "error" after the configuration has been specified
* Sets the event "owndestResult" = "ok" or "error" after the configuration has
* been specified
*
* @param args {yes or no}
* @param l logger to receive events and output
@ -1438,9 +1651,11 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
}
/**
* Specify the hostname / IP address of the interface that the tunnels should bind to
* Specify the hostname / IP address of the interface that the tunnels should
* bind to
*
* Sets the event "listen_onResult" = "ok" or "error" after the interface has been specified
* Sets the event "listen_onResult" = "ok" or "error" after the interface has
* been specified
*
* @param args {hostname}
* @param l logger to receive events and output
@ -1459,7 +1674,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
/**
* Specify the read timeout going to be used for newly-created I2PSockets
*
* Sets the event "read_timeoutResult" = "ok" or "error" after the interface has been specified
* Sets the event "read_timeoutResult" = "ok" or "error" after the interface has
* been specified
*
* @param args {hostname}
* @param l logger to receive events and output
@ -1485,7 +1701,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
* Does NOT support non-default sig types.
* Deprecated - only used by CLI
*
* Sets the event "genkeysResult" = "ok" or "error" after the generation is complete
* Sets the event "genkeysResult" = "ok" or "error" after the generation is
* complete
*
* @param args {privateKeyFilename, publicKeyFilename} or {privateKeyFilename}
* @param l logger to receive events and output
@ -1522,7 +1739,11 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
// notifyEvent("genkeysResult", "error");
// _log.error(getPrefix() + "Error generating keys", ioe);
} finally {
if(pubdest != null) try { pubdest.close(); } catch(IOException ioe) {}
if (pubdest != null)
try {
pubdest.close();
} catch (IOException ioe) {
}
}
}
@ -1550,8 +1771,10 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
* running, it returns.
* Deprecated - only used by CLI
*
* Sets the event "quitResult" = "error" if there are tasks running (but if there
* aren't, well, there's no point in setting the quitResult to "ok", now is there?)
* Sets the event "quitResult" = "error" if there are tasks running (but if
* there
* aren't, well, there's no point in setting the quitResult to "ok", now is
* there?)
*
* @param l logger to receive events and output
*/
@ -1587,7 +1810,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
*
* Sets the event "closeResult" = "ok" after the closing is complete
*
* @param args {jobNumber}, {"forced", jobNumber}, {"forced", "all"}, {"destroy", jobNumber}, {"destroy", "all"}
* @param args {jobNumber}, {"forced", jobNumber}, {"forced", "all"},
* {"destroy", jobNumber}, {"destroy", "all"}
* @param l logger to receive events and output
*/
public void runClose(String args[], Logging l) {
@ -1663,7 +1887,11 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
_log.error(getPrefix() + "Error running the file", ioe);
notifyEvent("runResult", "error");
} finally {
if (br != null) try { br.close(); } catch (IOException ioe) {}
if (br != null)
try {
br.close();
} catch (IOException ioe) {
}
}
} else {
l.log("run <commandfile>\n" +
@ -1677,7 +1905,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
* Perform a lookup of the name specified
* Deprecated - only used by CLI
*
* Sets the event "lookupResult" = base64 of the destination, or an error message
* Sets the event "lookupResult" = base64 of the destination, or an error
* message
*
* @param args {name}
* @param l logger to receive events and output
@ -1708,7 +1937,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
}
/**
* Start up a ping task with the specified args (currently supporting -ns, -h, -l)
* Start up a ping task with the specified args (currently supporting -ns, -h,
* -l)
* Deprecated - only used by CLI
*
* Sets the event "pingTaskId" = Integer of the taskId, or -1
@ -1741,10 +1971,12 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
/**
* Send a BlindingInfo message, just for testing
*
* @since 0.9.43
*/
private void runBlinding(String[] argv, Logging l) {
// blinding [-d|p] [-k key] [-s password] [-e expires] xxx.b32.i2p // -d for DH; -p for PSK; expires in days
// blinding [-d|p] [-k key] [-s password] [-e expires] xxx.b32.i2p // -d for DH;
// -p for PSK; expires in days
Getopt g = new Getopt("blinding", argv, "dpk:s:e:");
boolean error = false;
boolean dh = false;
@ -1783,7 +2015,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
}
int remaining = argv.length - g.getOptind();
if (error || remaining != 1 || (dh && psk) || ((dh || psk) && key == null)) {
System.out.println("Usage: blinding [-d|p] [-k key] [-s password] [-e expires] xxx.b32.i2p // -d for DH; -p for PSK; expires in days");
System.out.println(
"Usage: blinding [-d|p] [-k key] [-s password] [-e expires] xxx.b32.i2p // -d for DH; -p for PSK; expires in days");
return;
}
String name = argv[g.getOptind()];
@ -1833,12 +2066,18 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
session.connect();
System.out.println("Sending: " + bd);
session.sendBlindingInfo(bd);
try { Thread.sleep(1000); } catch (InterruptedException ie) {}
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
}
} catch (I2PSessionException ise) {
System.out.println("Send blinding info failed: " + ise);
} finally {
if (session != null) {
try { session.destroySession(); } catch (I2PSessionException ise) {}
try {
session.destroySession();
} catch (I2PSessionException ise) {
}
}
}
}
@ -1906,7 +2145,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
}
/**
* Log the given message (using both the logging subsystem and standard output...)
* Log the given message (using both the logging subsystem and standard
* output...)
*
*/
public void log(String s) {
@ -1972,7 +2212,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
* @param l logger to send messages to
*/
private static void writePubKey(Destination d, OutputStream o, Logging l) throws I2PException, IOException {
if (o == null) return;
if (o == null)
return;
d.writeBytes(o);
l.log("Public key saved.");
}
@ -1986,7 +2227,10 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
*
* Since file:&lt;filename&gt; isn't really used, this method is deprecated,
* just call context.namingService.lookup() directly.
* @deprecated Don't use i2ptunnel for lookup! Use I2PAppContext.getGlobalContext().namingService().lookup(name) from i2p.jar
*
* @deprecated Don't use i2ptunnel for lookup! Use
* I2PAppContext.getGlobalContext().namingService().lookup(name)
* from i2p.jar
*/
@Deprecated
public static Destination destFromName(String name) throws DataFormatException {
@ -2004,7 +2248,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
String i2cpPort, boolean isSSL,
String user, String pw) throws DataFormatException {
if ((name == null) || (name.trim().length() <= 0)) throw new DataFormatException("Empty destination provided");
if ((name == null) || (name.trim().length() <= 0))
throw new DataFormatException("Empty destination provided");
I2PAppContext ctx = I2PAppContext.getGlobalContext();
Log log = ctx.logManager().getLog(I2PTunnel.class);
@ -2023,7 +2268,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
System.out.println(ioe.getMessage());
return null;
} finally {
if (in != null) try {
if (in != null)
try {
in.close();
} catch (IOException io) {
}
@ -2082,7 +2328,10 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
log.warn("Lookup via router failed", ise);
} finally {
if (session != null) {
try { session.destroySession(); } catch (I2PSessionException ise) {}
try {
session.destroySession();
} catch (I2PSessionException ise) {
}
}
}
return d;
@ -2090,28 +2339,36 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
}
public void addConnectionEventListener(ConnectionEventListener lsnr) {
if (lsnr == null) return;
if (lsnr == null)
return;
listeners.add(lsnr);
}
public void removeConnectionEventListener(ConnectionEventListener lsnr) {
if (lsnr == null) return;
if (lsnr == null)
return;
listeners.remove(lsnr);
}
private String getPrefix() { return "[" + _tunnelId + "]: "; }
private String getPrefix() {
return "[" + _tunnelId + "]: ";
}
public I2PAppContext getContext() { return _context; }
public I2PAppContext getContext() {
return _context;
}
/**
* Call this whenever we lose touch with the router involuntarily (aka the router
* Call this whenever we lose touch with the router involuntarily (aka the
* router
* is off / crashed / etc)
*
*/
void routerDisconnected() {
_log.error(getPrefix() + "Router disconnected - firing notification events");
for (ConnectionEventListener lsnr : listeners) {
if (lsnr != null) lsnr.routerDisconnected();
if (lsnr != null)
lsnr.routerDisconnected();
}
}

View File

@ -167,8 +167,13 @@ public class TunnelController implements Logging {
/** Server in the UI and I2P side but a client on the localhost side */
public static final String TYPE_STREAMR_SERVER = "streamrserver";
/** UDP Client and Server */
public static final String TYPE_UDP_CLIENT = "udpclient";
public static final String TYPE_UDP_SERVER = "udpserver";
/**
* This is guaranteed to be available.
*
* @since 0.9.17
*/
public static final SigType PREFERRED_SIGTYPE;
@ -189,10 +194,12 @@ public class TunnelController implements Logging {
* the prefix should be used (and, in turn, that prefix should be stripped off
* before being interpreted by this controller)
*
* If config contains the "configFile" property, it will be set as the config path
* If config contains the "configFile" property, it will be set as the config
* path
* and may be retrieved with getConfigFile().
*
* Defaults in config properties are not recommended, they may or may not be honored.
* Defaults in config properties are not recommended, they may or may not be
* honored.
*
* @param config original key=value mapping non-null
* @param prefix beginning of key values that are relevant to this tunnel
@ -207,14 +214,17 @@ public class TunnelController implements Logging {
* the prefix should be used (and, in turn, that prefix should be stripped off
* before being interpreted by this controller)
*
* If config contains the "configFile" property, it will be set as the config path
* If config contains the "configFile" property, it will be set as the config
* path
* and may be retrieved with getConfigFile().
*
* Defaults in config properties are not recommended, they may or may not be honored.
* Defaults in config properties are not recommended, they may or may not be
* honored.
*
* @param config original key=value mapping non-null
* @param prefix beginning of key values that are relevant to this tunnel
* @param createKey for servers, whether we want to create a brand new destination
* @param createKey for servers, whether we want to create a brand new
* destination
* with private keys at the location specified or not (does not
* overwrite existing ones)
*/
@ -301,13 +311,18 @@ public class TunnelController implements Logging {
log("Error writing the keys to " + keyFile.getAbsolutePath());
return false;
} finally {
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
if (fos != null)
try {
fos.close();
} catch (IOException ioe) {
}
}
return true;
}
/**
* Creates alternate Destination with the same encryption keys as the primary Destination,
* Creates alternate Destination with the same encryption keys as the primary
* Destination,
* but a different signing key.
*
* Must have already called createPrivateKey() successfully.
@ -363,7 +378,10 @@ public class TunnelController implements Logging {
d.writeBytes(out);
priv.writeBytes(out);
signingPrivKey.writeBytes(out);
try { out.close(); } catch (IOException ioe) {}
try {
out.close();
} catch (IOException ioe) {
}
String destStr = d.toBase64();
log("Alternate private key created and saved in " + altFile.getAbsolutePath());
@ -397,7 +415,11 @@ public class TunnelController implements Logging {
log("Error creating keys " + e);
return false;
} finally {
if (out != null) try { out.close(); } catch (IOException ioe) {}
if (out != null)
try {
out.close();
} catch (IOException ioe) {
}
}
}
@ -406,7 +428,11 @@ public class TunnelController implements Logging {
if (_state != TunnelState.STOPPED && _state != TunnelState.START_ON_LOAD)
return;
}
new I2PAppThread(new Runnable() { public void run() { startTunnel(); } }, "Tunnel Starter " + getName()).start();
new I2PAppThread(new Runnable() {
public void run() {
startTunnel();
}
}, "Tunnel Starter " + getName()).start();
}
/**
@ -492,6 +518,10 @@ public class TunnelController implements Logging {
startIrcServer();
} else if (TYPE_STREAMR_SERVER.equals(type)) {
startStreamrServer();
} else if (TYPE_UDP_CLIENT.equals(type)) {
startUDPClient();
} else if (TYPE_UDP_SERVER.equals(type)) {
startUDPServer();
} else {
changeState(TunnelState.STOPPED);
if (_log.shouldLog(Log.ERROR))
@ -592,6 +622,29 @@ public class TunnelController implements Logging {
_tunnel.runStreamrClient(new String[] { targetHost, targetPort, dest }, this);
}
/**
* UDP server is a UDP client, use the targetPort field for listenPort
*/
private void startUDPServer() {
String listenOn = getListenOnInterface();
if ((listenOn != null) && (listenOn.length() > 0)) {
_tunnel.runListenOn(new String[] { listenOn }, this);
}
String listenPort = getTargetPort();
String privKeyFile = getPrivKeyFile();
_tunnel.runUDPServer(new String[] { listenPort, privKeyFile }, this);
}
/**
* UDP client is a UDP server, use the listenPort field for targetPort
*/
private void startUDPClient() {
String targetHost = getTargetHost();
String targetPort = getListenPort();
String dest = getTargetDestination();
_tunnel.runUDPClient(new String[] { targetHost, targetPort, dest }, this);
}
/**
* Streamr server is a UDP client, use the targetPort field for listenPort
*/
@ -783,7 +836,8 @@ public class TunnelController implements Logging {
/**
* May be restarted with restartTunnel() or startTunnel() later.
* This may not release all resources. In particular, the I2PSocketManager remains
* This may not release all resources. In particular, the I2PSocketManager
* remains
* and it may have timer threads that continue running.
*/
public void stopTunnel() {
@ -837,7 +891,10 @@ public class TunnelController implements Logging {
}
if (oldState != TunnelState.STOPPED) {
long ms = _tunnel.getContext().isRouterContext() ? 100 : 500;
try { Thread.sleep(ms); } catch (InterruptedException ie) {}
try {
Thread.sleep(ms);
} catch (InterruptedException ie) {
}
}
startTunnel();
}
@ -896,7 +953,7 @@ public class TunnelController implements Logging {
// override UI that sets it to false
_config.setProperty(OPT_BUNDLE_REPLY, "true");
}
if (type.contains("irc") || type.equals(TYPE_STREAMR_CLIENT)) {
if (type.contains("irc") || type.equals(TYPE_STREAMR_CLIENT) || type.equals(TYPE_UDP_SERVER)) {
// maybe a bad idea for ircclient if DCC is enabled
if (!_config.containsKey(OPT_TAGS_SEND))
_config.setProperty(OPT_TAGS_SEND, "20");
@ -946,7 +1003,8 @@ public class TunnelController implements Logging {
String p10 = _config.getProperty(OPT_POST_TOTAL_MAX, "0");
if (p9.equals("0") && p10.equals("0")) {
_config.setProperty(OPT_POST_MAX, Integer.toString(I2PTunnelHTTPServer.DEFAULT_POST_MAX));
_config.setProperty(OPT_POST_TOTAL_MAX, Integer.toString(I2PTunnelHTTPServer.DEFAULT_POST_TOTAL_MAX));
_config.setProperty(OPT_POST_TOTAL_MAX,
Integer.toString(I2PTunnelHTTPServer.DEFAULT_POST_TOTAL_MAX));
}
}
}
@ -999,6 +1057,7 @@ public class TunnelController implements Logging {
/**
* Is property p different in p1 and p2?
*
* @since 0.9.30
*/
private static boolean configChanged(Properties p1, Properties p2, String p) {
@ -1030,30 +1089,54 @@ public class TunnelController implements Logging {
* or as set later, or null
* @since 0.9.42
*/
public File getConfigFile() { return _configFile; }
public File getConfigFile() {
return _configFile;
}
/**
* Set the config file. Only do this if previously null.
*
* @since 0.9.42
*/
public void setConfigFile(File file) { _configFile = file; }
public void setConfigFile(File file) {
_configFile = file;
}
public String getType() { return _config.getProperty(PROP_TYPE); }
public String getName() { return _config.getProperty(PROP_NAME); }
public String getDescription() { return _config.getProperty(PROP_DESCR); }
public String getI2CPHost() { return _config.getProperty(PROP_I2CP_HOST); }
public String getI2CPPort() { return _config.getProperty(PROP_I2CP_PORT); }
public String getType() {
return _config.getProperty(PROP_TYPE);
}
public String getName() {
return _config.getProperty(PROP_NAME);
}
public String getDescription() {
return _config.getProperty(PROP_DESCR);
}
public String getI2CPHost() {
return _config.getProperty(PROP_I2CP_HOST);
}
public String getI2CPPort() {
return _config.getProperty(PROP_I2CP_PORT);
}
/**
* Absolute path to filter definition file
*
* @since 0.9.40
*/
public String getFilter() { return _config.getProperty(PROP_FILTER); }
public String getFilter() {
return _config.getProperty(PROP_FILTER);
}
/**
* Is it a client or server in the UI and I2P side?
* Note that a streamr client is a UI and I2P client but a server on the localhost side.
* Note that a streamr server is a UI and I2P server but a client on the localhost side.
* Note that a streamr client is a UI and I2P client but a server on the
* localhost side.
* Note that a streamr server is a UI and I2P server but a client on the
* localhost side.
*
* @since 0.9.17
*/
@ -1063,8 +1146,10 @@ public class TunnelController implements Logging {
/**
* Is it a client or server in the UI and I2P side?
* Note that a streamr client is a UI and I2P client but a server on the localhost side.
* Note that a streamr server is a UI and I2P server but a client on the localhost side.
* Note that a streamr client is a UI and I2P client but a server on the
* localhost side.
* Note that a streamr server is a UI and I2P server but a client on the
* localhost side.
*
* @since 0.9.17 moved from IndexBean
* @return false if type == null
@ -1076,6 +1161,7 @@ public class TunnelController implements Logging {
TYPE_SOCKS_IRC.equals(type) ||
TYPE_CONNECT.equals(type) ||
TYPE_STREAMR_CLIENT.equals(type) ||
TYPE_UDP_CLIENT.equals(type) ||
TYPE_IRC_CLIENT.equals(type);
}
@ -1093,26 +1179,48 @@ public class TunnelController implements Logging {
if (key.startsWith(PFX_OPTION)) {
key = key.substring(PFX_OPTION.length());
String val = (String) e.getValue();
if (opts.length() > 0) opts.append(' ');
if (opts.length() > 0)
opts.append(' ');
opts.append(key).append('=').append(val);
}
}
return opts.toString();
}
public String getListenOnInterface() { return _config.getProperty(PROP_INTFC); }
public String getTargetHost() { return _config.getProperty(PROP_TARGET_HOST); }
public String getTargetPort() { return _config.getProperty(PROP_TARGET_PORT); }
public String getSpoofedHost() { return _config.getProperty(PROP_SPOOFED_HOST); }
public String getListenOnInterface() {
return _config.getProperty(PROP_INTFC);
}
public String getTargetHost() {
return _config.getProperty(PROP_TARGET_HOST);
}
public String getTargetPort() {
return _config.getProperty(PROP_TARGET_PORT);
}
public String getSpoofedHost() {
return _config.getProperty(PROP_SPOOFED_HOST);
}
/**
* Probably not absolute. May be null. getPrivateKeyFile() recommended.
*/
public String getPrivKeyFile() { return _config.getProperty(PROP_FILE); }
public String getPrivKeyFile() {
return _config.getProperty(PROP_FILE);
}
public String getListenPort() { return _config.getProperty(PROP_LISTEN_PORT); }
public String getTargetDestination() { return _config.getProperty(PROP_DEST); }
public String getProxyList() { return _config.getProperty(PROP_PROXIES); }
public String getListenPort() {
return _config.getProperty(PROP_LISTEN_PORT);
}
public String getTargetDestination() {
return _config.getProperty(PROP_DEST);
}
public String getProxyList() {
return _config.getProperty(PROP_PROXIES);
}
/** default true for clients, always false for servers */
public String getSharedClient() {
@ -1122,11 +1230,17 @@ public class TunnelController implements Logging {
}
/** default true */
public boolean getStartOnLoad() { return Boolean.parseBoolean(_config.getProperty(PROP_START, "true")); }
public boolean getPersistentClientKey() { return Boolean.parseBoolean(_config.getProperty(OPT_PERSISTENT)); }
public boolean getStartOnLoad() {
return Boolean.parseBoolean(_config.getProperty(PROP_START, "true"));
}
public boolean getPersistentClientKey() {
return Boolean.parseBoolean(_config.getProperty(OPT_PERSISTENT));
}
/**
* Does not necessarily exist.
*
* @return absolute path or null if unset
* @since 0.9.17
*/
@ -1136,6 +1250,7 @@ public class TunnelController implements Logging {
/**
* Does not necessarily exist.
*
* @return absolute path or null if unset
* @since 0.9.30
*/
@ -1145,6 +1260,7 @@ public class TunnelController implements Logging {
/**
* Does not necessarily exist.
*
* @param f relative or absolute path, may be null
* @return absolute path or null
* @since 0.9.30
@ -1163,6 +1279,7 @@ public class TunnelController implements Logging {
/**
* Returns null if not running.
*
* @return Base64 or null
*/
public String getMyDestination() {
@ -1174,6 +1291,7 @@ public class TunnelController implements Logging {
/**
* Returns null if not running.
*
* @return "{52 chars}.b32.i2p" or null
*/
public String getMyDestHashBase32() {
@ -1185,6 +1303,7 @@ public class TunnelController implements Logging {
/**
* Returns null if not running.
*
* @return Destination or null
* @since 0.9.17
*/
@ -1201,6 +1320,7 @@ public class TunnelController implements Logging {
/**
* Returns false if not running.
*
* @return true if the primary session has offline keys
* @since 0.9.40
*/
@ -1213,6 +1333,7 @@ public class TunnelController implements Logging {
/**
* Returns false if not running.
*
* @return true if ANY session or subsession has offline keys
* @since 0.9.48
*/
@ -1230,8 +1351,13 @@ public class TunnelController implements Logging {
}
// TODO synch
public boolean getIsRunning() { return _state == TunnelState.RUNNING; }
public boolean getIsStarting() { return _state == TunnelState.START_ON_LOAD || _state == TunnelState.STARTING; }
public boolean getIsRunning() {
return _state == TunnelState.RUNNING;
}
public boolean getIsStarting() {
return _state == TunnelState.START_ON_LOAD || _state == TunnelState.STARTING;
}
/** if running but no open sessions, we are in standby */
public boolean getIsStandby() {
@ -1254,6 +1380,7 @@ public class TunnelController implements Logging {
/**
* A text description of the tunnel.
*
* @deprecated unused
*/
@Deprecated
@ -1261,111 +1388,136 @@ public class TunnelController implements Logging {
String type = getType();
buf.append(type);
/****
if ("httpclient".equals(type))
getHttpClientSummary(buf);
else if ("client".equals(type))
getClientSummary(buf);
else if ("server".equals(type))
getServerSummary(buf);
else if ("httpserver".equals(type))
getHttpServerSummary(buf);
else
buf.append("Unknown type ").append(type);
* if ("httpclient".equals(type))
* getHttpClientSummary(buf);
* else if ("client".equals(type))
* getClientSummary(buf);
* else if ("server".equals(type))
* getServerSummary(buf);
* else if ("httpserver".equals(type))
* getHttpServerSummary(buf);
* else
* buf.append("Unknown type ").append(type);
****/
}
/****
private void getHttpClientSummary(StringBuilder buf) {
String description = getDescription();
if ( (description != null) && (description.trim().length() > 0) )
buf.append("<i>").append(description).append("</i><br />\n");
buf.append("HTTP proxy listening on port ").append(getListenPort());
String listenOn = getListenOnInterface();
if ("0.0.0.0".equals(listenOn))
buf.append(" (reachable by any machine)");
else if ("127.0.0.1".equals(listenOn))
buf.append(" (reachable locally only)");
else
buf.append(" (reachable at the ").append(listenOn).append(" interface)");
buf.append("<br />\n");
String proxies = getProxyList();
if ( (proxies == null) || (proxies.trim().length() <= 0) )
buf.append("Outproxy: default [squid.i2p]<br />\n");
else
buf.append("Outproxy: ").append(proxies).append("<br />\n");
getOptionSummary(buf);
}
private void getClientSummary(StringBuilder buf) {
String description = getDescription();
if ( (description != null) && (description.trim().length() > 0) )
buf.append("<i>").append(description).append("</i><br />\n");
buf.append("Client tunnel listening on port ").append(getListenPort());
buf.append(" pointing at ").append(getTargetDestination());
String listenOn = getListenOnInterface();
if ("0.0.0.0".equals(listenOn))
buf.append(" (reachable by any machine)");
else if ("127.0.0.1".equals(listenOn))
buf.append(" (reachable locally only)");
else
buf.append(" (reachable at the ").append(listenOn).append(" interface)");
buf.append("<br />\n");
getOptionSummary(buf);
}
private void getServerSummary(StringBuilder buf) {
String description = getDescription();
if ( (description != null) && (description.trim().length() > 0) )
buf.append("<i>").append(description).append("</i><br />\n");
buf.append("Server tunnel pointing at port ").append(getTargetPort());
buf.append(" on ").append(getTargetHost());
buf.append("<br />\n");
buf.append("Private destination loaded from ").append(getPrivKeyFile()).append("<br />\n");
getOptionSummary(buf);
}
private void getHttpServerSummary(StringBuilder buf) {
String description = getDescription();
if ( (description != null) && (description.trim().length() > 0) )
buf.append("<i>").append(description).append("</i><br />\n");
buf.append("Server tunnel pointing at port ").append(getTargetPort());
buf.append(" on ").append(getTargetHost());
buf.append(" for the site ").append(getSpoofedHost());
buf.append("<br />\n");
buf.append("Private destination loaded from ").append(getPrivKeyFile()).append("<br />\n");
getOptionSummary(buf);
}
private void getOptionSummary(StringBuilder buf) {
String opts = getClientOptions();
if ( (opts != null) && (opts.length() > 0) )
buf.append("Network options: ").append(opts).append("<br />\n");
if (_running) {
List<I2PSession> sessions = _tunnel.getSessions();
for (int i = 0; i < sessions.size(); i++) {
I2PSession session = sessions.get(i);
Destination dest = session.getMyDestination();
if (dest != null) {
buf.append("Destination hash: ").append(dest.calculateHash().toBase64()).append("<br />\n");
if ( ("server".equals(getType())) || ("httpserver".equals(getType())) ) {
buf.append("Full destination: ");
buf.append("<input type=\"text\" size=\"10\" onclick=\"this.select();\" ");
buf.append("value=\"").append(dest.toBase64()).append("\" />\n");
long val = new Random().nextLong();
if (val < 0) val = 0 - val;
buf.append("<br />You can <a href=\"http://temp").append(val);
buf.append(".i2p/?i2paddresshelper=").append(dest.toBase64()).append("\">view</a>");
buf.append(" it in a browser (only when you're using the eepProxy)\n");
buf.append("<br />If you are going to share this on IRC, you need to split it up:<br />\n");
String str = dest.toBase64();
buf.append(str.substring(0, str.length()/2)).append("<br />\n");
buf.append(str.substring(str.length()/2)).append("<br />\n");
buf.append("You can also post it to <a href=\"http://forum.i2p/viewforum.php?f=16\">Eepsite announcement forum</a><br />");
}
}
}
}
}
* private void getHttpClientSummary(StringBuilder buf) {
* String description = getDescription();
* if ( (description != null) && (description.trim().length() > 0) )
* buf.append("<i>").append(description).append("</i><br />
* \n");
* buf.append("HTTP proxy listening on port ").append(getListenPort());
* String listenOn = getListenOnInterface();
* if ("0.0.0.0".equals(listenOn))
* buf.append(" (reachable by any machine)");
* else if ("127.0.0.1".equals(listenOn))
* buf.append(" (reachable locally only)");
* else
* buf.append(" (reachable at the ").append(listenOn).append(" interface)");
* buf.append("<br />
* \n");
* String proxies = getProxyList();
* if ( (proxies == null) || (proxies.trim().length() <= 0) )
* buf.append("Outproxy: default [squid.i2p]<br />
* \n");
* else
* buf.append("Outproxy: ").append(proxies).append("<br />
* \n");
* getOptionSummary(buf);
* }
*
* private void getClientSummary(StringBuilder buf) {
* String description = getDescription();
* if ( (description != null) && (description.trim().length() > 0) )
* buf.append("<i>").append(description).append("</i><br />
* \n");
* buf.append("Client tunnel listening on port ").append(getListenPort());
* buf.append(" pointing at ").append(getTargetDestination());
* String listenOn = getListenOnInterface();
* if ("0.0.0.0".equals(listenOn))
* buf.append(" (reachable by any machine)");
* else if ("127.0.0.1".equals(listenOn))
* buf.append(" (reachable locally only)");
* else
* buf.append(" (reachable at the ").append(listenOn).append(" interface)");
* buf.append("<br />
* \n");
* getOptionSummary(buf);
* }
*
* private void getServerSummary(StringBuilder buf) {
* String description = getDescription();
* if ( (description != null) && (description.trim().length() > 0) )
* buf.append("<i>").append(description).append("</i><br />
* \n");
* buf.append("Server tunnel pointing at port ").append(getTargetPort());
* buf.append(" on ").append(getTargetHost());
* buf.append("<br />
* \n");
* buf.append("Private destination loaded from
* ").append(getPrivKeyFile()).append("<br />
* \n");
* getOptionSummary(buf);
* }
*
* private void getHttpServerSummary(StringBuilder buf) {
* String description = getDescription();
* if ( (description != null) && (description.trim().length() > 0) )
* buf.append("<i>").append(description).append("</i><br />
* \n");
* buf.append("Server tunnel pointing at port ").append(getTargetPort());
* buf.append(" on ").append(getTargetHost());
* buf.append(" for the site ").append(getSpoofedHost());
* buf.append("<br />
* \n");
* buf.append("Private destination loaded from
* ").append(getPrivKeyFile()).append("<br />
* \n");
* getOptionSummary(buf);
* }
*
* private void getOptionSummary(StringBuilder buf) {
* String opts = getClientOptions();
* if ( (opts != null) && (opts.length() > 0) )
* buf.append("Network options: ").append(opts).append("<br />
* \n");
* if (_running) {
* List<I2PSession> sessions = _tunnel.getSessions();
* for (int i = 0; i < sessions.size(); i++) {
* I2PSession session = sessions.get(i);
* Destination dest = session.getMyDestination();
* if (dest != null) {
* buf.append("Destination hash:
* ").append(dest.calculateHash().toBase64()).append("<br />
* \n");
* if ( ("server".equals(getType())) || ("httpserver".equals(getType())) ) {
* buf.append("Full destination: ");
* buf.append("<input type=\"text\" size=\"10\" onclick=\"this.select();\" ");
* buf.append("value=\"").append(dest.toBase64()).append("\" />\n");
* long val = new Random().nextLong();
* if (val < 0) val = 0 - val;
* buf.append("<br />
* You can <a href=\"http://temp").append(val);
* buf.append(".i2p/?i2paddresshelper=").append(dest.toBase64()).append("\">view</a>");
* buf.append(" it in a browser (only when you're using the eepProxy)\n");
* buf.append("<br />
* If you are going to share this on IRC, you need to split it up:<br />
* \n");
* String str = dest.toBase64();
* buf.append(str.substring(0, str.length()/2)).append("<br />
* \n");
* buf.append(str.substring(str.length()/2)).append("<br />
* \n");
* buf.append("You can also post it to <a
* href=\"http://forum.i2p/viewforum.php?f=16\">Eepsite announcement
* forum</a><br />
* ");
* }
* }
* }
* }
* }
****/
/**
@ -1416,6 +1568,7 @@ public class TunnelController implements Logging {
/**
* caller must schedule
*
* @param f2 may be null
*/
public PKFChecker(File f, File f2) {
@ -1469,9 +1622,11 @@ public class TunnelController implements Logging {
// can't sign another LS
String msg;
if (remaining > 0)
msg = "Offline signature in private key file " + f + " for tunnel expires " + DataHelper.formatTime(exp) + ", stopping the tunnel!";
msg = "Offline signature in private key file " + f + " for tunnel expires "
+ DataHelper.formatTime(exp) + ", stopping the tunnel!";
else
msg = "Offline signature in private key file " + f + " for tunnel expired " + DataHelper.formatTime(exp) + ", stopping the tunnel!";
msg = "Offline signature in private key file " + f + " for tunnel expired "
+ DataHelper.formatTime(exp) + ", stopping the tunnel!";
_log.log(Log.CRIT, msg);
_tunnel.log(msg);
stopTunnel();
@ -1494,7 +1649,8 @@ public class TunnelController implements Logging {
}
}
if (remaining < 30 * 24 * 60 * 60 * 1000L) {
String msg = "Offline signature in private key file " + f + " for tunnel expires in " + DataHelper.formatDuration(remaining);
String msg = "Offline signature in private key file " + f + " for tunnel expires in "
+ DataHelper.formatDuration(remaining);
_log.logAlways(Log.WARN, msg);
_tunnel.log("WARNING: " + msg);
}

View File

@ -0,0 +1,199 @@
package net.i2p.i2ptunnel.udpTunnel;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import net.i2p.client.I2PSession;
import net.i2p.client.datagram.I2PDatagramDissector;
import net.i2p.data.DataFormatException;
import net.i2p.i2ptunnel.I2PTunnel;
import net.i2p.i2ptunnel.Logging;
import net.i2p.i2ptunnel.udp.I2PSink;
import net.i2p.i2ptunnel.udp.I2PSource;
//import net.i2p.i2ptunnel.streamr.Pinger;
import net.i2p.i2ptunnel.udp.UDPSink;
import net.i2p.i2ptunnel.udp.UDPSource;
import net.i2p.util.EventDispatcher;
import net.i2p.util.Log;
/**
*
* Client side(I2PTunnelUDPClient.java):
*
* - permanent DatagramSocket at e.g. localhost:5353
* - For EVERY incoming IP datagram request, assign a new I2CP source port,
* store the source IP/port in a table keyed by the I2CP source port
* - send a REPLIABLE datagram to the server with the I2CP source port
*
* Server side(I2PTunnelUDPServerClient.java):
*
* - receive request, store source I2P Dest/port associated with the request
* - For EVERY incoming I2P datagram request, open a NEW DatagramSocket on
* localhost with an EPHEMERAL port. Send the request out the socket and wait
* for a single reply.
* - Send reply as a RAW datagram to the stored I2P Dest/port. and CLOSE the
* DatagramSocket.
*
* Client side:
*
* - receive reply on the destination I2CP port. Look up source IP/port in the
* table by the destination I2CP port.
* - Send reply to the stored IP/port, and remove entry from table.
*
* @author idk
*/
public class I2PTunnelUDPClient extends I2PTunnelUDPClientBase {
private final Log _log = new Log(I2PTunnelUDPClient.class);
// UDP Side
// permanent DatagramSocket at e.g. localhost:5353
private final DatagramSocket _socket;
// InetAddress corresponding to local DatagramSocket
private final InetAddress UDP_HOSTNAME;
// UDP port corresponding to local DatagramSocket
private final int UDP_PORT;
private final int MAX_SIZE = 1024;
private final UDPSink _sink;
// SourceIP/Port table
private Map<Integer, InetSocketAddress> _sourceIPPortTable = new HashMap<>();
// Constructor, host is localhost(usually) or the host of the UDP client, port
// is the port of the UDP client
public I2PTunnelUDPClient(String host, int port, String destination, Logging l, EventDispatcher notifyThis,
I2PTunnel tunnel) {
// super(host, port,
super(destination, l, notifyThis, tunnel);
UDPSink sink = null;
UDP_PORT = port;
InetAddress udpHostname = null;
try {
udpHostname = InetAddress.getByName(host);
} catch (Exception e) {
if (_log.shouldLog(Log.WARN))
_log.warn("Failed to resolve hostname, using default(localhost): " + host, e);
udpHostname = null;
}
UDP_HOSTNAME = udpHostname;
DatagramSocket socket = null;
try {
socket = new DatagramSocket(UDP_PORT, UDP_HOSTNAME);
} catch (Exception e) {
socket = null;
}
_socket = socket;
if (!_socket.isBound()) {
if (_log.shouldLog(Log.WARN))
_log.warn("Failed to bind to UDP port: " + UDP_PORT);
_socket.close();
}
try {
sink = new UDPSink(socket, InetAddress.getByName(host), port);
} catch (Exception e) {
sink = null;
}
this._sink = sink;
// this._source = new UDPSource(this._sink.getSocket());
this.setSink(this._sink);
}
private int newSourcePort() {
int randomPort = (int) (Math.random() * 65535);
while (_sourceIPPortTable.containsKey(randomPort)) {
randomPort = (int) (Math.random() * 65535);
}
return randomPort;
}
private void sendRepliableI2PDatagram(DatagramPacket packet) {
try {
InetSocketAddress sourceIP = new InetSocketAddress(packet.getAddress(), packet.getPort());
int sourcePort = this.newSourcePort();
_sourceIPPortTable.put(sourcePort, sourceIP);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Added sourceIP/port to table: " + sourceIP.toString());
this.send(null, sourcePort, 0, packet.getData());
} catch (Exception e) {
if (_log.shouldLog(Log.WARN))
_log.warn("Failed to send UDP packet", e);
}
}
private DatagramPacket recieveRAWReplyPacket() {
DatagramPacket packet = new DatagramPacket(new byte[MAX_SIZE], MAX_SIZE);
try {
this._socket.receive(packet);
} catch (SocketTimeoutException ste) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Socket timeout, no packet received");
} catch (Exception e) {
if (_log.shouldLog(Log.WARN))
_log.warn("Failed to receive UDP packet", e);
}
return packet;
}
private final synchronized void send() {
while (true) {
DatagramPacket outboundpacket = new DatagramPacket(new byte[MAX_SIZE], MAX_SIZE);
try {
this._socket.receive(outboundpacket);
} catch (SocketTimeoutException ste) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Socket timeout, no packet received");
break;
} catch (Exception e) {
if (_log.shouldLog(Log.WARN))
_log.warn("Failed to receive UDP packet", e);
break;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received UDP packet on local address: " + outboundpacket.getAddress().toString()
+ ", Forwarding");
this.sendRepliableI2PDatagram(outboundpacket);
}
}
private final synchronized void receive() {
while (true) {
DatagramPacket packet = new DatagramPacket(new byte[MAX_SIZE], MAX_SIZE);
try {
this._socket.receive(packet);
} catch (SocketTimeoutException ste) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Socket timeout, no packet received");
break;
} catch (Exception e) {
if (_log.shouldLog(Log.WARN))
_log.warn("Failed to receive UDP packet", e);
break;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received UDP packet on local address: " + packet.getAddress().toString()
+ ", Forwarding");
this.sendRepliableI2PDatagram(packet);
}
}
@Override
public final void startRunning() {
super.startRunning();
while (true) {
this.send();
this.receive();
}
}
@Override
public boolean close(boolean forced) {
return super.close(forced);
}
}

View File

@ -147,11 +147,13 @@ import net.i2p.util.EventDispatcher;
* Classes should override to close UDP side as well
*/
public boolean close(boolean forced) {
if (!open) return true;
if (!open)
return true;
if (_session != null) {
try {
_session.destroySession();
} catch (I2PSessionException ise) {}
} catch (I2PSessionException ise) {
}
}
l.log("Closing client " + toString());
open = false;

View File

@ -0,0 +1,175 @@
package net.i2p.i2ptunnel.udpTunnel;
import java.io.File;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import net.i2p.i2ptunnel.I2PTunnel;
import net.i2p.i2ptunnel.Logging;
import net.i2p.i2ptunnel.streamr.Pinger;
import net.i2p.i2ptunnel.udp.UDPSink;
import net.i2p.i2ptunnel.udp.UDPSource;
import net.i2p.util.EventDispatcher;
import net.i2p.util.Log;
/**
*
* * Client side:
*
* - permanent DatagramSocket at e.g. localhost:5353
* - For EVERY incoming IP datagram request, assign a new I2CP source port,
* store the source IP/port in a table keyed by the I2CP source port
* - send a REPLIABLE datagram to the server with the I2CP source port
*
* Server side:
*
* - receive request, store source I2P Dest/port associated with the request
* - For EVERY incoming I2P datagram request, open a NEW DatagramSocket on
* localhost with an EPHEMERAL port. Send the request out the socket and wait
* for a single reply.
* - Send reply as a RAW datagram to the stored I2P Dest/port. and CLOSE the
* DatagramSocket.
*
* Client side:
*
* - receive reply on the destination I2CP port. Look up source IP/port in the
* table by the destination I2CP port.
* - Send reply to the stored IP/port, and remove entry from table.
*
* @author idk
*/
public class I2PTunnelUDPServerClient extends I2PTunnelUDPServerBase {
private final Log _log = new Log(I2PTunnelUDPServerClient.class);
private final UDPSink sink;
private final UDPSource source;
private final InetAddress UDP_HOSTNAME;
private final int UDP_PORT;
private final int MAX_SIZE = 1024;
public I2PTunnelUDPServerClient(String host, int port, File privkey, String privkeyname, Logging l,
EventDispatcher notifyThis,
I2PTunnel tunnel) {
super(privkey, privkeyname, l, notifyThis, tunnel);
// File privkey, String privkeyname, Logging l,EventDispatcher notifyThis,
// I2PTunnel tunnel
InetAddress _udpHostname = null;
try {
_udpHostname = InetAddress.getByName(host);
} catch (Exception e) {
if (_log.shouldLog(Log.WARN))
_log.warn("Failed to resolve hostname, using default(localhost): " + host, e);
try {
_udpHostname = InetAddress.getLocalHost();
} catch (Exception crite) {
if (_log.shouldLog(Log.ERROR))
_log.warn("Failed to resolve localhost, UDP tunnel will fail.: " + host, crite);
_udpHostname = null;
}
} finally {
if (_udpHostname == null) {
_log.error("Failed to resolve UDP hostname: " + host);
}
}
this.UDP_HOSTNAME = _udpHostname;
this.UDP_PORT = port;
this.sink = new UDPSink(this.UDP_HOSTNAME, this.UDP_PORT);
this.source = new UDPSource(this.UDP_PORT);
this.setSink(this.sink);
}
private DatagramPacket recieveRepliableDatagramFromClient() {
byte[] buf = new byte[MAX_SIZE];
DatagramPacket pack = new DatagramPacket(buf, buf.length);
DatagramSocket sock = null;
try {
sock = new DatagramSocket(0);
sock.receive(pack);
} catch (Exception e) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error receiving UDP datagram from client", e);
return null;
} finally {
// pack.getData()
try {
if (sock != null)
sock.close();
} catch (Exception e) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error closing UDP socket", e);
}
}
return pack;
}
private void sendRawDatagamToClient(DatagramPacket pack) {
DatagramSocket sock = null;
try {
sock = new DatagramSocket();
sock.send(pack);
} catch (Exception e) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error sending UDP datagram to client", e);
} finally {
// pack.getData()
try {
if (sock != null)
sock.close();
} catch (Exception e) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error closing UDP socket", e);
}
}
}
@Override
public final void startRunning() {
super.startRunning();
l.log("I2PTunnelUDPServer server ready");
while (true) {
DatagramPacket pack = recieveRepliableDatagramFromClient();
if (pack == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error receiving UDP datagram from client");
continue;
}
byte[] buf = pack.getData();
int len = pack.getLength();
if (len < 4) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error receiving UDP datagram from client, length is less than 4");
continue;
}
int port = ((buf[0] & 0xff) << 8) | (buf[1] & 0xff);
int ip1 = buf[2] & 0xff;
int ip2 = buf[3] & 0xff;
int ip3 = buf[4] & 0xff;
int ip4 = buf[5] & 0xff;
InetAddress ip = null;
try {
ip = InetAddress.getByAddress(new byte[] { (byte) ip1, (byte) ip2, (byte) ip3, (byte) ip4 });
} catch (Exception e) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error receiving UDP datagram from client, invalid IP address", e);
continue;
}
if (ip == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error receiving UDP datagram from client, invalid IP address");
continue;
}
if (_log.shouldLog(Log.INFO))
_log.info("Received UDP datagram from client: " + ip + ":" + port);
DatagramPacket reply = new DatagramPacket(buf, len, ip, port);
sendRawDatagamToClient(reply);
}
// send subscribe-message
}
@Override
public boolean close(boolean forced) {
// send unsubscribe-message
this.sink.stop();
return super.close(forced);
}
}

View File

@ -82,8 +82,10 @@ public class GeneralHelper {
* @return null if not found or tcg is null
*/
public static TunnelController getController(TunnelControllerGroup tcg, int tunnel) {
if (tunnel < 0) return null;
if (tcg == null) return null;
if (tunnel < 0)
return null;
if (tcg == null)
return null;
List<TunnelController> controllers = tcg.getControllers();
if (controllers.size() > tunnel)
return controllers.get(tunnel);
@ -105,7 +107,8 @@ public class GeneralHelper {
*
* @param context unused, taken from tcg
*/
public static List<String> saveTunnel(I2PAppContext context, TunnelControllerGroup tcg, int tunnel, TunnelConfig config) {
public static List<String> saveTunnel(I2PAppContext context, TunnelControllerGroup tcg, int tunnel,
TunnelConfig config) {
List<String> msgs = new ArrayList<String>();
TunnelController cur = updateTunnelConfig(tcg, tunnel, config, msgs);
msgs.addAll(saveConfig(tcg, cur));
@ -113,8 +116,10 @@ public class GeneralHelper {
}
/**
* Update the config and if shared, adjust and save the config of other shared clients.
* If a new tunnel, this will call tcg.addController(), and start it if so configured.
* Update the config and if shared, adjust and save the config of other shared
* clients.
* If a new tunnel, this will call tcg.addController(), and start it if so
* configured.
* This does NOT save this tunnel's config. Caller must call saveConfig() also.
*/
protected static List<String> updateTunnelConfig(TunnelControllerGroup tcg, int tunnel, TunnelConfig config) {
@ -124,15 +129,18 @@ public class GeneralHelper {
}
/**
* Update the config and if shared, adjust and save the config of other shared clients.
* If a new tunnel, this will call tcg.addController(), and start it if so configured.
* Update the config and if shared, adjust and save the config of other shared
* clients.
* If a new tunnel, this will call tcg.addController(), and start it if so
* configured.
* This does NOT save this tunnel's config. Caller must call saveConfig() also.
*
* @param msgs out parameter, messages will be added
* @return the old or new controller, non-null.
* @since 0.9.49
*/
private static TunnelController updateTunnelConfig(TunnelControllerGroup tcg, int tunnel, TunnelConfig config, List<String> msgs) {
private static TunnelController updateTunnelConfig(TunnelControllerGroup tcg, int tunnel, TunnelConfig config,
List<String> msgs) {
// Get current tunnel controller
TunnelController cur = getController(tcg, tunnel);
@ -141,7 +149,8 @@ public class GeneralHelper {
String type = props.getProperty(TunnelController.PROP_TYPE);
if (TunnelController.TYPE_STD_CLIENT.equals(type) || TunnelController.TYPE_IRC_CLIENT.equals(type)) {
//
// If we switch to SSL, create the keystore here, so we can store the new properties.
// If we switch to SSL, create the keystore here, so we can store the new
// properties.
// Down in I2PTunnelClientBase it's very hard to save the config.
//
if (Boolean.parseBoolean(props.getProperty(OPT + I2PTunnelClientBase.PROP_USE_SSL))) {
@ -199,7 +208,8 @@ public class GeneralHelper {
TunnelController c = controllers.get(i);
// Current tunnel modified by user, skip
if (c == cur) continue;
if (c == cur)
continue;
// Only modify this non-current tunnel
// if it belongs to a shared destination, and is of supported type
@ -221,6 +231,7 @@ public class GeneralHelper {
* I2CP/Dest/LS options affecting shared client tunnels.
* Streaming options should not be here, each client gets its own SocketManger.
* All must be prefixed with "option."
*
* @since 0.9.46
*/
private static final String[] SHARED_OPTIONS = {
@ -236,6 +247,7 @@ public class GeneralHelper {
/**
* Copy relevant options over
*
* @since 0.9.46 pulled out of updateTunnelConfig
*/
private static void copySharedOptions(TunnelConfig fromConfig, Properties from,
@ -301,6 +313,7 @@ public class GeneralHelper {
public List<String> deleteTunnel(int tunnel, String privKeyFile) {
return deleteTunnel(_context, _group, tunnel, privKeyFile);
}
/**
* Stop the tunnel, delete from config,
* rename the private key file if in the default directory
@ -493,23 +506,29 @@ public class GeneralHelper {
public int getTunnelStatus(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun == null) return NOT_RUNNING;
if (tun == null)
return NOT_RUNNING;
if (tun.getIsRunning()) {
if (tun.isClient() && tun.getIsStandby())
return STANDBY;
else
return RUNNING;
} else if (tun.getIsStarting()) return STARTING;
else return NOT_RUNNING;
} else if (tun.getIsStarting())
return STARTING;
else
return NOT_RUNNING;
}
public String getClientDestination(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun == null) return "";
if (tun == null)
return "";
String rv;
if (TunnelController.TYPE_STD_CLIENT.equals(tun.getType()) ||
TunnelController.TYPE_IRC_CLIENT.equals(tun.getType()) ||
TunnelController.TYPE_STREAMR_CLIENT.equals(tun.getType()))
TunnelController.TYPE_STREAMR_CLIENT.equals(tun.getType()) ||
TunnelController.TYPE_UDP_CLIENT.equals(tun.getType()) ||
TunnelController.TYPE_UDP_SERVER.equals(tun.getType()))
rv = tun.getTargetDestination();
else
rv = tun.getProxyList();
@ -518,6 +537,7 @@ public class GeneralHelper {
/**
* Works even if tunnel is not running.
*
* @return Destination or null
*/
public Destination getDestination(int tunnel) {
@ -535,7 +555,8 @@ public class GeneralHelper {
if (rv != null)
return rv;
} catch (I2PException e) {
} catch (IOException e) {}
} catch (IOException e) {
}
}
}
return null;
@ -543,6 +564,7 @@ public class GeneralHelper {
/**
* Works even if tunnel is not running.
*
* @return Destination or null
* @since 0.9.30
*/
@ -558,7 +580,8 @@ public class GeneralHelper {
if (rv != null)
return rv;
} catch (I2PException e) {
} catch (IOException e) {}
} catch (IOException e) {
}
}
}
return null;
@ -566,6 +589,7 @@ public class GeneralHelper {
/**
* Works even if tunnel is not running.
*
* @return true if offline keys
* @since 0.9.40
*/
@ -724,6 +748,7 @@ public class GeneralHelper {
/**
* List of b64 name : b64key
* Pubkeys for DH, privkeys for PSK
*
* @param isDH true for DH, false for PSK
* @return non-null
* @since 0.9.41
@ -772,6 +797,7 @@ public class GeneralHelper {
TunnelController.TYPE_SOCKS_IRC.equals(ttype) ||
TunnelController.TYPE_SOCKS.equals(ttype) ||
TunnelController.TYPE_STREAMR_CLIENT.equals(ttype) ||
TunnelController.TYPE_UDP_CLIENT.equals(ttype) ||
TunnelController.TYPE_STD_CLIENT.equals(ttype) ||
TunnelController.TYPE_CONNECT.equals(ttype) ||
TunnelController.TYPE_HTTP_CLIENT.equals(ttype))
@ -804,7 +830,8 @@ public class GeneralHelper {
dflt = "4,0";
} else if (TunnelController.TYPE_HTTP_SERVER.equals(type) ||
TunnelController.TYPE_IRC_SERVER.equals(type) ||
TunnelController.TYPE_STREAMR_SERVER.equals(type)) {
TunnelController.TYPE_STREAMR_SERVER.equals(type) ||
TunnelController.TYPE_UDP_SERVER.equals(type)) {
dflt = "4,0";
} else {
dflt = "0";
@ -997,7 +1024,8 @@ public class GeneralHelper {
}
public int getTotalMinute(int tunnel) {
return getProperty(tunnel, TunnelController.PROP_MAX_TOTAL_CONNS_MIN, TunnelController.DEFAULT_MAX_TOTAL_CONNS_MIN);
return getProperty(tunnel, TunnelController.PROP_MAX_TOTAL_CONNS_MIN,
TunnelController.DEFAULT_MAX_TOTAL_CONNS_MIN);
}
public int getTotalHour(int tunnel) {
@ -1014,6 +1042,7 @@ public class GeneralHelper {
/**
* POST limits
*
* @since 0.9.9
*/
public int getPostMax(int tunnel) {
@ -1029,11 +1058,13 @@ public class GeneralHelper {
}
public int getPostBanTime(int tunnel) {
return getProperty(tunnel, I2PTunnelHTTPServer.OPT_POST_BAN_TIME, I2PTunnelHTTPServer.DEFAULT_POST_BAN_TIME) / 60;
return getProperty(tunnel, I2PTunnelHTTPServer.OPT_POST_BAN_TIME, I2PTunnelHTTPServer.DEFAULT_POST_BAN_TIME)
/ 60;
}
public int getPostTotalBanTime(int tunnel) {
return getProperty(tunnel, I2PTunnelHTTPServer.OPT_POST_TOTAL_BAN_TIME, I2PTunnelHTTPServer.DEFAULT_POST_TOTAL_BAN_TIME) / 60;
return getProperty(tunnel, I2PTunnelHTTPServer.OPT_POST_TOTAL_BAN_TIME,
I2PTunnelHTTPServer.DEFAULT_POST_TOTAL_BAN_TIME) / 60;
}
public boolean getRejectInproxy(int tunnel) {
@ -1063,7 +1094,8 @@ public class GeneralHelper {
TunnelController tun = getController(tunnel);
if (tun != null) {
Properties opts = tun.getClientOptionProps();
if (opts == null) return "";
if (opts == null)
return "";
boolean isMD5Proxy = TunnelController.TYPE_HTTP_CLIENT.equals(tun.getType()) ||
TunnelController.TYPE_CONNECT.equals(tun.getType());
Map<String, String> sorted = new TreeMap<String, String>();
@ -1113,10 +1145,12 @@ public class GeneralHelper {
Properties opts = tun.getClientOptionProps();
if (opts != null) {
String s = opts.getProperty(prop);
if (s == null) return def;
if (s == null)
return def;
try {
return Integer.parseInt(s);
} catch (NumberFormatException nfe) {}
} catch (NumberFormatException nfe) {
}
}
}
return def;

View File

@ -107,6 +107,7 @@ public class TunnelConfig {
public void setType(String type) {
_type = (type != null ? type.trim() : null);
}
public String getType() {
return _type;
}
@ -115,70 +116,85 @@ public class TunnelConfig {
public void setName(String name) {
_name = (name != null ? name.trim() : null);
}
/** one line description */
public void setDescription(String description) {
// '#' will blow up DataHelper.storeProps()
_description = (description != null ? description.replace('#', ' ').trim() : null);
}
/** I2CP host the router is on, ignored when in router context */
public void setClientHost(String host) {
_i2cpHost = (host != null ? host.trim() : null);
}
/** I2CP port the router is on, ignored when in router context */
public void setClientPort(String port) {
_i2cpPort = (port != null ? port.trim() : null);
}
/** how many hops to use for inbound tunnels
/**
* how many hops to use for inbound tunnels
* In or both in/out
*/
public void setTunnelDepth(int tunnelDepth) {
_tunnelDepth = tunnelDepth;
}
/** how many parallel inbound tunnels to use
/**
* how many parallel inbound tunnels to use
* In or both in/out
*/
public void setTunnelQuantity(int tunnelQuantity) {
_tunnelQuantity = tunnelQuantity;
}
/** how much randomisation to apply to the depth of tunnels
/**
* how much randomisation to apply to the depth of tunnels
* In or both in/out
*/
public void setTunnelVariance(int tunnelVariance) {
_tunnelVariance = tunnelVariance;
}
/** how many tunnels to hold in reserve to guard against failures
/**
* how many tunnels to hold in reserve to guard against failures
* In or both in/out
*/
public void setTunnelBackupQuantity(int tunnelBackupQuantity) {
_tunnelBackupQuantity = tunnelBackupQuantity;
}
/** how many hops to use for outbound tunnels
/**
* how many hops to use for outbound tunnels
*
* @since 0.9.33
*/
public void setTunnelDepthOut(int tunnelDepth) {
_tunnelDepthOut = tunnelDepth;
}
/** how many parallel outbound tunnels to use
/**
* how many parallel outbound tunnels to use
*
* @since 0.9.33
*/
public void setTunnelQuantityOut(int tunnelQuantity) {
_tunnelQuantityOut = tunnelQuantity;
}
/** how much randomisation to apply to the depth of tunnels
/**
* how much randomisation to apply to the depth of tunnels
*
* @since 0.9.33
*/
public void setTunnelVarianceOut(int tunnelVariance) {
_tunnelVarianceOut = tunnelVariance;
}
/** how many tunnels to hold in reserve to guard against failures
/**
* how many tunnels to hold in reserve to guard against failures
*
* @since 0.9.33
*/
public void setTunnelBackupQuantityOut(int tunnelBackupQuantity) {
@ -189,46 +205,56 @@ public class TunnelConfig {
public void setCustomOptions(String customOptions) {
_customOptions = (customOptions != null ? customOptions.trim() : null);
}
/** what HTTP outproxies should be used (httpclient specific) */
public void setProxyList(String proxyList) {
_proxyList = (proxyList != null ? proxyList.trim() : null);
}
/** what port should this client/httpclient/ircclient listen on */
public void setPort(int port) {
_port = port;
}
/**
* what interface should this client/httpclient/ircclient listen on
*/
public void setReachableBy(String reachableBy) {
_reachableBy = (reachableBy != null ? reachableBy.trim() : null);
}
/** What peer does this client tunnel point at */
public void setTargetDestination(String dest) {
_targetDestination = (dest != null ? dest.trim() : null);
}
/** What host does this server tunnel point at */
public void setTargetHost(String host) {
_targetHost = (host != null ? host.trim() : null);
}
/** What port does this server tunnel point at */
public void setTargetPort(int port) {
_targetPort = port;
}
/** What host does this http server tunnel spoof */
public void setSpoofedHost(String host) {
_spoofedHost = (host != null ? host.trim() : null);
}
/** What filename is this server tunnel's private keys stored in */
public void setPrivKeyFile(String file) {
_privKeyFile = (file != null ? file.trim() : null);
}
public String getPrivKeyFile() {
return _privKeyFile;
}
/**
* What filename is this server tunnel's alternate private keys stored in
*
* @since 0.9.30
*/
public void setAltPrivKeyFile(String file) {
@ -243,12 +269,15 @@ public class TunnelConfig {
public void setStartOnLoad(boolean val) {
_startOnLoad = val;
}
public void setShared(boolean val) {
_sharedClient = val;
}
public void setConnectDelay(boolean val) {
_connectDelay = val;
}
public void setProfile(String profile) {
_profile = profile;
}
@ -259,12 +288,14 @@ public class TunnelConfig {
else
_booleanOptions.remove("i2cp.reduceOnIdle");
}
public void setClose(boolean val) {
if (val)
_booleanOptions.add("i2cp.closeOnIdle");
else
_booleanOptions.remove("i2cp.closeOnIdle");
}
public void setEncrypt(boolean val) {
if (val)
_booleanOptions.add("i2cp.encryptLeaseSet");
@ -288,6 +319,7 @@ public class TunnelConfig {
/**
* Multiple entries in form
*
* @since 0.9.41
*/
public void addClientNames(String[] s) {
@ -297,6 +329,7 @@ public class TunnelConfig {
/**
* Multiple entries in form
* Handles either order addClientName/addClientKey
*
* @since 0.9.41
*/
public void addClientKeys(String[] s) {
@ -305,6 +338,7 @@ public class TunnelConfig {
/**
* Multiple entries in form
*
* @since 0.9.41
*/
public void revokeClients(String[] s) {
@ -312,12 +346,14 @@ public class TunnelConfig {
for (String k : s) {
try {
_clientRevocations.add(Integer.valueOf(Integer.parseInt(k)));
} catch (NumberFormatException nfe) {}
} catch (NumberFormatException nfe) {
}
}
}
/**
* Handles either order newClientName/newClientKey
*
* @since 0.9.41
*/
public void newClientName(String s) {
@ -326,6 +362,7 @@ public class TunnelConfig {
/**
* Handles either order newClientName/newClientKey
*
* @since 0.9.41
*/
public void newClientKey(String s) {
@ -345,12 +382,14 @@ public class TunnelConfig {
else
_booleanOptions.remove(I2PTunnelIRCClient.PROP_DCC);
}
public void setUseSSL(boolean val) {
if (val)
_booleanOptions.add(I2PTunnelServer.PROP_USE_SSL);
else
_booleanOptions.remove(I2PTunnelServer.PROP_USE_SSL);
}
public void setRejectInproxy(boolean val) {
if (val)
_booleanOptions.add(I2PTunnelHTTPServer.OPT_REJECT_INPROXY);
@ -437,7 +476,8 @@ public class TunnelConfig {
* <p>
* If {@link #setClose(boolean)} is set to false then mode 1 == mode 0.
*
* @param mode 0 for new dest on restart, 1 for new dest on resume from idle, 2 for persistent key
* @param mode 0 for new dest on restart, 1 for new dest on resume from idle, 2
* for persistent key
*/
public void setNewDest(int mode) {
switch (mode) {
@ -458,9 +498,11 @@ public class TunnelConfig {
public void setReduceTime(int val) {
_otherOptions.put("i2cp.reduceIdleTime", Integer.toString(val * 60 * 1000));
}
public void setReduceCount(int val) {
_otherOptions.put("i2cp.reduceQuantity", Integer.toString(val));
}
public void setEncryptKey(String val) {
if (val != null)
_otherOptions.put("i2cp.leaseSetKey", val.trim());
@ -492,7 +534,8 @@ public class TunnelConfig {
public void setJumpList(String val) {
if (val != null)
_otherOptions.put(I2PTunnelHTTPClient.PROP_JUMP_SERVERS, val.trim().replace("\r\n", ",").replace("\n", ",").replace(" ", ","));
_otherOptions.put(I2PTunnelHTTPClient.PROP_JUMP_SERVERS,
val.trim().replace("\r\n", ",").replace("\n", ",").replace(" ", ","));
}
public void setCloseTime(int val) {
@ -505,18 +548,21 @@ public class TunnelConfig {
else
_booleanOptions.remove(I2PTunnelHTTPClient.PROP_USER_AGENT);
}
public void setAllowReferer(boolean val) {
if (val)
_booleanOptions.add(I2PTunnelHTTPClient.PROP_REFERER);
else
_booleanOptions.remove(I2PTunnelHTTPClient.PROP_REFERER);
}
public void setAllowAccept(boolean val) {
if (val)
_booleanOptions.add(I2PTunnelHTTPClient.PROP_ACCEPT);
else
_booleanOptions.remove(I2PTunnelHTTPClient.PROP_ACCEPT);
}
public void setAllowInternalSSL(boolean val) {
if (val)
_booleanOptions.add(I2PTunnelHTTPClient.PROP_INTERNAL_SSL);
@ -656,6 +702,7 @@ public class TunnelConfig {
/**
* Adds to existing, comma separated
*
* @since 0.9.44
*/
public void setEncType(String val) {
@ -711,7 +758,8 @@ public class TunnelConfig {
updateConfigGeneric(config);
if ((TunnelController.isClient(_type) && !TunnelController.TYPE_STREAMR_CLIENT.equals(_type)) ||
TunnelController.TYPE_STREAMR_SERVER.equals(_type)) {
!TunnelController.TYPE_UDP_CLIENT.equals(_type) || TunnelController.TYPE_STREAMR_SERVER.equals(_type) ||
TunnelController.TYPE_UDP_SERVER.equals(_type)) {
// streamrserver uses interface
if (_reachableBy != null)
config.setProperty(TunnelController.PROP_INTFC, _reachableBy);
@ -815,7 +863,8 @@ public class TunnelConfig {
if (TunnelController.TYPE_IRC_CLIENT.equals(_type) ||
TunnelController.TYPE_STD_CLIENT.equals(_type) ||
TunnelController.TYPE_STREAMR_CLIENT.equals(_type)) {
TunnelController.TYPE_STREAMR_CLIENT.equals(_type) ||
TunnelController.TYPE_UDP_CLIENT.equals(_type)) {
if (_targetDestination != null)
config.setProperty(TunnelController.PROP_DEST, _targetDestination);
} else if (TunnelController.TYPE_HTTP_SERVER.equals(_type) ||
@ -826,7 +875,9 @@ public class TunnelConfig {
if (_otherOptions.containsKey(p))
config.setProperty(OPT + p, _otherOptions.get(p));
}
if (TunnelController.TYPE_HTTP_BIDIR_SERVER.equals(_type)) {
if (TunnelController.TYPE_HTTP_BIDIR_SERVER.equals(_type) ||
TunnelController.TYPE_UDP_SERVER.equals(_type) ||
TunnelController.TYPE_UDP_CLIENT.equals(_type)) {
if (_port >= 0)
config.setProperty(TunnelController.PROP_LISTEN_PORT, Integer.toString(_port));
if (_reachableBy != null)
@ -927,6 +978,7 @@ public class TunnelConfig {
/**
* Servers only.
*
* @since 0.9.41 pulled out from getConfig() above
*/
private void processEncryptMode(Properties config) {
@ -1053,7 +1105,8 @@ public class TunnelConfig {
if (name.length() > 0)
name = Base64.encode(DataHelper.getUTF8(name));
else
name = Base64.encode(DataHelper.getUTF8(GeneralHelper._t("Client", _context) + ' ' + (clientAuth.size() + 1)));
name = Base64.encode(
DataHelper.getUTF8(GeneralHelper._t("Client", _context) + ' ' + (clientAuth.size() + 1)));
String key;
if (_encryptMode == 6 || _encryptMode == 7) {
byte[] b = new byte[32];
@ -1080,6 +1133,7 @@ public class TunnelConfig {
/**
* Servers only.
* Also sets/clears i2cp.leaseSetType
*
* @since 0.9.41
*/
private void addLeaseSetPrivKey(Properties config, boolean isBlinded) {
@ -1164,7 +1218,8 @@ public class TunnelConfig {
};
/**
* do NOT add these to noShoOpts, we must leave them in for HTTPClient and ConnectCLient
* do NOT add these to noShoOpts, we must leave them in for HTTPClient and
* ConnectCLient
* so they will get migrated to MD5
* TODO migrate socks to MD5
*/
@ -1238,7 +1293,8 @@ public class TunnelConfig {
// This was 1 which doesn't make much sense
// The real way to make it interactive is to make the streaming lib
// MessageInputStream flush faster but there's no option for that yet,
// Setting it to 16 instead of the default but not sure what good that is either.
// Setting it to 16 instead of the default but not sure what good that is
// either.
config.setProperty("option.i2p.streaming.maxWindowSize", "16");
else
config.remove("option.i2p.streaming.maxWindowSize");
@ -1361,33 +1417,33 @@ public class TunnelConfig {
}
/****
private static String[] tests = {
"", "foo", "foo=bar",
"f=b x", "x f=b",
" aaa=bbb ccc=ddd ",
"aaa=bbb ccc=ddd x",
"aaa=bbb ccc=ddd x=",
"a=\"w x y z\" b c= d='1 2 3 4'",
"klsjdf owi=\"w\tx y\tz\"",
"z= aaa= ",
"=", " = ", "=foo", " =fpp ",
"a=\"\", b='', c='xxx\" d='aaa'",
"xx=\"missingquote",
"'zxw=123'",
"'zxw=123",
"'zxw=123' a=b c d e",
"x====", "x====x",
"aaa=b=cc====dddddd====",
};
public static void main(String[] args) {
for (int i = 0; i < tests.length; i++) {
Map<String, String> m = parseCustomOptions(tests[i]);
System.out.println("\nTest \"" + tests[i] + '"');
for (Map.Entry<String, String> e : m.entrySet()) {
System.out.println(" \"" + e.getKey() + "\" = \"" + e.getValue() + '"');
}
}
}
* private static String[] tests = {
* "", "foo", "foo=bar",
* "f=b x", "x f=b",
* " aaa=bbb ccc=ddd ",
* "aaa=bbb ccc=ddd x",
* "aaa=bbb ccc=ddd x=",
* "a=\"w x y z\" b c= d='1 2 3 4'",
* "klsjdf owi=\"w\tx y\tz\"",
* "z= aaa= ",
* "=", " = ", "=foo", " =fpp ",
* "a=\"\", b='', c='xxx\" d='aaa'",
* "xx=\"missingquote",
* "'zxw=123'",
* "'zxw=123",
* "'zxw=123' a=b c d e",
* "x====", "x====x",
* "aaa=b=cc====dddddd====",
* };
*
* public static void main(String[] args) {
* for (int i = 0; i < tests.length; i++) {
* Map<String, String> m = parseCustomOptions(tests[i]);
* System.out.println("\nTest \"" + tests[i] + '"');
* for (Map.Entry<String, String> e : m.entrySet()) {
* System.out.println(" \"" + e.getKey() + "\" = \"" + e.getValue() + '"');
* }
* }
* }
****/
}

View File

@ -75,7 +75,7 @@
String phdisabled = (canChangePort && isShared) ? "" : tstopFirst;
%>
<th colspan="2" <%=phdisabled%>>
<% if ("streamrclient".equals(tunnelType)) { %>
<% if ("streamrclient".equals(tunnelType) || "udpclient".equals(tunnelType)) { %>
<%=intl._t("Target")%>
<% } else { %>
<%=intl._t("Access Point")%>
@ -98,7 +98,7 @@
<input type="text" size="6" maxlength="5" name="port" title="<%=ptext%>" value="<%=editBean.getClientPort(curTunnel)%>" class="freetext port" placeholder="required" <%=pdisabled%>/>
</td>
<%
if ("streamrclient".equals(tunnelType)) {
if ("streamrclient".equals(tunnelType) || "udpclient".equals(tunnelType)) {
%>
<td>
<b><%=intl._t("Host")%>:</b>
@ -192,7 +192,7 @@
</td>
</tr>
<%
} else if ("client".equals(tunnelType) || "ircclient".equals(tunnelType) || "streamrclient".equals(tunnelType)) {
} else if ("client".equals(tunnelType) || "ircclient".equals(tunnelType) || "streamrclient".equals(tunnelType) || "udpclient".equals(tunnelType)) {
%>
<tr>
<th colspan="2">
@ -210,7 +210,7 @@
<input type="text" size="30" id="targetDestination" name="targetDestination" title="<%=intl._t("Specify the .i2p address or destination (b32 or b64) of the tunnel here.")%>&nbsp;<%=intl._t("For a random selection from a pool, separate with commas e.g. server1.i2p,server2.i2p")%>" value="<%=editBean.getClientDestination(curTunnel)%>" class="freetext destination" placeholder="required" />
<%=intl._t("name, name:port, or destination")%>
<%
if ("streamrclient".equals(tunnelType)) {
if ("streamrclient".equals(tunnelType) || "udpclient".equals(tunnelType)) {
/* deferred resolution unimplemented in streamr client */
%>
- <%=intl._t("b32 not recommended")%>
@ -222,7 +222,7 @@
<%
}
if (!"streamrclient".equals(tunnelType)) {
if (!"streamrclient".equals(tunnelType) && !"udpclient".equals(tunnelType)) {
%>
<tr>
<th colspan="2" <%=phdisabled%>>
@ -270,7 +270,7 @@
</table>
<h3><%=intl._t("Advanced networking options")%></h3>
<%
if (!"streamrclient".equals(tunnelType) && (canChangePort || isShared)) {
if (!"streamrclient".equals(tunnelType) && !"udpclient".equals(tunnelType) && (canChangePort || isShared)) {
// no shared client tunnels for streamr
// If running and not shared, this doesn't apply.
%>
@ -393,7 +393,7 @@
} // client
%>
<%
if (!"streamrclient".equals(tunnelType)) {
if (!"streamrclient".equals(tunnelType) && !"udpclient".equals(tunnelType)) {
// streamr client sends pings so it will never be idle
%>
<tr>

View File

@ -66,8 +66,9 @@
String tstopFirst = " title=\"" + stopFirst + "\" ";
boolean canChangeDest = editBean.canChangePort(curTunnel);
boolean isStreamrServer = "streamrserver".equals(tunnelType);
boolean isUDPServer = "udpserver".equals(tunnelType);
boolean isBidirServer = "httpbidirserver".equals(tunnelType);
boolean canChangePort = canChangeDest || !(isStreamrServer || isBidirServer);
boolean canChangePort = canChangeDest || !(isStreamrServer || isBidirServer || isUDPServer);
String phdisabled = canChangePort ? "" : tstopFirst;
%>
<th colspan="2" <%=phdisabled%>>
@ -101,7 +102,7 @@
String pdisabled = canChangePort ? "" : " readonly=\"readonly\" ";
%>
<input type="text" size="6" maxlength="5" id="targetPort" name="targetPort" title="<%=ptext%>" value="<%=editBean.getTargetPort(curTunnel)%>" class="freetext port" placeholder="required" <%=pdisabled%>/>
<% if (!isStreamrServer) { %>
<% if (!isStreamrServer && !isUDPServer) { %>
<label title="<%=intl._t("To avoid traffic sniffing if connecting to a remote server, you can enable an SSL connection. Note that the target server must be configured to accept SSL connections.")%>"><input value="1" type="checkbox" name="useSSL"<%=(editBean.isSSLEnabled(curTunnel) ? " checked=\"checked\"" : "")%> class="tickbox" />
<%=intl._t("Use SSL to connect to target")%></label>
<% } /* !streamrserver */ %>
@ -155,7 +156,7 @@
</td>
<% } /* httpbidirserver || streamrserver */ %>
</tr>
<% if ("httpserver".equals(tunnelType) || isBidirServer) {
<% if ("httpserver".equals(tunnelType) || isBidirServer || isUDPServer) {
%>
<tr>
<th>
@ -717,7 +718,7 @@
<%
/* alternate dest, only if current dest is set and is DSA_SHA1 */
if (currentSigType == 0 && !"".equals(b64) && !isStreamrServer) {
if (currentSigType == 0 && !"".equals(b64) && (!isStreamrServer && !isUDPServer)) {
String attitle = canChangeEncType ? "" : tstopFirst;
String atitle = canChangeEncType ? intl._t("Path to Private Key File") : stopFirst;
String adisabled = canChangeEncType ? "" : " readonly=\"readonly\" ";
@ -867,7 +868,7 @@
</th>
</tr>
<%
if (!isStreamrServer) {
if (!isStreamrServer && !isUDPServer) {
%>
<tr>
<th colspan="5">

View File

@ -1,27 +1,42 @@
<%@include file="headers.jsi"
%><%@page pageEncoding="UTF-8"
%><%@page trimDirectiveWhitespaces="true"
%><%@page contentType="text/html" import="net.i2p.i2ptunnel.web.IndexBean"
%><?xml version="1.0" encoding="UTF-8"?>
%>
<%@page pageEncoding="UTF-8"
%>
<%@page trimDirectiveWhitespaces="true"
%>
<%@page contentType="text/html" import="net.i2p.i2ptunnel.web.IndexBean"
%>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<jsp:useBean class="net.i2p.i2ptunnel.web.IndexBean" id="indexBean" scope="request" />
<jsp:setProperty name="indexBean" property="tunnel" /><%-- must be set before key1-4 --%>
<jsp:setProperty name="indexBean" property="tunnel" />
<%-- must be set before key1-4 --%>
<jsp:setProperty name="indexBean" property="*" />
<jsp:useBean class="net.i2p.i2ptunnel.ui.Messages" id="intl" scope="request" />
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title><%=intl._t("Hidden Services Manager")%></title>
<title>
<%=intl._t("Hidden Services Manager")%>
</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="/themes/console/images/favicon.ico" type="image/x-icon" rel="shortcut icon" />
<link href="<%=indexBean.getTheme()%>i2ptunnel.css?<%=net.i2p.CoreVersion.VERSION%>" rel="stylesheet" type="text/css" />
<script src="js/copy.js?<%=net.i2p.CoreVersion.VERSION%>" type="text/javascript"></script>
<noscript><style> .jsonly { display: none } </style></noscript>
</head><body id="tunnelListPage">
<div class="panel" id="overview"><h2><%=intl._t("Hidden Services Manager")%></h2><p>
</head>
<body id="tunnelListPage">
<div class="panel" id="overview">
<h2>
<%=intl._t("Hidden Services Manager")%>
</h2>
<p>
<%=intl._t("These are the local services provided by your router.")%>
&nbsp;
<%=intl._t("By default, most of your client services (email, HTTP proxy, IRC) will share the same set of tunnels and be listed as \"Shared Clients\".")%>
</p></div>
</p>
</div>
<%
boolean isInitialized = indexBean.isInitialized();
String nextNonce = isInitialized ? net.i2p.i2ptunnel.web.IndexBean.getNextNonce() : null;
@ -32,19 +47,26 @@
if (msgs.length() > 0) {
%>
<div class="panel" id="messages">
<h2><%=intl._t("Status Messages")%></h2>
<h2>
<%=intl._t("Status Messages")%>
</h2>
<table id="statusMessagesTable">
<tr>
<td id="tunnelMessages">
<textarea id="statusMessages" rows="4" cols="60" readonly="readonly"><%=msgs%></textarea>
</td>
</tr><tr>
</tr>
<tr>
<td class="buttons">
<a class="control" href="list"><%=intl._t("Refresh")%></a>
<a class="control" href="list">
<%=intl._t("Refresh")%>
</a>
<%
if (isInitialized) {
%>
<a class="control" href="list?action=Clear&amp;msgid=<%=lastID%>&amp;nonce=<%=nextNonce%>"><%=intl._t("Clear")%></a>
<a class="control" href="list?action=Clear&amp;msgid=<%=lastID%>&amp;nonce=<%=nextNonce%>">
<%=intl._t("Clear")%>
</a>
<%
} // isInitialized
%>
@ -57,32 +79,58 @@
if (isInitialized) {
%>
<div class="panel" id="globalTunnelControl">
<h2><%=intl._t("Global Tunnel Control")%></h2>
<h2>
<%=intl._t("Global Tunnel Control")%>
</h2>
<table>
<tr>
<td class="buttons">
<a class="control" href="wizard"><%=intl._t("Tunnel Wizard")%></a>
<a class="control" href="list?nonce=<%=nextNonce%>&amp;action=Stop%20all"><%=intl._t("Stop All")%></a>
<a class="control" href="list?nonce=<%=nextNonce%>&amp;action=Start%20all"><%=intl._t("Start All")%></a>
<a class="control" href="list?nonce=<%=nextNonce%>&amp;action=Restart%20all"><%=intl._t("Restart All")%></a>
<a class="control" href="wizard">
<%=intl._t("Tunnel Wizard")%>
</a>
<a class="control" href="list?nonce=<%=nextNonce%>&amp;action=Stop%20all">
<%=intl._t("Stop All")%>
</a>
<a class="control" href="list?nonce=<%=nextNonce%>&amp;action=Start%20all">
<%=intl._t("Start All")%>
</a>
<a class="control" href="list?nonce=<%=nextNonce%>&amp;action=Restart%20all">
<%=intl._t("Restart All")%>
</a>
<%--
//this is really bad because it stops and restarts all tunnels, which is probably not what you want
<a class="control" href="list?nonce=<%=nextNonce%>&amp;action=Reload%20configuration"><%=intl._t("Reload Config")%></a>
<a class="control" href="list?nonce=<%=nextNonce%>&amp;action=Reload%20configuration">
<%=intl._t("Reload Config")%>
</a>
--%>
</td>
</tr>
</table>
</div>
<div class="panel" id="servers">
<h2><%=intl._t("I2P Hidden Services")%></h2>
<h2>
<%=intl._t("I2P Hidden Services")%>
</h2>
<table id="serverTunnels">
<tr>
<th class="tunnelName"><%=intl._t("Name")%></th>
<th class="tunnelType"><%=intl._t("Type")%></th>
<th class="tunnelLocation"><%=intl._t("Points at")%></th>
<th class="tunnelPreview"><%=intl._t("Preview")%></th>
<th class="tunnelStatus"><%=intl._t("Status")%></th>
<th class="tunnelControl"><%=intl._t("Control")%></th>
<th class="tunnelName">
<%=intl._t("Name")%>
</th>
<th class="tunnelType">
<%=intl._t("Type")%>
</th>
<th class="tunnelLocation">
<%=intl._t("Points at")%>
</th>
<th class="tunnelPreview">
<%=intl._t("Preview")%>
</th>
<th class="tunnelStatus">
<%=intl._t("Status")%>
</th>
<th class="tunnelControl">
<%=intl._t("Control")%>
</th>
</tr>
<%
for (int curServer = 0; curServer < indexBean.getTunnelCount(); curServer++) {
@ -90,19 +138,28 @@
%>
<tr class="tunnelProperties">
<td class="tunnelName">
<a href="edit?tunnel=<%=curServer%>" title="<%=intl._t("Edit Server Tunnel Settings for")%>&nbsp;<%=indexBean.getTunnelName(curServer)%>"><%=indexBean.getTunnelName(curServer)%></a>
</td><td class="tunnelType"><%=indexBean.getTunnelType(curServer)%>
</td><td class="tunnelLocation">
<a href="edit?tunnel=<%=curServer%>" title="<%=intl._t(" Edit Server Tunnel Settings for ")%>&nbsp;<%=indexBean.getTunnelName(curServer)%>">
<%=indexBean.getTunnelName(curServer)%>
</a>
</td>
<td class="tunnelType">
<%=indexBean.getTunnelType(curServer)%>
</td>
<td class="tunnelLocation">
<%
if (indexBean.isServerTargetLinkValid(curServer)) {
if (indexBean.isSSLEnabled(curServer)) { %>
<a href="https://<%=indexBean.getServerTarget(curServer)%>/" title="<%=intl._t("Test HTTPS server, bypassing I2P")%>" target="_top"><%=indexBean.getServerTarget(curServer)%> SSL</a>
<a href="https://<%=indexBean.getServerTarget(curServer)%>/" title="<%=intl._t(" Test HTTPS server, bypassing I2P ")%>" target="_top">
<%=indexBean.getServerTarget(curServer)%> SSL</a>
<% } else { %>
<a href="http://<%=indexBean.getServerTarget(curServer)%>/" title="<%=intl._t("Test HTTP server, bypassing I2P")%>" target="_top"><%=indexBean.getServerTarget(curServer)%></a>
<a href="http://<%=indexBean.getServerTarget(curServer)%>/" title="<%=intl._t(" Test HTTP server, bypassing I2P ")%>" target="_top">
<%=indexBean.getServerTarget(curServer)%>
</a>
<%
}
} else {
%><%=indexBean.getServerTarget(curServer)%>
%>
<%=indexBean.getServerTarget(curServer)%>
<%
if (indexBean.isSSLEnabled(curServer)) { %>
SSL
@ -110,42 +167,66 @@
}
}
%>
</td><td class="tunnelPreview">
</td>
<td class="tunnelPreview">
<%
if (("httpserver".equals(indexBean.getInternalType(curServer)) || ("httpbidirserver".equals(indexBean.getInternalType(curServer)))) && indexBean.getTunnelStatus(curServer) == IndexBean.RUNNING) {
%>
<a class="control" title="<%=intl._t("Test HTTP server through I2P")%>" href="http://<%=indexBean.getDestHashBase32(curServer)%>" target="_top"><%=intl._t("Preview")%></a>
<a class="control" title="<%=intl._t(" Test HTTP server through I2P ")%>" href="http://<%=indexBean.getDestHashBase32(curServer)%>" target="_top">
<%=intl._t("Preview")%>
</a>
<%
} else {
%><%=intl._t("No Preview")%>
%>
<%=intl._t("No Preview")%>
<%
}
%>
</td><td class="tunnelStatus">
</td>
<td class="tunnelStatus">
<%
switch (indexBean.getTunnelStatus(curServer)) {
case IndexBean.STARTING:
%><div class="statusStarting text" title="<%=intl._t("Starting...")%>"><%=intl._t("Starting...")%></div>
</td><td class="tunnelControl">
<a class="control" title="<%=intl._t("Stop this Tunnel")%>" href="list?nonce=<%=nextNonce%>&amp;action=stop&amp;tunnel=<%=curServer%>"><%=intl._t("Stop")%></a>
%>
<div class="statusStarting text" title="<%=intl._t(" Starting... ")%>">
<%=intl._t("Starting...")%>
</div>
</td>
<td class="tunnelControl">
<a class="control" title="<%=intl._t(" Stop this Tunnel ")%>" href="list?nonce=<%=nextNonce%>&amp;action=stop&amp;tunnel=<%=curServer%>">
<%=intl._t("Stop")%>
</a>
<%
break;
case IndexBean.RUNNING:
%><div class="statusRunning text" title="<%=intl._t("Running")%>"><%=intl._t("Running")%></div>
</td><td class="tunnelControl">
<a class="control" title="<%=intl._t("Stop this Tunnel")%>" href="list?nonce=<%=nextNonce%>&amp;action=stop&amp;tunnel=<%=curServer%>"><%=intl._t("Stop")%></a>
%>
<div class="statusRunning text" title="<%=intl._t(" Running ")%>">
<%=intl._t("Running")%>
</div>
</td>
<td class="tunnelControl">
<a class="control" title="<%=intl._t(" Stop this Tunnel ")%>" href="list?nonce=<%=nextNonce%>&amp;action=stop&amp;tunnel=<%=curServer%>">
<%=intl._t("Stop")%>
</a>
<%
break;
case IndexBean.NOT_RUNNING:
%><div class="statusNotRunning text" title="<%=intl._t("Stopped")%>"><%=intl._t("Stopped")%></div>
</td><td class="tunnelControl">
<a class="control" title="<%=intl._t("Start this Tunnel")%>" href="list?nonce=<%=nextNonce%>&amp;action=start&amp;tunnel=<%=curServer%>"><%=intl._t("Start")%></a>
%>
<div class="statusNotRunning text" title="<%=intl._t(" Stopped ")%>">
<%=intl._t("Stopped")%>
</div>
</td>
<td class="tunnelControl">
<a class="control" title="<%=intl._t(" Start this Tunnel ")%>" href="list?nonce=<%=nextNonce%>&amp;action=start&amp;tunnel=<%=curServer%>">
<%=intl._t("Start")%>
</a>
<%
break;
}
%>
</td>
</tr><tr>
</tr>
<tr>
<td class="tunnelDestination" colspan="6">
<span class="tunnelDestinationLabel">
<%
@ -154,15 +235,9 @@
name = indexBean.getTunnelName(curServer);
out.write("<b>");
out.write(intl._t("Destination"));
out.write(":</b></span> ");
out.write(indexBean.getDestHashBase32(curServer));
} else {
out.write("<b>");
out.write(":</b></span> "); out.write(indexBean.getDestHashBase32(curServer)); } else { out.write("<b>");
out.write(intl._t("Hostname"));
out.write(":</b></span> ");
out.write(name);
}
%>
out.write(":</b></span> "); out.write(name); } %>
</td>
</tr>
<%
@ -189,7 +264,8 @@
<%
} else {
// needed to make the spacing look right
%>&nbsp;<%
%>&nbsp;
<%
} // descr
%>
</td>
@ -239,6 +315,7 @@
<option value="httpbidirserver">HTTP bidir</option>
<option value="ircserver">IRC</option>
<option value="streamrserver">Streamr</option>
<option value="udpserver">UDP Server/Client(Bidirectional)</option>
</select>
<input class="control" type="submit" value="<%=intl._t(" Create ")%>" />
</form>
@ -247,15 +324,29 @@
</table>
</div>
<div class="panel" id="clients">
<h2><%=intl._t("I2P Client Tunnels")%></h2>
<h2>
<%=intl._t("I2P Client Tunnels")%>
</h2>
<table id="clientTunnels">
<tr>
<th class="tunnelName"><%=intl._t("Name")%></th>
<th class="tunnelType"><%=intl._t("Type")%></th>
<th class="tunnelInterface"><%=intl._t("Interface")%></th>
<th class="tunnelPort"><%=intl._t("Port")%></th>
<th class="tunnelStatus"><%=intl._t("Status")%></th>
<th class="tunnelControl"><%=intl._t("Control")%></th>
<th class="tunnelName">
<%=intl._t("Name")%>
</th>
<th class="tunnelType">
<%=intl._t("Type")%>
</th>
<th class="tunnelInterface">
<%=intl._t("Interface")%>
</th>
<th class="tunnelPort">
<%=intl._t("Port")%>
</th>
<th class="tunnelStatus">
<%=intl._t("Status")%>
</th>
<th class="tunnelControl">
<%=intl._t("Control")%>
</th>
</tr>
<%
for (int curClient = 0; curClient < indexBean.getTunnelCount(); curClient++) {
@ -263,9 +354,14 @@
%>
<tr class="tunnelProperties">
<td class="tunnelName">
<a href="edit?tunnel=<%=curClient%>" title="<%=intl._t("Edit Tunnel Settings for")%>&nbsp;<%=indexBean.getTunnelName(curClient)%>"><%=indexBean.getTunnelName(curClient)%></a>
</td><td class="tunnelType"><%=indexBean.getTunnelType(curClient)%>
</td><td class="tunnelInterface">
<a href="edit?tunnel=<%=curClient%>" title="<%=intl._t(" Edit Tunnel Settings for ")%>&nbsp;<%=indexBean.getTunnelName(curClient)%>">
<%=indexBean.getTunnelName(curClient)%>
</a>
</td>
<td class="tunnelType">
<%=indexBean.getTunnelType(curClient)%>
</td>
<td class="tunnelInterface">
<%
/* should only happen for streamr client */
String cHost= indexBean.getClientInterface(curClient);
@ -277,44 +373,71 @@
out.write(cHost);
}
%>
</td><td class="tunnelPort">
</td>
<td class="tunnelPort">
<%
String cPort= indexBean.getClientPort2(curClient);
out.write(cPort);
if (indexBean.isSSLEnabled(curClient))
out.write(" SSL");
%>
</td><td class="tunnelStatus">
</td>
<td class="tunnelStatus">
<%
switch (indexBean.getTunnelStatus(curClient)) {
case IndexBean.STARTING:
%><div class="statusStarting text" title="<%=intl._t("Starting...")%>"><%=intl._t("Starting...")%></div>
</td><td class="tunnelControl">
<a class="control" title="<%=intl._t("Stop this Tunnel")%>" href="list?nonce=<%=nextNonce%>&amp;action=stop&amp;tunnel=<%=curClient%>"><%=intl._t("Stop")%></a>
%>
<div class="statusStarting text" title="<%=intl._t(" Starting... ")%>">
<%=intl._t("Starting...")%>
</div>
</td>
<td class="tunnelControl">
<a class="control" title="<%=intl._t(" Stop this Tunnel ")%>" href="list?nonce=<%=nextNonce%>&amp;action=stop&amp;tunnel=<%=curClient%>">
<%=intl._t("Stop")%>
</a>
<%
break;
case IndexBean.STANDBY:
%><div class="statusStarting text" title="<%=intl._t("Standby")%>"><%=intl._t("Standby")%></div>
</td><td class="tunnelControl">
<a class="control" title="Stop this Tunnel" href="list?nonce=<%=nextNonce%>&amp;action=stop&amp;tunnel=<%=curClient%>"><%=intl._t("Stop")%></a>
%>
<div class="statusStarting text" title="<%=intl._t(" Standby ")%>">
<%=intl._t("Standby")%>
</div>
</td>
<td class="tunnelControl">
<a class="control" title="Stop this Tunnel" href="list?nonce=<%=nextNonce%>&amp;action=stop&amp;tunnel=<%=curClient%>">
<%=intl._t("Stop")%>
</a>
<%
break;
case IndexBean.RUNNING:
%><div class="statusRunning text" title="<%=intl._t("Running")%>"><%=intl._t("Running")%></div>
</td><td class="tunnelControl">
<a class="control" title="Stop this Tunnel" href="list?nonce=<%=nextNonce%>&amp;action=stop&amp;tunnel=<%=curClient%>"><%=intl._t("Stop")%></a>
%>
<div class="statusRunning text" title="<%=intl._t(" Running ")%>">
<%=intl._t("Running")%>
</div>
</td>
<td class="tunnelControl">
<a class="control" title="Stop this Tunnel" href="list?nonce=<%=nextNonce%>&amp;action=stop&amp;tunnel=<%=curClient%>">
<%=intl._t("Stop")%>
</a>
<%
break;
case IndexBean.NOT_RUNNING:
%><div class="statusNotRunning text" title="<%=intl._t("Stopped")%>"><%=intl._t("Stopped")%></div>
</td><td class="tunnelControl">
<a class="control" title="<%=intl._t("Start this Tunnel")%>" href="list?nonce=<%=nextNonce%>&amp;action=start&amp;tunnel=<%=curClient%>"><%=intl._t("Start")%></a>
%>
<div class="statusNotRunning text" title="<%=intl._t(" Stopped ")%>">
<%=intl._t("Stopped")%>
</div>
</td>
<td class="tunnelControl">
<a class="control" title="<%=intl._t(" Start this Tunnel ")%>" href="list?nonce=<%=nextNonce%>&amp;action=start&amp;tunnel=<%=curClient%>">
<%=intl._t("Start")%>
</a>
<%
break;
}
%>
</td>
</tr><tr>
</tr>
<tr>
<td class="tunnelDestination" colspan="6">
<span class="tunnelDestinationLabel">
<% if ("httpclient".equals(indexBean.getInternalType(curClient)) || "connectclient".equals(indexBean.getInternalType(curClient)) ||
@ -326,15 +449,23 @@
</span>
<%
if (indexBean.getIsUsingOutproxyPlugin(curClient)) {
%><%=intl._t("internal plugin")%><%
%>
<%=intl._t("internal plugin")%>
<%
} else {
String cdest = indexBean.getClientDestination(curClient);
if (cdest.length() > 70) { // Probably a B64 (a B32 is 60 chars) so truncate
%><%=cdest.substring(0, 45)%>&hellip;<%=cdest.substring(cdest.length() - 15, cdest.length())%><%
%>
<%=cdest.substring(0, 45)%>&hellip;
<%=cdest.substring(cdest.length() - 15, cdest.length())%>
<%
} else if (cdest.length() > 0) {
%><%=cdest%><%
%>
<%=cdest%>
<%
} else {
%><i><%=intl._t("none")%></i><%
%><i><%=intl._t("none")%></i>
<%
}
} %>
</td>
@ -355,13 +486,19 @@
%><b><%=intl._t("Description")%><%
}
if (descr != null && descr.length() > 0) {
%>:</b></span> <%=descr%><%
%>:</b></span>
<%=descr%>
<%
} else {
%></b></span><%
%>
</b>
</span>
<%
}
} else {
// needed to make the spacing look right
%>&nbsp;<%
%>&nbsp;
<%
} // descr
%>
</td>
@ -381,6 +518,7 @@
<option value="socksirctunnel">SOCKS IRC</option>
<option value="connectclient">CONNECT</option>
<option value="streamrclient">Streamr</option>
<option value="udpclient">UDP Client(Recieve-Only)</option>
</select>
<input class="control" type="submit" value="<%=intl._t(" Create ")%>" />
</form>
@ -393,4 +531,6 @@
} // isInitialized()
%>
</body></html>
</body>
</html>