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

File diff suppressed because it is too large Load Diff

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

@ -21,29 +21,29 @@ import net.i2p.i2ptunnel.Logging;
import net.i2p.i2ptunnel.udp.*;
import net.i2p.util.EventDispatcher;
/**
* Base client class that sets up an I2P Datagram client destination.
* The UDP side is not implemented here, as there are at least
* two possibilities:
*
* 1) UDP side is a "server"
* Example: Streamr Consumer
* - Configure a destination host and port
* - External application sends no data
* - Extending class must have a constructor with host and port arguments
*
* 2) UDP side is a client/server
* Example: SOCKS UDP (DNS requests?)
* - configure an inbound port and a destination host and port
* - External application sends and receives data
* - Extending class must have a constructor with host and 2 port arguments
*
* So the implementing class must create a UDPSource and/or UDPSink,
* and must call setSink().
*
* @author zzz with portions from welterde's streamr
*/
public abstract class I2PTunnelUDPClientBase extends I2PTunnelTask implements Source, Sink {
/**
* Base client class that sets up an I2P Datagram client destination.
* The UDP side is not implemented here, as there are at least
* two possibilities:
*
* 1) UDP side is a "server"
* Example: Streamr Consumer
* - Configure a destination host and port
* - External application sends no data
* - Extending class must have a constructor with host and port arguments
*
* 2) UDP side is a client/server
* Example: SOCKS UDP (DNS requests?)
* - configure an inbound port and a destination host and port
* - External application sends and receives data
* - Extending class must have a constructor with host and 2 port arguments
*
* So the implementing class must create a UDPSource and/or UDPSink,
* and must call setSink().
*
* @author zzz with portions from welterde's streamr
*/
public abstract class I2PTunnelUDPClientBase extends I2PTunnelTask implements Source, Sink {
protected I2PAppContext _context;
protected Logging l;
@ -54,7 +54,7 @@ import net.i2p.util.EventDispatcher;
protected long _clientId;
private final Object startLock = new Object();
private final I2PSession _session;
private final Source _i2pSource;
private final Sink _i2pSink;
@ -63,8 +63,8 @@ import net.i2p.util.EventDispatcher;
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
* badly that we cant create a socketManager
*/
public I2PTunnelUDPClientBase(String destination, Logging l, EventDispatcher notifyThis,
I2PTunnel tunnel) throws IllegalArgumentException {
public I2PTunnelUDPClientBase(String destination, Logging l, EventDispatcher notifyThis,
I2PTunnel tunnel) throws IllegalArgumentException {
super("UDPServer", notifyThis, tunnel);
_clientId = __clientId.incrementAndGet();
this.l = l;
@ -72,7 +72,7 @@ import net.i2p.util.EventDispatcher;
_context = tunnel.getContext();
tunnel.getClientOptions().setProperty("i2cp.dontPublishLeaseSet", "true");
// create i2pclient and destination
I2PClient client = I2PClientFactory.createClient();
byte[] key;
@ -89,7 +89,7 @@ import net.i2p.util.EventDispatcher;
}
client.createDestination(out, stype);
key = out.toByteArray();
} catch(Exception exc) {
} catch (Exception exc) {
throw new RuntimeException("failed to create i2p-destination", exc);
}
@ -99,7 +99,7 @@ import net.i2p.util.EventDispatcher;
// FIXME this may not pick up non-default I2CP host/port settings from tunnel
_session = client.createSession(in, tunnel.getClientOptions());
connected(_session);
} catch(Exception exc) {
} catch (Exception exc) {
throw new RuntimeException("failed to create session", exc);
}
@ -117,9 +117,9 @@ import net.i2p.util.EventDispatcher;
_i2pSink = new I2PSink(_session, addr.getAddress(), false, addr.getPort());
} else {
_i2pSink = new I2PSinkAnywhere(_session, false);
}
}
}
/**
* Actually start working on outgoing connections.
* Classes should override to start UDP side as well.
@ -132,7 +132,7 @@ import net.i2p.util.EventDispatcher;
synchronized (startLock) {
try {
_session.connect();
} catch(I2PSessionException exc) {
} catch (I2PSessionException exc) {
throw new RuntimeException("failed to connect session", exc);
}
start();
@ -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;
@ -159,11 +161,11 @@ import net.i2p.util.EventDispatcher;
}
/**
* Source Methods
* Source Methods
*
* Sets the receiver of the UDP datagrams from I2P
* Subclass must call this after constructor
* and before start()
* Sets the receiver of the UDP datagrams from I2P
* Subclass must call this after constructor
* and before start()
*/
public void setSink(Sink s) {
_i2pSource.setSink(s);
@ -175,10 +177,10 @@ import net.i2p.util.EventDispatcher;
}
/**
* Sink Methods
* Sink Methods
*
* @param to - ignored if configured for a single destination
* (we use the dest specified in the constructor)
* (we use the dest specified in the constructor)
* @since 0.9.53 added fromPort and toPort parameters
* @throws RuntimeException if session is closed
*/

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

@ -59,14 +59,14 @@ public class GeneralHelper {
protected final TunnelControllerGroup _group;
/**
* @param tcg may be null ???
* @param tcg may be null ???
*/
public GeneralHelper(TunnelControllerGroup tcg) {
this(I2PAppContext.getGlobalContext(), tcg);
}
/**
* @param tcg may be null ???
* @param tcg may be null ???
*/
public GeneralHelper(I2PAppContext context, TunnelControllerGroup tcg) {
_context = context;
@ -78,12 +78,14 @@ public class GeneralHelper {
}
/**
* @param tcg may be null
* @return null if not found or tcg is null
* @param tcg may be null
* @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);
@ -92,20 +94,21 @@ public class GeneralHelper {
}
/**
* Save the configuration for a new or existing tunnel to disk.
* For new tunnels, adds to controller and (if configured) starts it.
* Save the configuration for a new or existing tunnel to disk.
* For new tunnels, adds to controller and (if configured) starts it.
*/
public List<String> saveTunnel(int tunnel, TunnelConfig config) {
return saveTunnel(_context, _group, tunnel, config);
}
/**
* Save the configuration for a new or existing tunnel to disk.
* For new tunnels, adds to controller and (if configured) starts it.
* Save the configuration for a new or existing tunnel to disk.
* For new tunnels, adds to controller and (if configured) starts it.
*
* @param context unused, taken from tcg
* @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,9 +116,11 @@ 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.
* This does NOT save this tunnel's config. Caller must call saveConfig() also.
* 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) {
List<String> msgs = new ArrayList<String>();
@ -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.
* This does NOT save this tunnel's config. Caller must call saveConfig() also.
* 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
* @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))) {
@ -149,7 +158,7 @@ public class GeneralHelper {
String intfc = props.getProperty(TunnelController.PROP_INTFC);
Set<String> altNames = new HashSet<String>(4);
if (intfc != null && !intfc.equals("0.0.0.0") && !intfc.equals("::") &&
!intfc.equals("0:0:0:0:0:0:0:0"))
!intfc.equals("0:0:0:0:0:0:0:0"))
altNames.add(intfc);
String tgts = props.getProperty(TunnelController.PROP_DEST);
if (tgts != null) {
@ -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
@ -218,28 +228,30 @@ 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
* 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 = {
// I2CP
"i2cp.reduceOnIdle", "i2cp.closeOnIdle", "i2cp.newDestOnResume",
"i2cp.reduceIdleTime", "i2cp.reduceQuantity", "i2cp.closeIdleTime",
// dest / LS
I2PClient.PROP_SIGTYPE, "i2cp.leaseSetEncType", "i2cp.leaseSetType",
"persistentClientKey",
// following are mostly server but could also be persistent client
"inbound.randomKey", "outbound.randomKey", "i2cp.leaseSetSigningPrivateKey", "i2cp.leaseSetPrivateKey"
// I2CP
"i2cp.reduceOnIdle", "i2cp.closeOnIdle", "i2cp.newDestOnResume",
"i2cp.reduceIdleTime", "i2cp.reduceQuantity", "i2cp.closeIdleTime",
// dest / LS
I2PClient.PROP_SIGTYPE, "i2cp.leaseSetEncType", "i2cp.leaseSetType",
"persistentClientKey",
// following are mostly server but could also be persistent client
"inbound.randomKey", "outbound.randomKey", "i2cp.leaseSetSigningPrivateKey", "i2cp.leaseSetPrivateKey"
};
/**
* Copy relevant options over
* @since 0.9.46 pulled out of updateTunnelConfig
* Copy relevant options over
*
* @since 0.9.46 pulled out of updateTunnelConfig
*/
private static void copySharedOptions(TunnelConfig fromConfig, Properties from,
TunnelController to) {
TunnelController to) {
Properties cOpt = to.getConfig("");
fromConfig.updateTunnelQuantities(cOpt);
cOpt.setProperty("option.inbound.nickname", TunnelConfig.SHARED_CLIENT_NICKNAME);
@ -260,12 +272,12 @@ public class GeneralHelper {
}
/**
* Save the configuration for an existing tunnel to disk.
* New tunnels must use saveConfig(..., TunnelController).
* Save the configuration for an existing tunnel to disk.
* New tunnels must use saveConfig(..., TunnelController).
*
* @param context unused, taken from tcg
* @param tunnel must already exist
* @since 0.9.49
* @param context unused, taken from tcg
* @param tunnel must already exist
* @since 0.9.49
*/
protected static List<String> saveConfig(I2PAppContext context, TunnelControllerGroup tcg, int tunnel) {
TunnelController cur = getController(tcg, tunnel);
@ -278,11 +290,11 @@ public class GeneralHelper {
}
/**
* Save the configuration to disk.
* For new and existing tunnels.
* Does NOT call tcg.addController() for new tunnels. See udpateConfig()
* Save the configuration to disk.
* For new and existing tunnels.
* Does NOT call tcg.addController() for new tunnels. See udpateConfig()
*
* @since 0.9.49
* @since 0.9.49
*/
private static List<String> saveConfig(TunnelControllerGroup tcg, TunnelController cur) {
I2PAppContext context = tcg.getContext();
@ -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
@ -321,7 +334,7 @@ public class GeneralHelper {
msgs = tcg.removeController(cur);
try {
tcg.removeConfig(cur);
}catch (IOException ioe){
} catch (IOException ioe) {
msgs.add(ioe.toString());
}
@ -333,7 +346,7 @@ public class GeneralHelper {
if (pk == null)
pk = privKeyFile;
if (pk != null && pk.startsWith("i2ptunnel") && pk.endsWith("-privKeys.dat") &&
((!TunnelController.isClient(cur.getType())) || cur.getPersistentClientKey())) {
((!TunnelController.isClient(cur.getType())) || cur.getPersistentClientKey())) {
File pkf = new File(context.getConfigDir(), pk);
if (pkf.exists()) {
String name = cur.getName();
@ -356,7 +369,7 @@ public class GeneralHelper {
boolean success = FileUtil.rename(pkf, to);
if (success)
msgs.add("Private key file " + pkf.getAbsolutePath() +
" renamed to " + to.getAbsolutePath());
" renamed to " + to.getAbsolutePath());
}
}
return msgs;
@ -372,14 +385,14 @@ public class GeneralHelper {
}
/**
* @return null if unset
* @return null if unset
*/
public String getTunnelName(int tunnel) {
return getTunnelName(_group, tunnel);
}
/**
* @return null if unset
* @return null if unset
*/
public static String getTunnelName(TunnelControllerGroup tcg, int tunnel) {
TunnelController tun = getController(tcg, tunnel);
@ -418,14 +431,14 @@ public class GeneralHelper {
}
/**
* @return path, non-null, non-empty
* @return path, non-null, non-empty
*/
public String getPrivateKeyFile(int tunnel) {
return getPrivateKeyFile(_group, tunnel);
}
/**
* @return path, non-null, non-empty
* @return path, non-null, non-empty
*/
public String getPrivateKeyFile(TunnelControllerGroup tcg, int tunnel) {
TunnelController tun = getController(tcg, tunnel);
@ -447,16 +460,16 @@ public class GeneralHelper {
}
/**
* @return path or ""
* @since 0.9.30
* @return path or ""
* @since 0.9.30
*/
public String getAltPrivateKeyFile(int tunnel) {
return getAltPrivateKeyFile(_group, tunnel);
}
/**
* @return path or ""
* @since 0.9.30
* @return path or ""
* @since 0.9.30
*/
public String getAltPrivateKeyFile(TunnelControllerGroup tcg, int tunnel) {
TunnelController tun = getController(tcg, tunnel);
@ -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_IRC_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();
@ -517,8 +536,9 @@ public class GeneralHelper {
}
/**
* Works even if tunnel is not running.
* @return Destination or null
* Works even if tunnel is not running.
*
* @return Destination or null
*/
public Destination getDestination(int tunnel) {
TunnelController tun = getController(tunnel);
@ -535,16 +555,18 @@ public class GeneralHelper {
if (rv != null)
return rv;
} catch (I2PException e) {
} catch (IOException e) {}
} catch (IOException e) {
}
}
}
return null;
}
/**
* Works even if tunnel is not running.
* @return Destination or null
* @since 0.9.30
* Works even if tunnel is not running.
*
* @return Destination or null
* @since 0.9.30
*/
public Destination getAltDestination(int tunnel) {
TunnelController tun = getController(tunnel);
@ -558,16 +580,18 @@ public class GeneralHelper {
if (rv != null)
return rv;
} catch (I2PException e) {
} catch (IOException e) {}
} catch (IOException e) {
}
}
}
return null;
}
/**
* Works even if tunnel is not running.
* @return true if offline keys
* @since 0.9.40
* Works even if tunnel is not running.
*
* @return true if offline keys
* @since 0.9.40
*/
public boolean isOfflineKeys(int tunnel) {
TunnelController tun = getController(tunnel);
@ -652,11 +676,11 @@ public class GeneralHelper {
/**
* @param tunnel
* @param def in minutes
* @param def in minutes
* @return time in minutes
*/
public int getReduceTime(int tunnel, int def) {
return getProperty(tunnel, "i2cp.reduceIdleTime", def*60*1000) / (60*1000);
return getProperty(tunnel, "i2cp.reduceIdleTime", def * 60 * 1000) / (60 * 1000);
}
public int getCert(int tunnel) {
@ -676,7 +700,7 @@ public class GeneralHelper {
}
/**
* @since 0.9.40
* @since 0.9.40
*/
public int getEncryptMode(int tunnel) {
if (getEncrypt(tunnel))
@ -710,7 +734,7 @@ public class GeneralHelper {
}
/**
* @since 0.9.40
* @since 0.9.40
*/
public String getBlindedPassword(int tunnel) {
String rv = getProperty(tunnel, "i2cp.leaseSetSecret", null);
@ -720,13 +744,14 @@ public class GeneralHelper {
rv = "";
return rv;
}
/**
* 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
* 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
*/
public List<String> getClientAuths(int tunnel, boolean isDH) {
List<String> rv = new ArrayList<String>(4);
@ -734,16 +759,16 @@ public class GeneralHelper {
int i = 0;
String p;
while ((p = getProperty(tunnel, pfx + i, null)) != null) {
rv.add(p);
i++;
rv.add(p);
i++;
}
return rv;
}
/**
* @param newTunnelType used if tunnel &lt; 0
* @return the current type if we have a destination already,
* else the default for that type of tunnel
* @param newTunnelType used if tunnel &lt; 0
* @return the current type if we have a destination already,
* else the default for that type of tunnel
*/
public int getSigType(int tunnel, String newTunnelType) {
SigType type;
@ -751,7 +776,7 @@ public class GeneralHelper {
if (tunnel >= 0) {
ttype = getTunnelType(tunnel);
if (!TunnelController.isClient(ttype) ||
getBooleanProperty(tunnel, "persistentClientKey")) {
getBooleanProperty(tunnel, "persistentClientKey")) {
Destination d = getDestination(tunnel);
if (d != null) {
type = d.getSigType();
@ -768,13 +793,14 @@ public class GeneralHelper {
if (type == null) {
// same default logic as in TunnelController.setConfig()
if (!TunnelController.isClient(ttype) ||
TunnelController.TYPE_IRC_CLIENT.equals(ttype) ||
TunnelController.TYPE_SOCKS_IRC.equals(ttype) ||
TunnelController.TYPE_SOCKS.equals(ttype) ||
TunnelController.TYPE_STREAMR_CLIENT.equals(ttype) ||
TunnelController.TYPE_STD_CLIENT.equals(ttype) ||
TunnelController.TYPE_CONNECT.equals(ttype) ||
TunnelController.TYPE_HTTP_CLIENT.equals(ttype))
TunnelController.TYPE_IRC_CLIENT.equals(ttype) ||
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))
type = TunnelController.PREFERRED_SIGTYPE;
else
type = SigType.DSA_SHA1;
@ -783,8 +809,8 @@ public class GeneralHelper {
}
/**
* @param encType code
* @since 0.9.44
* @param encType code
* @since 0.9.44
*/
public boolean hasEncType(int tunnel, int encType) {
TunnelController tun = getController(tunnel);
@ -796,15 +822,16 @@ public class GeneralHelper {
String type = tun.getType();
String dflt;
if (tun.isClient() &&
(TunnelController.TYPE_HTTP_CLIENT.equals(type) ||
TunnelController.TYPE_IRC_CLIENT.equals(type) ||
TunnelController.TYPE_SOCKS_IRC.equals(type) ||
TunnelController.TYPE_STREAMR_CLIENT.equals(type) ||
Boolean.parseBoolean(tun.getSharedClient()))) {
(TunnelController.TYPE_HTTP_CLIENT.equals(type) ||
TunnelController.TYPE_IRC_CLIENT.equals(type) ||
TunnelController.TYPE_SOCKS_IRC.equals(type) ||
TunnelController.TYPE_STREAMR_CLIENT.equals(type) ||
Boolean.parseBoolean(tun.getSharedClient()))) {
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_IRC_SERVER.equals(type) ||
TunnelController.TYPE_STREAMR_SERVER.equals(type) ||
TunnelController.TYPE_UDP_SERVER.equals(type)) {
dflt = "4,0";
} else {
dflt = "0";
@ -820,7 +847,7 @@ public class GeneralHelper {
}
/**
* Random keys
* Random keys
*/
public String getInboundRandomKey(int tunnel) {
return getProperty(tunnel, "inbound.randomKey", "");
@ -859,7 +886,7 @@ public class GeneralHelper {
}
/**
* @return entries sorted, converted to b32, separated by newlines, or ""
* @return entries sorted, converted to b32, separated by newlines, or ""
*/
public String getAccessList(int tunnel) {
String val = getProperty(tunnel, "i2cp.accessList", "");
@ -890,7 +917,7 @@ public class GeneralHelper {
}
/**
* @since 0.9.40
* @since 0.9.40
*/
public String getFilterDefinition(int tunnel) {
TunnelController tunnelController = getController(tunnel);
@ -904,7 +931,7 @@ public class GeneralHelper {
public String getJumpList(int tunnel) {
return getProperty(tunnel, I2PTunnelHTTPClient.PROP_JUMP_SERVERS,
I2PTunnelHTTPClient.DEFAULT_JUMP_SERVERS).replace(",", "\n");
I2PTunnelHTTPClient.DEFAULT_JUMP_SERVERS).replace(",", "\n");
}
public boolean getCloseOnIdle(int tunnel, boolean def) {
@ -912,13 +939,13 @@ public class GeneralHelper {
}
public int getCloseTime(int tunnel, int def) {
return getProperty(tunnel, "i2cp.closeIdleTime", def*60*1000) / (60*1000);
return getProperty(tunnel, "i2cp.closeIdleTime", def * 60 * 1000) / (60 * 1000);
}
public boolean getNewDest(int tunnel) {
return getBooleanProperty(tunnel, "i2cp.newDestOnResume") &&
getBooleanProperty(tunnel, "i2cp.closeOnIdle") &&
!getBooleanProperty(tunnel, "persistentClientKey");
getBooleanProperty(tunnel, "i2cp.closeOnIdle") &&
!getBooleanProperty(tunnel, "persistentClientKey");
}
public boolean getPersistentClientKey(int tunnel) {
@ -942,12 +969,12 @@ public class GeneralHelper {
}
/**
* As of 0.9.35, default true, and overridden to true unless
* PROP_SSL_SET is set
* As of 0.9.35, default true, and overridden to true unless
* PROP_SSL_SET is set
*/
public boolean getAllowInternalSSL(int tunnel) {
return getBooleanProperty(tunnel, I2PTunnelHTTPClient.PROP_INTERNAL_SSL, true) ||
!getBooleanProperty(tunnel, I2PTunnelHTTPClient.PROP_SSL_SET, true);
!getBooleanProperty(tunnel, I2PTunnelHTTPClient.PROP_SSL_SET, true);
}
public boolean getMultihome(int tunnel) {
@ -977,7 +1004,7 @@ public class GeneralHelper {
}
/**
* Default true
* Default true
*/
public boolean getUseOutproxyPlugin(int tunnel) {
return getBooleanProperty(tunnel, I2PTunnelHTTPClientBase.PROP_USE_OUTPROXY_PLUGIN, true);
@ -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,22 +1094,23 @@ 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());
TunnelController.TYPE_CONNECT.equals(tun.getType());
Map<String, String> sorted = new TreeMap<String, String>();
for (Map.Entry<Object, Object> e : opts.entrySet()) {
String key = (String)e.getKey();
String key = (String) e.getKey();
if (TunnelConfig._noShowSet.contains(key))
continue;
// leave in for HTTP and Connect so it can get migrated to MD5
// hide for SOCKS until migrated to MD5
if ((!isMD5Proxy) &&
TunnelConfig._nonProxyNoShowSet.contains(key))
TunnelConfig._nonProxyNoShowSet.contains(key))
continue;
if (key.startsWith("i2cp.leaseSetClient."))
continue;
sorted.put(key, (String)e.getValue());
sorted.put(key, (String) e.getValue());
}
if (sorted.isEmpty())
return "";
@ -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

@ -4,11 +4,11 @@
if (curTunnel >= 0) {
tunnelTypeName = editBean.getTunnelType(curTunnel);
tunnelType = editBean.getInternalType(curTunnel);
%><h2><%=intl._t("Edit proxy settings")%> (<%=editBean.getTunnelName(curTunnel)%>)</h2><%
%><h2><%=intl._t("Edit proxy settings")%> (<%=editBean.getTunnelName(curTunnel)%>)</h2><%
} else {
tunnelTypeName = editBean.getTypeName(request.getParameter("type"));
tunnelType = net.i2p.data.DataHelper.stripHTML(request.getParameter("type"));
%><h2><%=intl._t("New proxy settings")%></h2><%
%><h2><%=intl._t("New proxy settings")%></h2><%
} %>
<input type="hidden" name="tunnel" value="<%=curTunnel%>" />
<input type="hidden" name="nonce" value="<%=net.i2p.i2ptunnel.web.IndexBean.getNextNonce()%>" />
@ -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%>>
@ -234,7 +234,7 @@
// we don't actually disable the field for a new tunnel, but we provide a warning
String shtitle = (canChangePort && isShared) ?
intl._t("Traffic from all clients with this feature enabled will be routed over the same set of tunnels. This will make profiling the tunnels by an adversary more difficult, but will link the clients together.") :
(isShared ? bStopFirst : aStopFirst);
(isShared ? bStopFirst : aStopFirst);
String shdisabled = canChangePort ? "" : " disabled=\"disabled\" ";
%>
<label title="<%=shtitle%>">
@ -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.
%>
@ -309,7 +309,7 @@
<option value="5"<%=(tunnelDepth == 5 ? " selected=\"selected\"" : "") %>><%=intl.ngettext("{0} hop tunnel", "{0} hop tunnel", 5)%></option>
<option value="6"<%=(tunnelDepth == 6 ? " selected=\"selected\"" : "") %>><%=intl.ngettext("{0} hop tunnel", "{0} hop tunnel", 6)%></option>
<option value="7"<%=(tunnelDepth == 7 ? " selected=\"selected\"" : "") %>><%=intl.ngettext("{0} hop tunnel", "{0} hop tunnel", 7)%></option>
<% } else if (tunnelDepth > 3) {
<% } else if (tunnelDepth > 3) {
%> <option value="<%=tunnelDepth%>" selected="selected"><%=intl.ngettext("{0} hop tunnel", "{0} hop tunnel", tunnelDepth)%></option>
<%
}
@ -355,7 +355,7 @@
<option value="3"<%=(tunnelBackupQuantity == 3 ? " selected=\"selected\"" : "") %>><%=intl.ngettext("{0} backup tunnel", "{0} backup tunnels", 3)%><%=editBean.unlessAdvanced("high redundancy, high resource usage")%></option>
<%
if (tunnelBackupQuantity > 3) {
%> <option value="<%=tunnelBackupQuantity%>" selected="selected"><%=intl.ngettext("{0} backup tunnel", "{0} backup tunnels", tunnelBackupQuantity)%></option>
%> <option value="<%=tunnelBackupQuantity%>" selected="selected"><%=intl.ngettext("{0} backup tunnel", "{0} backup tunnels", tunnelBackupQuantity)%></option>
<%
}
%></select>
@ -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

@ -4,11 +4,11 @@
if (curTunnel >= 0) {
tunnelTypeName = editBean.getTunnelType(curTunnel);
tunnelType = editBean.getInternalType(curTunnel);
%><h2><%=intl._t("Edit Server Settings")%> (<%=editBean.getTunnelName(curTunnel)%>)</h2><%
%><h2><%=intl._t("Edit Server Settings")%> (<%=editBean.getTunnelName(curTunnel)%>)</h2><%
} else {
tunnelTypeName = editBean.getTypeName(request.getParameter("type"));
tunnelType = net.i2p.data.DataHelper.stripHTML(request.getParameter("type"));
%><h2><%=intl._t("New Server Settings")%></h2><%
%><h2><%=intl._t("New Server Settings")%></h2><%
} %>
<input type="hidden" name="tunnel" value="<%=curTunnel%>" />
<input type="hidden" name="nonce" value="<%=net.i2p.i2ptunnel.web.IndexBean.getNextNonce()%>" />
@ -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>
@ -301,7 +302,7 @@
<option value="5"<%=(tunnelDepth == 5 ? " selected=\"selected\"" : "") %>><%=intl.ngettext("{0} hop tunnel", "{0} hop tunnel", 5)%></option>
<option value="6"<%=(tunnelDepth == 6 ? " selected=\"selected\"" : "") %>><%=intl.ngettext("{0} hop tunnel", "{0} hop tunnel", 6)%></option>
<option value="7"<%=(tunnelDepth == 7 ? " selected=\"selected\"" : "") %>><%=intl.ngettext("{0} hop tunnel", "{0} hop tunnel", 7)%></option>
<% } else if (tunnelDepth > 3) {
<% } else if (tunnelDepth > 3) {
%> <option value="<%=tunnelDepth%>" selected="selected"><%=intl.ngettext("{0} hop tunnel", "{0} hop tunnel", tunnelDepth)%></option>
<% }
%></select>
@ -395,7 +396,7 @@
<option value="3"<%=(tunnelBackupQuantity == 3 ? " selected=\"selected\"" : "") %>><%=intl.ngettext("{0} backup tunnel", "{0} backup tunnels", 3)%><%=editBean.unlessAdvanced("high redundancy, high resource usage")%></option>
<%
if (tunnelBackupQuantity > 3) {
%> <option value="<%=tunnelBackupQuantity%>" selected="selected"><%=intl.ngettext("{0} backup tunnel", "{0} backup tunnels", tunnelBackupQuantity)%></option>
%> <option value="<%=tunnelBackupQuantity%>" selected="selected"><%=intl.ngettext("{0} backup tunnel", "{0} backup tunnels", tunnelBackupQuantity)%></option>
<% }
%></select>
</td>
@ -426,7 +427,7 @@
<option value="2"<%=(tunnelBackupQuantityOut == 2 ? " selected=\"selected\"" : "") %>><%=intl.ngettext("{0} backup tunnel", "{0} backup tunnels", 2)%></option>
<option value="3"<%=(tunnelBackupQuantityOut == 3 ? " selected=\"selected\"" : "") %>><%=intl.ngettext("{0} backup tunnel", "{0} backup tunnels", 3)%></option>
<% if (tunnelBackupQuantityOut > 3) {
%> <option value="<%=tunnelBackupQuantityOut%>" selected="selected"><%=intl.ngettext("{0} backup tunnel", "{0} backup tunnels", tunnelBackupQuantity)%></option>
%> <option value="<%=tunnelBackupQuantityOut%>" selected="selected"><%=intl.ngettext("{0} backup tunnel", "{0} backup tunnels", tunnelBackupQuantity)%></option>
<% }
%></select>
</td>
@ -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\" ";
@ -817,7 +818,7 @@
</td>
</tr><tr>
<td colspan="8">
<%=intl._t("You can define an advanced filter for this tunnel.")%> (<a href="http://i2p-projekt.i2p/spec/filter-format" target="_blank"><%=intl._t("Format Specification")%></a>)
<%=intl._t("You can define an advanced filter for this tunnel.")%> (<a href="http://i2p-projekt.i2p/spec/filter-format" target="_blank"><%=intl._t("Format Specification")%></a>)
</td>
</tr><tr>
<td colspan="8">
@ -867,7 +868,7 @@
</th>
</tr>
<%
if (!isStreamrServer) {
if (!isStreamrServer && !isUDPServer) {
%>
<tr>
<th colspan="5">

View File

@ -1,396 +1,536 @@
<%@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"?>
<!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="*" />
<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>
<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>
<%=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>
<%
boolean isInitialized = indexBean.isInitialized();
String nextNonce = isInitialized ? net.i2p.i2ptunnel.web.IndexBean.getNextNonce() : null;
// not synced, oh well
int lastID = indexBean.getLastMessageID();
String msgs = indexBean.getMessages();
if (msgs.length() > 0) {
%>
<div class="panel" id="messages">
<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>
<td class="buttons">
<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>
<%
} // isInitialized
%>
</td>
</tr>
</table>
</div>
<%
} // !msgs.isEmpty()
if (isInitialized) {
%>
<div class="panel" id="globalTunnelControl">
<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>
<%--
//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>
--%>
</td>
</tr>
</table>
</div>
<div class="panel" id="servers">
<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>
</tr>
<%
for (int curServer = 0; curServer < indexBean.getTunnelCount(); curServer++) {
if (indexBean.isClient(curServer)) continue;
%>
<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">
<%
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>
<% } else { %>
<a href="http://<%=indexBean.getServerTarget(curServer)%>/" title="<%=intl._t("Test HTTP server, bypassing I2P")%>" target="_top"><%=indexBean.getServerTarget(curServer)%></a>
<%
}
} else {
%><%=indexBean.getServerTarget(curServer)%>
<%
if (indexBean.isSSLEnabled(curServer)) { %>
SSL
<%
}
}
%>
</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>
<%
} else {
%><%=intl._t("No Preview")%>
<%
}
%>
</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>
<%
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>
<%
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>
<%
break;
}
%>
</td>
</tr><tr>
<td class="tunnelDestination" colspan="6">
<span class="tunnelDestinationLabel">
<%
String name = indexBean.getSpoofedHost(curServer);
if (name == null || name.equals("")) {
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(intl._t("Hostname"));
out.write(":</b></span> ");
out.write(name);
}
%>
</td>
</tr>
<%
String encName = indexBean.getEncryptedBase32(curServer);
if (encName != null && encName.length() > 0) {
%>
<tr>
<td class="tunnelDestination" colspan="6">
<span class="tunnelDestinationLabel"><b><%=intl._t("Encrypted")%>:</b></span>
<%=encName%>
</td>
</tr>
<%
} // encName
%>
<tr>
<td class="tunnelDescription" colspan="2">
<%
String descr = indexBean.getTunnelDescription(curServer);
if (descr != null && descr.length() > 0) {
%>
<span class="tunnelDestinationLabel"><b><%=intl._t("Description")%>:</b></span>
<%=descr%>
<%
} else {
// needed to make the spacing look right
%>&nbsp;<%
} // descr
%>
</td>
<td class="tunnelPreview tunnelPreviewHostname" colspan="1">
<%
if (("httpserver".equals(indexBean.getInternalType(curServer)) || ("httpbidirserver".equals(indexBean.getInternalType(curServer)))) && indexBean.getTunnelStatus(curServer) == IndexBean.RUNNING) {
if (name != null && !name.equals("") && name.endsWith(".i2p") ) {
%>
<textarea wrap="off" class="tunnelPreviewHostname" title="<%=intl._t("Share your site using the hostname")%>">http://<%=indexBean.getSpoofedHost(curServer)%>/?i2paddresshelper=<%=indexBean.getDestHashBase32(curServer)%></textarea>
<%
}
} else {
// needed to make the spacing look right
%>&nbsp;
<%
}
%>
</td>
<td class="tunnelPreview tunnelPreviewHostname" colspan="1">
<%
if (("httpserver".equals(indexBean.getInternalType(curServer)) || ("httpbidirserver".equals(indexBean.getInternalType(curServer)))) && indexBean.getTunnelStatus(curServer) == IndexBean.RUNNING) {
if (name != null && !name.equals("") && name.endsWith(".i2p") ) {
%>
<button class="jsonly control tunnelHostnameCopy tunnelPreview" title="<%=intl._t("Copy the hostname to the clipboard")%>"><%=intl._t("Copy Hostname")%></button>
<%
}
} else {
// needed to make the spacing look right
%>&nbsp;
<%
}
%>
</td>
<td colspan="2" class="tunnelPreviewHostname">
</td>
</tr>
<%
} // for loop
%>
<tr>
<td class="newTunnel" colspan="6">
<form id="addNewServerTunnelForm" action="edit">
<b><%=intl._t("New hidden service")%>:</b>&nbsp;
<select name="type">
<option value="httpserver">HTTP</option>
<option value="server"><%=intl._t("Standard")%></option>
<option value="httpbidirserver">HTTP bidir</option>
<option value="ircserver">IRC</option>
<option value="streamrserver">Streamr</option>
</select>
<input class="control" type="submit" value="<%=intl._t("Create")%>" />
</form>
</td>
</tr>
</table>
</div>
<div class="panel" id="clients">
<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>
</tr>
<%
for (int curClient = 0; curClient < indexBean.getTunnelCount(); curClient++) {
if (!indexBean.isClient(curClient)) continue;
%>
<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">
<%
/* should only happen for streamr client */
String cHost= indexBean.getClientInterface(curClient);
if (cHost == null || "".equals(cHost)) {
out.write("<font color=\"red\">");
out.write(intl._t("Host not set"));
out.write("</font>");
} else {
out.write(cHost);
}
%>
</td><td class="tunnelPort">
<%
String cPort= indexBean.getClientPort2(curClient);
out.write(cPort);
if (indexBean.isSSLEnabled(curClient))
out.write(" SSL");
%>
</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>
<%
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>
<%
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>
<%
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>
<%
break;
}
%>
</td>
</tr><tr>
<td class="tunnelDestination" colspan="6">
<span class="tunnelDestinationLabel">
<% if ("httpclient".equals(indexBean.getInternalType(curClient)) || "connectclient".equals(indexBean.getInternalType(curClient)) ||
"sockstunnel".equals(indexBean.getInternalType(curClient)) || "socksirctunnel".equals(indexBean.getInternalType(curClient))) { %>
<b><%=intl._t("Outproxy")%>:</b>
<% } else { %>
<b><%=intl._t("Destination")%>:</b>
<% } %>
</span>
<%
if (indexBean.getIsUsingOutproxyPlugin(curClient)) {
%><%=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())%><%
} else if (cdest.length() > 0) {
%><%=cdest%><%
} else {
%><i><%=intl._t("none")%></i><%
}
} %>
</td>
</tr>
<% /* TODO SSL outproxy for httpclient if plugin not present */ %>
<tr>
<td class="tunnelDescription" colspan="6">
<%
boolean isShared = indexBean.isSharedClient(curClient);
String descr = indexBean.getTunnelDescription(curClient);
if (isShared || (descr != null && descr.length() > 0)) {
%>
<span class="tunnelDescriptionLabel">
<%
if (isShared) {
%><b><%=intl._t("Shared Client")%><%
} else {
%><b><%=intl._t("Description")%><%
}
if (descr != null && descr.length() > 0) {
%>:</b></span> <%=descr%><%
} else {
%></b></span><%
}
} else {
// needed to make the spacing look right
%>&nbsp;<%
} // descr
%>
</td>
</tr>
<%
} // for loop
%>
<tr>
<td class="newTunnel" colspan="6">
<form id="addNewClientTunnelForm" action="edit">
<b><%=intl._t("New client tunnel")%>:</b>&nbsp;
<select name="type">
<option value="client"><%=intl._t("Standard")%></option>
<option value="httpclient">HTTP/CONNECT</option>
<option value="ircclient">IRC</option>
<option value="sockstunnel">SOCKS 4/4a/5</option>
<option value="socksirctunnel">SOCKS IRC</option>
<option value="connectclient">CONNECT</option>
<option value="streamrclient">Streamr</option>
</select>
<input class="control" type="submit" value="<%=intl._t("Create")%>" />
</form>
</td>
</tr>
</table>
</div>
<%
} // isInitialized()
%>
</body></html>
<%@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"?>
<!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="*" />
<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>
<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>
<%=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>
<%
boolean isInitialized = indexBean.isInitialized();
String nextNonce = isInitialized ? net.i2p.i2ptunnel.web.IndexBean.getNextNonce() : null;
// not synced, oh well
int lastID = indexBean.getLastMessageID();
String msgs = indexBean.getMessages();
if (msgs.length() > 0) {
%>
<div class="panel" id="messages">
<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>
<td class="buttons">
<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>
<%
} // isInitialized
%>
</td>
</tr>
</table>
</div>
<%
} // !msgs.isEmpty()
if (isInitialized) {
%>
<div class="panel" id="globalTunnelControl">
<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>
<%--
//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>
--%>
</td>
</tr>
</table>
</div>
<div class="panel" id="servers">
<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>
</tr>
<%
for (int curServer = 0; curServer < indexBean.getTunnelCount(); curServer++) {
if (indexBean.isClient(curServer)) continue;
%>
<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">
<%
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>
<% } else { %>
<a href="http://<%=indexBean.getServerTarget(curServer)%>/" title="<%=intl._t(" Test HTTP server, bypassing I2P ")%>" target="_top">
<%=indexBean.getServerTarget(curServer)%>
</a>
<%
}
} else {
%>
<%=indexBean.getServerTarget(curServer)%>
<%
if (indexBean.isSSLEnabled(curServer)) { %>
SSL
<%
}
}
%>
</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>
<%
} else {
%>
<%=intl._t("No Preview")%>
<%
}
%>
</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>
<%
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>
<%
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>
<%
break;
}
%>
</td>
</tr>
<tr>
<td class="tunnelDestination" colspan="6">
<span class="tunnelDestinationLabel">
<%
String name = indexBean.getSpoofedHost(curServer);
if (name == null || name.equals("")) {
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(intl._t("Hostname"));
out.write(":</b></span> "); out.write(name); } %>
</td>
</tr>
<%
String encName = indexBean.getEncryptedBase32(curServer);
if (encName != null && encName.length() > 0) {
%>
<tr>
<td class="tunnelDestination" colspan="6">
<span class="tunnelDestinationLabel"><b><%=intl._t("Encrypted")%>:</b></span>
<%=encName%>
</td>
</tr>
<%
} // encName
%>
<tr>
<td class="tunnelDescription" colspan="2">
<%
String descr = indexBean.getTunnelDescription(curServer);
if (descr != null && descr.length() > 0) {
%>
<span class="tunnelDestinationLabel"><b><%=intl._t("Description")%>:</b></span>
<%=descr%>
<%
} else {
// needed to make the spacing look right
%>&nbsp;
<%
} // descr
%>
</td>
<td class="tunnelPreview tunnelPreviewHostname" colspan="1">
<%
if (("httpserver".equals(indexBean.getInternalType(curServer)) || ("httpbidirserver".equals(indexBean.getInternalType(curServer)))) && indexBean.getTunnelStatus(curServer) == IndexBean.RUNNING) {
if (name != null && !name.equals("") && name.endsWith(".i2p") ) {
%>
<textarea wrap="off" class="tunnelPreviewHostname" title="<%=intl._t(" Share your site using the hostname ")%>">http://<%=indexBean.getSpoofedHost(curServer)%>/?i2paddresshelper=<%=indexBean.getDestHashBase32(curServer)%></textarea>
<%
}
} else {
// needed to make the spacing look right
%>&nbsp;
<%
}
%>
</td>
<td class="tunnelPreview tunnelPreviewHostname" colspan="1">
<%
if (("httpserver".equals(indexBean.getInternalType(curServer)) || ("httpbidirserver".equals(indexBean.getInternalType(curServer)))) && indexBean.getTunnelStatus(curServer) == IndexBean.RUNNING) {
if (name != null && !name.equals("") && name.endsWith(".i2p") ) {
%>
<button class="jsonly control tunnelHostnameCopy tunnelPreview" title="<%=intl._t(" Copy the hostname to the clipboard ")%>"><%=intl._t("Copy Hostname")%></button>
<%
}
} else {
// needed to make the spacing look right
%>&nbsp;
<%
}
%>
</td>
<td colspan="2" class="tunnelPreviewHostname">
</td>
</tr>
<%
} // for loop
%>
<tr>
<td class="newTunnel" colspan="6">
<form id="addNewServerTunnelForm" action="edit">
<b><%=intl._t("New hidden service")%>:</b>&nbsp;
<select name="type">
<option value="httpserver">HTTP</option>
<option value="server"><%=intl._t("Standard")%></option>
<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>
</td>
</tr>
</table>
</div>
<div class="panel" id="clients">
<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>
</tr>
<%
for (int curClient = 0; curClient < indexBean.getTunnelCount(); curClient++) {
if (!indexBean.isClient(curClient)) continue;
%>
<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">
<%
/* should only happen for streamr client */
String cHost= indexBean.getClientInterface(curClient);
if (cHost == null || "".equals(cHost)) {
out.write("<font color=\"red\">");
out.write(intl._t("Host not set"));
out.write("</font>");
} else {
out.write(cHost);
}
%>
</td>
<td class="tunnelPort">
<%
String cPort= indexBean.getClientPort2(curClient);
out.write(cPort);
if (indexBean.isSSLEnabled(curClient))
out.write(" SSL");
%>
</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>
<%
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>
<%
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>
<%
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>
<%
break;
}
%>
</td>
</tr>
<tr>
<td class="tunnelDestination" colspan="6">
<span class="tunnelDestinationLabel">
<% if ("httpclient".equals(indexBean.getInternalType(curClient)) || "connectclient".equals(indexBean.getInternalType(curClient)) ||
"sockstunnel".equals(indexBean.getInternalType(curClient)) || "socksirctunnel".equals(indexBean.getInternalType(curClient))) { %>
<b><%=intl._t("Outproxy")%>:</b>
<% } else { %>
<b><%=intl._t("Destination")%>:</b>
<% } %>
</span>
<%
if (indexBean.getIsUsingOutproxyPlugin(curClient)) {
%>
<%=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())%>
<%
} else if (cdest.length() > 0) {
%>
<%=cdest%>
<%
} else {
%><i><%=intl._t("none")%></i>
<%
}
} %>
</td>
</tr>
<% /* TODO SSL outproxy for httpclient if plugin not present */ %>
<tr>
<td class="tunnelDescription" colspan="6">
<%
boolean isShared = indexBean.isSharedClient(curClient);
String descr = indexBean.getTunnelDescription(curClient);
if (isShared || (descr != null && descr.length() > 0)) {
%>
<span class="tunnelDescriptionLabel">
<%
if (isShared) {
%><b><%=intl._t("Shared Client")%><%
} else {
%><b><%=intl._t("Description")%><%
}
if (descr != null && descr.length() > 0) {
%>:</b></span>
<%=descr%>
<%
} else {
%>
</b>
</span>
<%
}
} else {
// needed to make the spacing look right
%>&nbsp;
<%
} // descr
%>
</td>
</tr>
<%
} // for loop
%>
<tr>
<td class="newTunnel" colspan="6">
<form id="addNewClientTunnelForm" action="edit">
<b><%=intl._t("New client tunnel")%>:</b>&nbsp;
<select name="type">
<option value="client"><%=intl._t("Standard")%></option>
<option value="httpclient">HTTP/CONNECT</option>
<option value="ircclient">IRC</option>
<option value="sockstunnel">SOCKS 4/4a/5</option>
<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>
</td>
</tr>
</table>
</div>
<%
} // isInitialized()
%>
</body>
</html>