Compare commits

...

10 Commits

9 changed files with 562 additions and 14 deletions

View File

@ -43,6 +43,7 @@ import java.lang.reflect.Constructor;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@ -78,6 +79,7 @@ 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.util.EventDispatcherImpl;
import net.i2p.util.Log;
import net.i2p.util.OrderedProperties;
@ -457,6 +459,12 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
runStreamrClient(args, l);
} else if ("streamrserver".equals(cmdname)) {
runStreamrServer(args, l);
} else if ("udpclient".equals(cmdname)) {
runUDPClient(args, l);
} else if ("udpserver".equals(cmdname)) {
runUDPServer(args, l);
} else if ("udppeer".equals(cmdname)) {
runUDPPeer(args, l);
} else if ("config".equals(cmdname)) {
runConfig(args, l);
} else if ("listen_on".equals(cmdname)) {
@ -1361,6 +1369,60 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
}
}
public void runUDPClient(String args[], Logging l) {
if (_log.shouldLog(Log.INFO))
_log.info(getPrefix() + "Running UDP client with args" + Arrays.toString(args));
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("udptunnelTaskId", 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("udptunnelTaskId", Integer.valueOf(-1));
}
if (_port <= 0)
throw new IllegalArgumentException(getPrefix() + "Bad port " + args[0]);
try {
I2PTunnelUDPClient task = new I2PTunnelUDPClient(_host.getHostAddress(), _port, args[2], l, this, this);
//(lhost, lport, destina, l, notifyThis, tunnel)
task.startRunning();
addtask(task);
notifyEvent("udptunnelTaskId", 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("udptunnnelTaskId", Integer.valueOf(-1));
throw iae;
}
} else {
l.log("udpclient <host> <port> <destination>\n" +
" creates a tunnel that receives UDP data.");
notifyEvent("udptunnelTaskId", Integer.valueOf(-1));
}
}
public void runUDPServer(String args[], Logging l) {
}
public void runUDPPeer(String args[], Logging l) {
}
/**
* Specify the i2cp host and port
* Deprecated - only used by CLI

View File

@ -166,6 +166,9 @@ public class TunnelController implements Logging {
public static final String TYPE_STREAMR_CLIENT = "streamrclient";
/** Server in the UI and I2P side but a client on the localhost side */
public static final String TYPE_STREAMR_SERVER = "streamrserver";
public static final String TYPE_UDP_CLIENT = "udpclient";
public static final String TYPE_UDP_SERVER = "udpserver";
public static final String TYPE_UDP_PEER = "udppeer";
/**
* This is guaranteed to be available.
@ -482,6 +485,8 @@ public class TunnelController implements Logging {
startClient();
} else if (TYPE_STREAMR_CLIENT.equals(type)) {
startStreamrClient();
} else if (TYPE_UDP_CLIENT.equals(type)) {
startUDPClient();
} else if (TYPE_STD_SERVER.equals(type)) {
startServer();
} else if (TYPE_HTTP_SERVER.equals(type)) {
@ -492,6 +497,10 @@ public class TunnelController implements Logging {
startIrcServer();
} else if (TYPE_STREAMR_SERVER.equals(type)) {
startStreamrServer();
} else if (TYPE_UDP_SERVER.equals(type)) {
startUDPServer();
} else if (TYPE_UDP_PEER.equals(type)) {
startUDPPeer();
} else {
changeState(TunnelState.STOPPED);
if (_log.shouldLog(Log.ERROR))
@ -605,6 +614,47 @@ public class TunnelController implements Logging {
_tunnel.runStreamrServer(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);
}
/**
* 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 peer is a UDP client on one port, and a UDP server on
* another, sort of like HTTP Bidirectional.
* use the targetPort field for server listenPort
* and the listenOn field for the client listenPort
*/
private void startUDPPeer() {
String listenOn = getListenOnInterface();
if ( (listenOn != null) && (listenOn.length() > 0) ) {
_tunnel.runListenOn(new String[] { listenOn }, this);
}
String listenPort = getTargetPort();
String targetHost = getTargetHost();
String targetPort = getListenPort();
String privKeyFile = getPrivKeyFile();
_tunnel.runUDPPeer(new String[] { listenPort, targetHost, targetPort, privKeyFile }, this);
}
/**
* Note the fact that we are using some sessions, so that they dont get
* closed by some other tunnels
@ -1076,6 +1126,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);
}

View File

@ -0,0 +1,204 @@
package net.i2p.i2ptunnel.udpTunnel;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.Map;
import net.i2p.client.streaming.I2PSocketAddress;
import net.i2p.data.Destination;
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.I2PAppThread;
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 implements Runnable {
private final Log _log = new Log(I2PTunnelUDPClient.class);
private final static int MAX_DATAGRAM_SIZE = 31744;
private final Destination _remoteDestination;
// UDP Side
// permanent DatagramSocket at e.g. localhost:5353
private final DatagramSocket _localSocket;
// InetAddress corresponding to local DatagramSocket
private final InetAddress _localAddress;
// UDP port corresponding to local DatagramSocket
private final int _localPort;
//private final int UDP_PORT;
//private final int MAX_SIZE = 1024;
private final UDPSink _sink;
private final UDPSource _source;
private final Thread thread;
// SourceIP/Port table
private Map<Integer, InetSocketAddress> _sourceIPPortTable = new HashMap<>();
private class outboundRequest {
public final InetAddress _sourceAddress;
public final int _i2cpSourcePort;
public final byte[] _request;
public int length() {
return _request.length;
}
public int sendRequestToI2PServer() {
try {
//DatagramPacket packet = new DatagramPacket(_request, _request.length, _serverAddress, _serverPort);
//send(packet);
send(_remoteDestination, _i2cpSourcePort, 0, _request);
return _i2cpSourcePort;
} catch (Exception e) {
_log.error("Error sending request to I2PServer", e);
}
return 0;
}
public outboundRequest(InetAddress sourceAddress, int sourcePort, byte[] request) {
_sourceAddress = sourceAddress;
_i2cpSourcePort = sourcePort;
_request = request;
}
}
// Constructor, host is localhost(usually) or the host of the UDP client, port
// is the port of the UDP client
public I2PTunnelUDPClient(String localhost, int localport, String destination, Logging l, EventDispatcher notifyThis,
I2PTunnel tunnel) {
super(destination, l, notifyThis, tunnel);
_log.debug("I2PTunnelUDPClient: " + localhost + ":" + localport + " -> " + destination);
this.thread = new I2PAppThread(this);
try {
this._localAddress = InetAddress.getByName(localhost);
this._localPort = localport;
} catch (Exception e) {
_log.error("I2PTunnelUDPClient: " + e.getMessage());
throw new RuntimeException(e);
}
try {
this._localSocket = new DatagramSocket(new InetSocketAddress(_localAddress, _localPort));
} catch (Exception e) {
_log.error("I2PTunnelUDPClient: " + e.getMessage());
throw new RuntimeException(e);
}
try {
this._remoteDestination = new Destination(destination);
} catch (Exception e) {
_log.error("I2PTunnelUDPClient: " + e.getMessage());
throw new RuntimeException(e);
}
_sink = new UDPSink(this._localSocket, this._localAddress, this._localPort);
//_localSocket, _remoteDestination, _log, notifyThis, tunnel);
//_sink.start();
this._source = new UDPSource(this._localSocket);
this.setSink(this._sink);
}
private DatagramPacket readDatagramFromLocalSocket() {
byte[] buf = new byte[MAX_DATAGRAM_SIZE];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
try {
_localSocket.receive(packet);
if (_log.shouldLog(Log.DEBUG)){
_log.debug("I2PTunnelUDPClient: readDatagramFromLocalSocket: " + packet.getLength() + " bytes from " + packet.getAddress().getHostAddress() + ":" + packet.getPort());
}
} catch (Exception e) {
_log.error("I2PTunnelUDPClient: " + e.getMessage());
throw new RuntimeException(e);
}
return packet;
}
private outboundRequest readOutboundRequestFromLocalDatagram(){
DatagramPacket packet = readDatagramFromLocalSocket();
if (_log.shouldLog(Log.DEBUG)){
_log.debug("I2PTunnelUDPClient: readOutboundRequestFromLocalDatagram: " + packet.getLength() + " bytes from " + packet.getAddress().getHostAddress() + ":" + packet.getPort());
}
InetAddress sourceAddress = packet.getAddress();
if (_log.shouldLog(Log.DEBUG)){
_log.debug("I2PTunnelUDPClient: readOutboundRequestFromLocalDatagram: " + sourceAddress.getHostAddress() + ":" + packet.getPort());
}
int sourcePort = newI2CPSourcePort();
if (_log.shouldLog(Log.DEBUG)){
_log.debug("I2PTunnelUDPClient: readOutboundRequestFromLocalDatagram: " + sourcePort);
}
byte[] request = packet.getData();
if (_log.shouldLog(Log.DEBUG)){
_log.debug("I2PTunnelUDPClient: readOutboundRequestFromLocalDatagram: " + request.length);
}
return new outboundRequest(sourceAddress, sourcePort, request);
}
private int sendOutboundRequest(){
outboundRequest request = readOutboundRequestFromLocalDatagram();
int i2cpSourcePort = request.sendRequestToI2PServer();
_sourceIPPortTable.put(i2cpSourcePort, new InetSocketAddress(_localSocket.getInetAddress(), _localSocket.getPort()));
return request.sendRequestToI2PServer();
}
private int newI2CPSourcePort() {
int randomPort = (int) (Math.random() * 55535);
while (_sourceIPPortTable.containsKey(randomPort)) {
randomPort = (int) (Math.random() * 55535);
}
return randomPort+10000;
}
private void recieveInboundResponse() {
}
@Override
public final void startRunning() {
_log.logAlways(Log.INFO, "UDP Client is starting");
super.startRunning();
_log.logAlways(Log.INFO, "UDP Client is nearly started");
this.thread.start();
_log.logAlways(Log.INFO, "UDP Client is ready");
}
public void run() {
_log.logAlways(Log.INFO, "UDP Client is running");
while (true) {
int i2cpPortSent = this.sendOutboundRequest();
_log.debug("I2PTunnelUDPClient: sent outbound request to I2PServer, i2cpPortSent: " + i2cpPortSent);
//this.receive();
}
}
@Override
public boolean close(boolean forced) {
return super.close(forced);
}
}

View File

@ -0,0 +1,152 @@
package net.i2p.i2ptunnel.udpTunnel;
import java.io.File;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import net.i2p.client.streaming.I2PSocketAddress;
import net.i2p.i2ptunnel.I2PTunnel;
import net.i2p.i2ptunnel.Logging;
import net.i2p.i2ptunnel.udp.UDPSink;
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(I2PTunnelUDPServer.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(I2PTunnelUDPClient.java):
*
* - 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 I2PTunnelUDPServer extends I2PTunnelUDPServerBase {
private final static int MAX_DATAGRAM_SIZE = 31744;
private final Log _log = new Log(I2PTunnelUDPServer.class);
private final InetAddress _localAddress;
private final int _localPort;
private Map<Integer, inboundDatagram> _sourceIPPortTable = new HashMap<Integer, inboundDatagram>();
// outboundReply sends a DatagramPacket to the client over I2P
private class outboundReply {
public final I2PSocketAddress _destination;
public final int _destinationPort; // I2CP source port
public final byte[] _reply;
public int length() {
return _reply.length;
}
public void sendReplyToI2PClient() {
try {
_log.debug("Sending reply to I2P client");
//_socket.send(_reply);
} catch (Exception e) {
_log.error("Error sending reply to I2P client", e);
}
}
public outboundReply(I2PSocketAddress destination, int destinationPort, byte[] reply) {
_destination = destination;
_destinationPort = destinationPort;
_reply = reply;
}
}
// inboundDatagram stores the source IP/port associated with a request recieved from I2P
// and forwards it to a UDP socket on the localhost.
private class inboundDatagram {
public final I2PSocketAddress _destination;
public final int _i2cpPort;
public final byte[] _request;
public final DatagramSocket socket;
public int length() {
return _request.length;
}
// sends the datagram to the local UDP socket, reads a single reply, then closes the DatagramSocket
public outboundReply sendRequestToLocalhost() {
//construct a datagram packet
DatagramPacket packet = new DatagramPacket(_request, _request.length, _localAddress, _localPort);
try {
socket.send(packet);
} catch (Exception e) {
_log.error("Error sending datagram", e);
}
//read the reply
byte[] reply = new byte[MAX_DATAGRAM_SIZE];
DatagramPacket replyPacket = new DatagramPacket(reply, reply.length);
try {
socket.receive(replyPacket);
} catch (Exception e) {
_log.error("Error receiving datagram", e);
}
//close the socket
socket.close();
//construct the outbound reply
return new outboundReply(_destination, _i2cpPort, reply);
}
public inboundDatagram(I2PSocketAddress dest, int i2cpPort, byte[] data) {
this._destination = dest;
this._i2cpPort = i2cpPort;
this._request = data;
// create a new DatagramSocket with an EPHEMERAL port
try {
this.socket = new DatagramSocket();
} catch (Exception e) {
_log.error("Error creating DatagramSocket", e);
throw new RuntimeException(e);
}
}
}
// Constructor, host is localhost(usually) or the host of the UDP client, port
// is the port of the UDP client
public I2PTunnelUDPServer(String host, int port, File privKeyFile, String privKeyPath, Logging l, EventDispatcher notifyThis,
I2PTunnel tunnel) {
super(privKeyFile, privKeyPath, l, notifyThis, tunnel);
try {
_localAddress = InetAddress.getByName(host);
} catch (Exception e) {
_log.error("Error getting InetAddress for host: " + host, e);
throw new RuntimeException(e);
}
_localPort = port;
}
//private inboundDatagram receiveInboundI2PDatagram() {
//}
@Override
public final void startRunning() {
super.startRunning();
while (true) {
}
}
@Override
public boolean close(boolean forced) {
return super.close(forced);
}
}

View File

@ -0,0 +1,71 @@
package net.i2p.i2ptunnel.udpTunnel;
import java.io.File;
import net.i2p.data.Destination;
import net.i2p.i2ptunnel.I2PTunnel;
import net.i2p.i2ptunnel.I2PTunnelTask;
import net.i2p.i2ptunnel.Logging;
import net.i2p.i2ptunnel.udp.Sink;
import net.i2p.i2ptunnel.udp.Source;
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 I2PTunnelTask implements Source, Sink {
private final Log _log = new Log(I2PTunnelUDPServerClient.class);
public I2PTunnelUDPServerClient(String host, int port, File privkey, String privkeyname, Logging l,
EventDispatcher notifyThis,
I2PTunnel tunnel) {
super("UDPPeer <- " + privkeyname, notifyThis, tunnel);
//super(privkey, privkeyname, l, notifyThis, tunnel);
}
public final void start() {
//super.startRunning();
if (_log.shouldInfo())
_log.info("I2PTunnelUDPServer server ready");
}
@Override
public boolean close(boolean forced) {
return forced;
//return super.close(forced);
}
@Override
public void send(Destination src, int fromPort, int toPort, byte[] data) {
// TODO
}
@Override
public void setSink(Sink sink) {
// TODO
}
}

View File

@ -509,6 +509,7 @@ public class GeneralHelper {
String rv;
if (TunnelController.TYPE_STD_CLIENT.equals(tun.getType()) ||
TunnelController.TYPE_IRC_CLIENT.equals(tun.getType()) ||
TunnelController.TYPE_UDP_CLIENT.equals(tun.getType()) ||
TunnelController.TYPE_STREAMR_CLIENT.equals(tun.getType()))
rv = tun.getTargetDestination();
else

View File

@ -761,6 +761,7 @@ public class TunnelConfig {
// override bundle setting set above
if (!TunnelController.isClient(_type) &&
!TunnelController.TYPE_HTTP_SERVER.equals(_type) &&
!TunnelController.TYPE_UDP_SERVER.equals(_type) &&
!TunnelController.TYPE_STREAMR_SERVER.equals(_type)) {
config.setProperty(TunnelController.OPT_BUNDLE_REPLY, "true");
}
@ -815,7 +816,9 @@ 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) ||
TunnelController.TYPE_UDP_PEER.equals(_type)) {
if (_targetDestination != null)
config.setProperty(TunnelController.PROP_DEST, _targetDestination);
} else if (TunnelController.TYPE_HTTP_SERVER.equals(_type) ||
@ -826,7 +829,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)

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

@ -239,6 +239,7 @@
<option value="httpbidirserver">HTTP bidir</option>
<option value="ircserver">IRC</option>
<option value="streamrserver">Streamr</option>
<option value="udpserver">UDP Peer(Server/Client)</option>
</select>
<input class="control" type="submit" value="<%=intl._t("Create")%>" />
</form>
@ -381,6 +382,7 @@
<option value="socksirctunnel">SOCKS IRC</option>
<option value="connectclient">CONNECT</option>
<option value="streamrclient">Streamr</option>
<option value="udpclient">UDP Peer(Client/Server)</option>
</select>
<input class="control" type="submit" value="<%=intl._t("Create")%>" />
</form>