2005-10-10 dust
* Implemented a new I2PTunnelIRCClient which locally filters inbound and outbound IRC commands for anonymity and security purposes, removing all CTCP messages except ACTION, as well as stripping the hostname from the USER message (while leaving the nick and 'full name'). The IRC proxy doesn't use this by default, but you can enable it by creating a new "IRC proxy" tunnel on the web interface, or by changing the tunnel type to "ircclient" in i2ptunnel.config. 2005-10-10 jrandom * I2PTunnel http client config cleanup and stats * Minor SSU congestion tweaks and stats * Reduced netDb exploration period
This commit is contained in:
@ -291,6 +291,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("genkeys <privkeyfile> [<pubkeyfile>]");
|
||||
l.log("gentextkeys");
|
||||
l.log("client <port> <pubkey>[,<pubkey,...]|file:<pubkeyfile> [<sharedClient>]");
|
||||
l.log("ircclient <port> <pubkey>[,<pubkey,...]|file:<pubkeyfile> [<sharedClient>]");
|
||||
l.log("httpclient <port> [<sharedClient>] [<proxy>]");
|
||||
l.log("lookup <name>");
|
||||
l.log("quit");
|
||||
@ -596,6 +597,60 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run an IRC client on the given port number
|
||||
*
|
||||
* Sets the event "ircclientTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error).
|
||||
* Also sets "ircclientStatus" = "ok" or "error" after the client tunnel has started.
|
||||
* parameter sharedClient is a String, either "true" or "false"
|
||||
*
|
||||
* @param args {portNumber,destinationBase64 or "file:filename" [, sharedClient]}
|
||||
* @param l logger to receive events and output
|
||||
*/
|
||||
public void runIrcClient(String args[], Logging l) {
|
||||
if (args.length >= 2 && args.length <= 3) {
|
||||
int port = -1;
|
||||
try {
|
||||
port = Integer.parseInt(args[0]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
|
||||
notifyEvent("ircclientTaskId", new Integer(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isShared = true;
|
||||
if (args.length > 1) {
|
||||
if ("true".equalsIgnoreCase(args[2].trim())) {
|
||||
isShared = true;
|
||||
} else if ("false".equalsIgnoreCase(args[1].trim())) {
|
||||
_log.warn("args[1] == [" + args[1] + "] and rejected explicitly");
|
||||
isShared = false;
|
||||
} else {
|
||||
// isShared not specified, default to true
|
||||
isShared = true;
|
||||
}
|
||||
}
|
||||
|
||||
I2PTunnelTask task;
|
||||
ownDest = !isShared;
|
||||
try {
|
||||
task = new I2PTunnelIRCClient(port, args[1],l, ownDest, (EventDispatcher) this, this);
|
||||
addtask(task);
|
||||
notifyEvent("ircclientTaskId", new Integer(task.getId()));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
_log.error(getPrefix() + "Invalid I2PTunnel config to create an ircclient [" + host + ":"+ port + "]", iae);
|
||||
l.log("Invalid I2PTunnel configuration [" + host + ":" + port + "]");
|
||||
notifyEvent("ircclientTaskId", new Integer(-1));
|
||||
}
|
||||
} else {
|
||||
l.log("ircclient <port> [<sharedClient>]");
|
||||
l.log(" creates a client that filter IRC protocol.");
|
||||
l.log(" <sharedClient> (optional) indicates if this client shares tunnels with other clients (true of false)");
|
||||
notifyEvent("ircclientTaskId", new Integer(-1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run an SOCKS tunnel on the given port number
|
||||
*
|
||||
|
@ -17,6 +17,7 @@ import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
@ -31,6 +32,7 @@ import net.i2p.util.Log;
|
||||
public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runnable {
|
||||
|
||||
private static final Log _log = new Log(I2PTunnelClientBase.class);
|
||||
protected I2PAppContext _context;
|
||||
protected Logging l;
|
||||
|
||||
static final long DEFAULT_CONNECT_TIMEOUT = 60 * 1000;
|
||||
@ -101,6 +103,12 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
this.l = l;
|
||||
this.handlerName = handlerName + _clientId;
|
||||
|
||||
_context = tunnel.getContext();
|
||||
_context.statManager().createRateStat("i2ptunnel.client.closeBacklog", "How many pending sockets remain when we close one due to backlog?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.client.closeNoBacklog", "How many pending sockets remain when it was removed prior to backlog timeout?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.client.manageTime", "How long it takes to accept a socket and fire it into an i2ptunnel runner (or queue it for the pool)?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.client.buildRunTime", "How long it takes to run a queued socket into an i2ptunnel runner?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
|
||||
// no need to load the netDb with leaseSets for destinations that will never
|
||||
// be looked up
|
||||
tunnel.getClientOptions().setProperty("i2cp.dontPublishLeaseSet", "true");
|
||||
@ -362,7 +370,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
|
||||
while (true) {
|
||||
Socket s = ss.accept();
|
||||
long before = System.currentTimeMillis();
|
||||
manageConnection(s);
|
||||
long total = System.currentTimeMillis() - before;
|
||||
_context.statManager().addRateData("i2ptunnel.client.manageTime", total, total);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
_log.error("Error listening for connections on " + localPort, ex);
|
||||
@ -422,14 +433,20 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
private Socket _s;
|
||||
public CloseEvent(Socket s) { _s = s; }
|
||||
public void timeReached() {
|
||||
int remaining = 0;
|
||||
boolean stillWaiting = false;
|
||||
synchronized (_waitingSockets) {
|
||||
stillWaiting = _waitingSockets.remove(_s);
|
||||
remaining = _waitingSockets.size();
|
||||
}
|
||||
if (stillWaiting) {
|
||||
try { _s.close(); } catch (IOException ioe) {}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
if (_log.shouldLog(Log.INFO)) {
|
||||
_context.statManager().addRateData("i2ptunnel.client.closeBacklog", remaining, 0);
|
||||
_log.info("Closed a waiting socket because of backlog");
|
||||
}
|
||||
} else {
|
||||
_context.statManager().addRateData("i2ptunnel.client.closeNoBacklog", remaining, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -496,8 +513,12 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
}
|
||||
} catch (InterruptedException ie) {}
|
||||
|
||||
if (s != null)
|
||||
if (s != null) {
|
||||
long before = System.currentTimeMillis();
|
||||
clientConnectionRun(s);
|
||||
long total = System.currentTimeMillis() - before;
|
||||
_context.statManager().addRateData("i2ptunnel.client.buildRunTime", total, 0);
|
||||
}
|
||||
s = null;
|
||||
}
|
||||
}
|
||||
|
@ -502,10 +502,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
String remoteID;
|
||||
|
||||
Properties opts = new Properties();
|
||||
opts.setProperty("i2p.streaming.inactivityTimeout", ""+120*1000);
|
||||
//opts.setProperty("i2p.streaming.inactivityTimeout", ""+120*1000);
|
||||
// 1 == disconnect. see ConnectionOptions in the new streaming lib, which i
|
||||
// dont want to hard link to here
|
||||
opts.setProperty("i2p.streaming.inactivityTimeoutAction", ""+1);
|
||||
//opts.setProperty("i2p.streaming.inactivityTimeoutAction", ""+1);
|
||||
I2PSocket i2ps = createI2PSocket(dest, getDefaultOptions(opts));
|
||||
byte[] data = newRequest.toString().getBytes("ISO-8859-1");
|
||||
Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
|
@ -0,0 +1,387 @@
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.EventDispatcher;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable {
|
||||
|
||||
private static final Log _log = new Log(I2PTunnelIRCClient.class);
|
||||
|
||||
/** used to assign unique IDs to the threads / clients. no logic or functionality */
|
||||
private static volatile long __clientId = 0;
|
||||
|
||||
/** list of Destination objects that we point at */
|
||||
protected List dests;
|
||||
private static final long DEFAULT_READ_TIMEOUT = 5*60*1000; // -1
|
||||
protected long readTimeout = DEFAULT_READ_TIMEOUT;
|
||||
|
||||
/**
|
||||
* @throws IllegalArgumentException if the I2PTunnel does not contain
|
||||
* valid config to contact the router
|
||||
*/
|
||||
public I2PTunnelIRCClient(
|
||||
int localPort,
|
||||
String destinations,
|
||||
Logging l,
|
||||
boolean ownDest,
|
||||
EventDispatcher notifyThis,
|
||||
I2PTunnel tunnel) throws IllegalArgumentException {
|
||||
super(localPort,
|
||||
ownDest,
|
||||
l,
|
||||
notifyThis,
|
||||
"IRCHandler " + (++__clientId), tunnel);
|
||||
|
||||
StringTokenizer tok = new StringTokenizer(destinations, ",");
|
||||
dests = new ArrayList(1);
|
||||
while (tok.hasMoreTokens()) {
|
||||
String destination = tok.nextToken();
|
||||
try {
|
||||
Destination dest = I2PTunnel.destFromName(destination);
|
||||
if (dest == null)
|
||||
l.log("Could not resolve " + destination);
|
||||
else
|
||||
dests.add(dest);
|
||||
} catch (DataFormatException dfe) {
|
||||
l.log("Bad format parsing \"" + destination + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
if (dests.size() <= 0) {
|
||||
l.log("No target destinations found");
|
||||
notifyEvent("openClientResult", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
setName(getLocalPort() + " -> IRCClient");
|
||||
|
||||
startRunning();
|
||||
|
||||
notifyEvent("openIRCClientResult", "ok");
|
||||
}
|
||||
|
||||
protected void clientConnectionRun(Socket s) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("got a connection.");
|
||||
Destination dest = pickDestination();
|
||||
I2PSocket i2ps = null;
|
||||
try {
|
||||
i2ps = createI2PSocket(dest);
|
||||
i2ps.setReadTimeout(readTimeout);
|
||||
Thread in = new Thread(new IrcInboundFilter(s,i2ps));
|
||||
in.start();
|
||||
Thread out = new Thread(new IrcOutboundFilter(s,i2ps));
|
||||
out.start();
|
||||
} catch (Exception ex) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error connecting", ex);
|
||||
l.log(ex.getMessage());
|
||||
closeSocket(s);
|
||||
if (i2ps != null) {
|
||||
synchronized (sockLock) {
|
||||
mySockets.remove(sockLock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final Destination pickDestination() {
|
||||
int size = dests.size();
|
||||
if (size <= 0) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("No client targets?!");
|
||||
return null;
|
||||
}
|
||||
if (size == 1) // skip the rand in the most common case
|
||||
return (Destination)dests.get(0);
|
||||
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
|
||||
return (Destination)dests.get(index);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
*/
|
||||
private class IrcInboundFilter implements Runnable {
|
||||
|
||||
private Socket local;
|
||||
private I2PSocket remote;
|
||||
|
||||
IrcInboundFilter(Socket _local, I2PSocket _remote) {
|
||||
local=_local;
|
||||
remote=_remote;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
InputStream input;
|
||||
OutputStream output;
|
||||
try {
|
||||
input=remote.getInputStream();
|
||||
output=local.getOutputStream();
|
||||
} catch (IOException e) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("IrcInboundFilter: no streams",e);
|
||||
return;
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("IrcInboundFilter: Running.");
|
||||
while(true)
|
||||
{
|
||||
try {
|
||||
String inmsg = DataHelper.readLine(input);
|
||||
if(inmsg==null)
|
||||
break;
|
||||
if(inmsg.endsWith("\r"))
|
||||
inmsg=inmsg.substring(0,inmsg.length()-1);
|
||||
String outmsg = inboundFilter(inmsg);
|
||||
if(outmsg!=null)
|
||||
{
|
||||
if(!inmsg.equals(outmsg)) {
|
||||
if (_log.shouldLog(Log.WARN)) {
|
||||
_log.warn("inbound FILTERED: "+outmsg);
|
||||
_log.warn(" - inbound was: "+inmsg);
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("inbound: "+outmsg);
|
||||
}
|
||||
outmsg=outmsg+"\n";
|
||||
output.write(outmsg.getBytes());
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("inbound BLOCKED: "+inmsg);
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("IrcInboundFilter: disconnected",e1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
local.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
if(_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("IrcInboundFilter: Done.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
*/
|
||||
private class IrcOutboundFilter implements Runnable {
|
||||
|
||||
private Socket local;
|
||||
private I2PSocket remote;
|
||||
|
||||
IrcOutboundFilter(Socket _local, I2PSocket _remote) {
|
||||
local=_local;
|
||||
remote=_remote;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
InputStream input;
|
||||
OutputStream output;
|
||||
try {
|
||||
input=local.getInputStream();
|
||||
output=remote.getOutputStream();
|
||||
} catch (IOException e) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("IrcOutboundFilter: no streams",e);
|
||||
return;
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("IrcOutboundFilter: Running.");
|
||||
while(true)
|
||||
{
|
||||
try {
|
||||
String inmsg = DataHelper.readLine(input);
|
||||
if(inmsg==null)
|
||||
break;
|
||||
if(inmsg.endsWith("\r"))
|
||||
inmsg=inmsg.substring(0,inmsg.length()-1);
|
||||
String outmsg = outboundFilter(inmsg);
|
||||
if(outmsg!=null)
|
||||
{
|
||||
if(!inmsg.equals(outmsg)) {
|
||||
if (_log.shouldLog(Log.WARN)) {
|
||||
_log.warn("outbound FILTERED: "+outmsg);
|
||||
_log.warn(" - outbound was: "+inmsg);
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("outbound: "+outmsg);
|
||||
}
|
||||
outmsg=outmsg+"\n";
|
||||
output.write(outmsg.getBytes());
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("outbound BLOCKED: "+"\""+inmsg+"\"");
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("IrcOutboundFilter: disconnected",e1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
remote.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("IrcOutboundFilter: Done.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
*/
|
||||
|
||||
public static String inboundFilter(String s) {
|
||||
|
||||
String field[]=s.split(" ",4);
|
||||
String command;
|
||||
int idx=0;
|
||||
final String[] allowedCommands =
|
||||
{
|
||||
"NOTICE",
|
||||
"PING",
|
||||
"PONG",
|
||||
"MODE",
|
||||
"JOIN",
|
||||
"NICK",
|
||||
"QUIT",
|
||||
"PART",
|
||||
"WALLOPS",
|
||||
"ERROR"
|
||||
};
|
||||
|
||||
if(field[0].charAt(0)==':')
|
||||
idx++;
|
||||
|
||||
command = field[idx++];
|
||||
|
||||
idx++; //skip victim
|
||||
|
||||
// Allow numerical responses
|
||||
try {
|
||||
new Integer(command);
|
||||
return s;
|
||||
} catch(NumberFormatException nfe){}
|
||||
|
||||
// Allow all allowedCommands
|
||||
for(int i=0;i<allowedCommands.length;i++) {
|
||||
if(allowedCommands[i].equals(command))
|
||||
return s;
|
||||
}
|
||||
|
||||
// Allow PRIVMSG, but block CTCP.
|
||||
if("PRIVMSG".equals(command))
|
||||
{
|
||||
String msg;
|
||||
msg = field[idx++];
|
||||
|
||||
byte[] bytes = msg.getBytes();
|
||||
if(bytes[1]==0x01)
|
||||
{
|
||||
// CTCP
|
||||
msg=msg.substring(2);
|
||||
if(msg.startsWith("ACTION ")) {
|
||||
// /me says hello
|
||||
return s;
|
||||
}
|
||||
return null; // Block all other ctcp
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// Block the rest
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String outboundFilter(String s) {
|
||||
|
||||
String field[]=s.split(" ",3);
|
||||
String command;
|
||||
final String[] allowedCommands =
|
||||
{
|
||||
"NOTICE",
|
||||
"PONG",
|
||||
"MODE",
|
||||
"JOIN",
|
||||
"NICK",
|
||||
"WHO",
|
||||
"WHOIS",
|
||||
"LIST",
|
||||
"NAMES",
|
||||
"NICK",
|
||||
"QUIT",
|
||||
"SILENCE",
|
||||
"PART",
|
||||
"OPER",
|
||||
"PING",
|
||||
"KICK",
|
||||
"HELPME",
|
||||
"RULES"
|
||||
};
|
||||
|
||||
if(field[0].charAt(0)==':')
|
||||
return null; // wtf
|
||||
|
||||
command = field[0].toUpperCase();
|
||||
|
||||
// Allow all allowedCommands
|
||||
for(int i=0;i<allowedCommands.length;i++)
|
||||
{
|
||||
if(allowedCommands[i].equals(command))
|
||||
return s;
|
||||
}
|
||||
|
||||
// Allow PRIVMSG, but block CTCP (except ACTION).
|
||||
if("PRIVMSG".equals(command))
|
||||
{
|
||||
String msg;
|
||||
msg = field[2];
|
||||
|
||||
byte[] bytes = msg.getBytes();
|
||||
if(bytes[1]==0x01)
|
||||
{
|
||||
// CTCP
|
||||
msg=msg.substring(2);
|
||||
if(msg.startsWith("ACTION ")) {
|
||||
// /me says hello
|
||||
return s;
|
||||
}
|
||||
return null; // Block all other ctcp
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
if("USER".equals(command)) {
|
||||
int idx = field[2].lastIndexOf(":");
|
||||
if(idx<0)
|
||||
return "USER user hostname localhost :realname";
|
||||
String realname = field[2].substring(idx+1);
|
||||
String ret = "USER "+field[1]+" hostname localhost :"+realname;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Block the rest
|
||||
return null;
|
||||
}
|
||||
}
|
@ -135,6 +135,8 @@ public class TunnelController implements Logging {
|
||||
}
|
||||
if ("httpclient".equals(type)) {
|
||||
startHttpClient();
|
||||
}else if("ircclient".equals(type)) {
|
||||
startIrcClient();
|
||||
} else if ("client".equals(type)) {
|
||||
startClient();
|
||||
} else if ("server".equals(type)) {
|
||||
@ -162,6 +164,18 @@ public class TunnelController implements Logging {
|
||||
_running = true;
|
||||
}
|
||||
|
||||
private void startIrcClient() {
|
||||
setI2CPOptions();
|
||||
setSessionOptions();
|
||||
setListenOn();
|
||||
String listenPort = getListenPort();
|
||||
String dest = getTargetDestination();
|
||||
String sharedClient = getSharedClient();
|
||||
_tunnel.runIrcClient(new String[] { listenPort, dest, sharedClient }, this);
|
||||
acquire();
|
||||
_running = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note the fact that we are using some sessions, so that they dont get
|
||||
* closed by some other tunnels
|
||||
|
@ -30,7 +30,9 @@ public class EditBean extends IndexBean {
|
||||
if (controllers.size() > tunnel) {
|
||||
TunnelController cur = (TunnelController)controllers.get(tunnel);
|
||||
if (cur == null) return false;
|
||||
return ( ("client".equals(cur.getType())) || ("httpclient".equals(cur.getType())) );
|
||||
return ( ("client".equals(cur.getType())) ||
|
||||
("httpclient".equals(cur.getType()))||
|
||||
("ircclient".equals(cur.getType())));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -198,7 +198,9 @@ public class IndexBean {
|
||||
cur.setConfig(config, "");
|
||||
}
|
||||
|
||||
if ("httpclient".equals(cur.getType()) || "client".equals(cur.getType())) {
|
||||
if ("ircclient".equals(cur.getType()) ||
|
||||
"httpclient".equals(cur.getType()) ||
|
||||
"client".equals(cur.getType())) {
|
||||
// all clients use the same I2CP session, and as such, use the same
|
||||
// I2CP options
|
||||
List controllers = _group.getControllers();
|
||||
@ -206,7 +208,10 @@ public class IndexBean {
|
||||
TunnelController c = (TunnelController)controllers.get(i);
|
||||
if (c == cur) continue;
|
||||
//only change when they really are declared of beeing a sharedClient
|
||||
if (("httpclient".equals(c.getType()) || "client".equals(c.getType())) && "true".equalsIgnoreCase(c.getSharedClient())) {
|
||||
if (("httpclient".equals(c.getType()) ||
|
||||
"ircclient".equals(c.getType())||
|
||||
"client".equals(c.getType())
|
||||
) && "true".equalsIgnoreCase(c.getSharedClient())) {
|
||||
Properties cOpt = c.getConfig("");
|
||||
if (_tunnelCount != null) {
|
||||
cOpt.setProperty("option.inbound.quantity", _tunnelCount);
|
||||
@ -278,7 +283,9 @@ public class IndexBean {
|
||||
public boolean isClient(int tunnelNum) {
|
||||
TunnelController cur = getController(tunnelNum);
|
||||
if (cur == null) return false;
|
||||
return ( ("client".equals(cur.getType())) || ("httpclient".equals(cur.getType())) );
|
||||
return ( ("client".equals(cur.getType())) ||
|
||||
("httpclient".equals(cur.getType())) ||
|
||||
("ircclient".equals(cur.getType())));
|
||||
}
|
||||
|
||||
public String getTunnelName(int tunnel) {
|
||||
@ -308,6 +315,7 @@ public class IndexBean {
|
||||
public String getTypeName(String internalType) {
|
||||
if ("client".equals(internalType)) return "Client proxy";
|
||||
else if ("httpclient".equals(internalType)) return "HTTP proxy";
|
||||
else if ("ircclient".equals(internalType)) return "IRC proxy";
|
||||
else if ("server".equals(internalType)) return "Server";
|
||||
else if ("httpserver".equals(internalType)) return "HTTP server";
|
||||
else return internalType;
|
||||
@ -356,7 +364,7 @@ public class IndexBean {
|
||||
public String getClientDestination(int tunnel) {
|
||||
TunnelController tun = getController(tunnel);
|
||||
if (tun == null) return "";
|
||||
if ("client".equals(tun.getType())) return tun.getTargetDestination();
|
||||
if ("client".equals(tun.getType())||"ircclient".equals(tun.getType())) return tun.getTargetDestination();
|
||||
else return tun.getProxyList();
|
||||
}
|
||||
|
||||
@ -386,7 +394,7 @@ public class IndexBean {
|
||||
///
|
||||
|
||||
/**
|
||||
* What type of tunnel (httpclient, client, or server). This is
|
||||
* What type of tunnel (httpclient, ircclient, client, or server). This is
|
||||
* required when adding a new tunnel.
|
||||
*
|
||||
*/
|
||||
@ -427,12 +435,12 @@ public class IndexBean {
|
||||
public void setProxyList(String proxyList) {
|
||||
_proxyList = (proxyList != null ? proxyList.trim() : null);
|
||||
}
|
||||
/** what port should this client/httpclient listen on */
|
||||
/** what port should this client/httpclient/ircclient listen on */
|
||||
public void setPort(String port) {
|
||||
_port = (port != null ? port.trim() : null);
|
||||
}
|
||||
/**
|
||||
* what interface should this client/httpclient listen on (unless
|
||||
* what interface should this client/httpclient/ircclient listen on (unless
|
||||
* overridden by the setReachableByOther() field)
|
||||
*/
|
||||
public void setReachableBy(String reachableBy) {
|
||||
@ -440,7 +448,7 @@ public class IndexBean {
|
||||
}
|
||||
/**
|
||||
* If specified, defines the exact IP interface to listen for requests
|
||||
* on (in the case of client/httpclient tunnels)
|
||||
* on (in the case of client/httpclient/ircclient tunnels)
|
||||
*/
|
||||
public void setReachableByOther(String reachableByOther) {
|
||||
_reachableByOther = (reachableByOther != null ? reachableByOther.trim() : null);
|
||||
@ -520,6 +528,24 @@ public class IndexBean {
|
||||
}
|
||||
|
||||
config.setProperty("sharedClient", _sharedClient + "");
|
||||
}else if ("ircclient".equals(_type)) {
|
||||
if (_port != null)
|
||||
config.setProperty("listenPort", _port);
|
||||
if (_reachableByOther != null)
|
||||
config.setProperty("interface", _reachableByOther);
|
||||
else
|
||||
config.setProperty("interface", _reachableBy);
|
||||
if (_targetDestination != null)
|
||||
config.setProperty("targetDestination", _targetDestination);
|
||||
|
||||
config.setProperty("option.inbound.nickname", CLIENT_NICKNAME);
|
||||
config.setProperty("option.outbound.nickname", CLIENT_NICKNAME);
|
||||
if (_name != null && !_sharedClient) {
|
||||
config.setProperty("option.inbound.nickname", _name);
|
||||
config.setProperty("option.outbound.nickname", _name);
|
||||
}
|
||||
|
||||
config.setProperty("sharedClient", _sharedClient + "");
|
||||
} else if ("client".equals(_type)) {
|
||||
if (_port != null)
|
||||
config.setProperty("listenPort", _port);
|
||||
@ -608,7 +634,7 @@ public class IndexBean {
|
||||
else
|
||||
config.setProperty("option.i2p.streaming.connectDelay", "0");
|
||||
if (_name != null) {
|
||||
if ( ((!"client".equals(_type)) && (!"httpclient".equals(_type))) || (!_sharedClient) ) {
|
||||
if ( ((!"client".equals(_type)) && (!"httpclient".equals(_type))&& (!"ircclient".equals(_type))) || (!_sharedClient) ) {
|
||||
config.setProperty("option.inbound.nickname", _name);
|
||||
config.setProperty("option.outbound.nickname", _name);
|
||||
} else {
|
||||
|
@ -15,7 +15,7 @@
|
||||
} else {
|
||||
String type = request.getParameter("type");
|
||||
int curTunnel = -1;
|
||||
if ("client".equals(type) || "httpclient".equals(type)) {
|
||||
if ("client".equals(type) || "httpclient".equals(type) || "ircclient".equals(type)) {
|
||||
%><jsp:include page="editClient.jsp" /><%
|
||||
} else if ("server".equals(type) || "httpserver".equals(type)) {
|
||||
%><jsp:include page="editServer.jsp" /><%
|
||||
|
@ -170,7 +170,7 @@ if (curTunnel >= 0) {
|
||||
<% } else { %>
|
||||
<input type="checkbox" value="true" name="shared" />
|
||||
<% } %>
|
||||
<i>(Share tunnels with other clients and httpclients? Change requires restart of client proxy)</i>
|
||||
<i>(Share tunnels with other clients and irc/httpclients? Change requires restart of client proxy)</i>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -169,6 +169,7 @@
|
||||
<b>Add new:</b>
|
||||
<select name="type">
|
||||
<option value="httpclient">HTTP proxy</option>
|
||||
<option value="ircclient">IRC proxy</option>
|
||||
<option value="client">Client tunnel</option>
|
||||
<option value="server">Server tunnel</option>
|
||||
<option value="httpserver">HTTP server tunnel</option>
|
||||
|
16
history.txt
16
history.txt
@ -1,4 +1,18 @@
|
||||
$Id: history.txt,v 1.287 2005/10/09 00:46:57 jrandom Exp $
|
||||
$Id: history.txt,v 1.288 2005/10/09 06:35:15 jrandom Exp $
|
||||
|
||||
2005-10-10 dust
|
||||
* Implemented a new I2PTunnelIRCClient which locally filters inbound and
|
||||
outbound IRC commands for anonymity and security purposes, removing all
|
||||
CTCP messages except ACTION, as well as stripping the hostname from the
|
||||
USER message (while leaving the nick and 'full name'). The IRC proxy
|
||||
doesn't use this by default, but you can enable it by creating a new
|
||||
"IRC proxy" tunnel on the web interface, or by changing the tunnel type
|
||||
to "ircclient" in i2ptunnel.config.
|
||||
|
||||
2005-10-10 jrandom
|
||||
* I2PTunnel http client config cleanup and stats
|
||||
* Minor SSU congestion tweaks and stats
|
||||
* Reduced netDb exploration period
|
||||
|
||||
2005-10-09 jrandom
|
||||
* Syndie CLI cleanup for simpler CLI posting. Usage shown with
|
||||
|
@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
|
||||
*
|
||||
*/
|
||||
public class RouterVersion {
|
||||
public final static String ID = "$Revision: 1.262 $ $Date: 2005/10/09 00:46:57 $";
|
||||
public final static String ID = "$Revision: 1.263 $ $Date: 2005/10/09 06:35:14 $";
|
||||
public final static String VERSION = "0.6.1.2";
|
||||
public final static long BUILD = 3;
|
||||
public final static long BUILD = 4;
|
||||
public static void main(String args[]) {
|
||||
System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
|
||||
System.out.println("Router ID: " + RouterVersion.ID);
|
||||
|
@ -27,8 +27,8 @@ class ExploreJob extends SearchJob {
|
||||
private Log _log;
|
||||
private PeerSelector _peerSelector;
|
||||
|
||||
/** how long each exploration should run for (currently a trivial 20 seconds) */
|
||||
private static final long MAX_EXPLORE_TIME = 20*1000;
|
||||
/** how long each exploration should run for (currently a trivial 10 seconds) */
|
||||
private static final long MAX_EXPLORE_TIME = 10*1000;
|
||||
|
||||
/** how many of the peers closest to the key being explored do we want to explicitly say "dont send me this"? */
|
||||
private static final int NUM_CLOSEST_TO_IGNORE = 3;
|
||||
|
@ -170,8 +170,8 @@ public class PeerState {
|
||||
* of 608
|
||||
*/
|
||||
private static final int DEFAULT_MTU = 608;//600; //1500;
|
||||
private static final int MIN_RTO = 800 + ACKSender.ACK_FREQUENCY;
|
||||
private static final int MAX_RTO = 2000; // 5000;
|
||||
private static final int MIN_RTO = 500 + ACKSender.ACK_FREQUENCY;
|
||||
private static final int MAX_RTO = 5000; // 5000;
|
||||
|
||||
public PeerState(I2PAppContext ctx) {
|
||||
_context = ctx;
|
||||
@ -515,7 +515,7 @@ public class PeerState {
|
||||
//if (true)
|
||||
// _sendWindowBytes -= 10000;
|
||||
//else
|
||||
_sendWindowBytes = _sendWindowBytes/4; //(_sendWindowBytes*2) / 3;
|
||||
_sendWindowBytes = _sendWindowBytes/2; //(_sendWindowBytes*2) / 3;
|
||||
if (_sendWindowBytes < MINIMUM_WINDOW_BYTES)
|
||||
_sendWindowBytes = MINIMUM_WINDOW_BYTES;
|
||||
//if (congestionAt/2 < _slowStartThreshold)
|
||||
@ -632,7 +632,7 @@ public class PeerState {
|
||||
float v = _context.random().nextFloat();
|
||||
if (v < 0) v = 0-v;
|
||||
if (v <= prob)
|
||||
_sendWindowBytes += 512; // bytesACKed;
|
||||
_sendWindowBytes += bytesACKed; //512; // bytesACKed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -640,7 +640,7 @@ public class PeerState {
|
||||
_sendWindowBytes = MAX_SEND_WINDOW_BYTES;
|
||||
_lastReceiveTime = _context.clock().now();
|
||||
|
||||
if (false) {
|
||||
if (true) {
|
||||
if (_sendWindowBytesRemaining + bytesACKed <= _sendWindowBytes)
|
||||
_sendWindowBytesRemaining += bytesACKed;
|
||||
else
|
||||
|
@ -890,6 +890,18 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
peers.addAll(_peersByIdent.values());
|
||||
}
|
||||
long offsetTotal = 0;
|
||||
|
||||
int bpsIn = 0;
|
||||
int bpsOut = 0;
|
||||
long uptimeMsTotal = 0;
|
||||
long cwinTotal = 0;
|
||||
long rttTotal = 0;
|
||||
long rtoTotal = 0;
|
||||
long sendTotal = 0;
|
||||
long recvTotal = 0;
|
||||
long resentTotal = 0;
|
||||
long dupRecvTotal = 0;
|
||||
int numPeers = 0;
|
||||
|
||||
StringBuffer buf = new StringBuffer(512);
|
||||
buf.append("<b id=\"udpcon\">UDP connections: ").append(peers.size()).append("</b><br />\n");
|
||||
@ -968,16 +980,22 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
}
|
||||
buf.append("</code></td>");
|
||||
|
||||
long idleIn = (now-peer.getLastReceiveTime())/1000;
|
||||
long idleOut = (now-peer.getLastSendTime())/1000;
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append((now-peer.getLastReceiveTime())/1000);
|
||||
buf.append(idleIn);
|
||||
buf.append("s/");
|
||||
buf.append((now-peer.getLastSendTime())/1000);
|
||||
buf.append(idleOut);
|
||||
buf.append("s</code></td>");
|
||||
|
||||
|
||||
int recvBps = (idleIn > 10 ? 0 : peer.getReceiveBps());
|
||||
int sendBps = (idleOut > 10 ? 0 : peer.getSendBps());
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append(formatKBps(peer.getReceiveBps()));
|
||||
buf.append(formatKBps(recvBps));
|
||||
buf.append("KBps/");
|
||||
buf.append(formatKBps(peer.getSendBps()));
|
||||
buf.append(formatKBps(sendBps));
|
||||
buf.append("KBps ");
|
||||
//buf.append(formatKBps(peer.getReceiveACKBps()));
|
||||
//buf.append("KBps/");
|
||||
@ -985,8 +1003,10 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
//buf.append("KBps ");
|
||||
buf.append("</code></td>");
|
||||
|
||||
long uptime = now - peer.getKeyEstablishedTime();
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append(DataHelper.formatDuration(now-peer.getKeyEstablishedTime()));
|
||||
buf.append(DataHelper.formatDuration(uptime));
|
||||
buf.append("</code></td>");
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
@ -994,16 +1014,21 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
buf.append("s</code></td>");
|
||||
offsetTotal = offsetTotal + peer.getClockSkew();
|
||||
|
||||
long sendWindow = peer.getSendWindowBytes();
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append(peer.getSendWindowBytes()/1024);
|
||||
buf.append(sendWindow/1024);
|
||||
buf.append("K</code></td>");
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append(peer.getSlowStartThreshold()/1024);
|
||||
buf.append("K</code></td>");
|
||||
|
||||
int rtt = peer.getRTT();
|
||||
int rto = peer.getRTO();
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append(peer.getRTT());
|
||||
buf.append(rtt);
|
||||
buf.append("</code></td>");
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
@ -1011,38 +1036,81 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
buf.append("</code></td>");
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append(peer.getRTO());
|
||||
buf.append(rto);
|
||||
buf.append("</code></td>");
|
||||
|
||||
long sent = peer.getPacketsTransmitted();
|
||||
long recv = peer.getPacketsReceived();
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append(sent);
|
||||
buf.append("</code></td>");
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append(peer.getPacketsTransmitted());
|
||||
buf.append(recv);
|
||||
buf.append("</code></td>");
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append(peer.getPacketsReceived());
|
||||
buf.append("</code></td>");
|
||||
//double sent = (double)peer.getPacketsPeriodTransmitted();
|
||||
//double sendLostPct = 0;
|
||||
//if (sent > 0)
|
||||
// sendLostPct = (double)peer.getPacketsRetransmitted()/(sent);
|
||||
|
||||
double sent = (double)peer.getPacketsPeriodTransmitted();
|
||||
double sendLostPct = 0;
|
||||
if (sent > 0)
|
||||
sendLostPct = (double)peer.getPacketsRetransmitted()/(sent);
|
||||
long resent = peer.getPacketsRetransmitted();
|
||||
long dupRecv = peer.getPacketsReceivedDuplicate();
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
//buf.append(formatPct(sendLostPct));
|
||||
buf.append(peer.getPacketsRetransmitted()); // + "/" + peer.getPacketsPeriodRetransmitted() + "/" + sent);
|
||||
buf.append(resent); // + "/" + peer.getPacketsPeriodRetransmitted() + "/" + sent);
|
||||
//buf.append(peer.getPacketRetransmissionRate());
|
||||
buf.append("</code></td>");
|
||||
|
||||
double recvDupPct = (double)peer.getPacketsReceivedDuplicate()/(double)peer.getPacketsReceived();
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append(peer.getPacketsReceivedDuplicate()); //formatPct(recvDupPct));
|
||||
buf.append(dupRecv); //formatPct(recvDupPct));
|
||||
buf.append("</code></td>");
|
||||
|
||||
buf.append("</tr>");
|
||||
out.write(buf.toString());
|
||||
buf.setLength(0);
|
||||
|
||||
bpsIn += recvBps;
|
||||
bpsOut += sendBps;
|
||||
|
||||
uptimeMsTotal += uptime;
|
||||
cwinTotal += sendWindow;
|
||||
rttTotal += rtt;
|
||||
rtoTotal += rto;
|
||||
|
||||
sendTotal += sent;
|
||||
recvTotal += recv;
|
||||
resentTotal += resent;
|
||||
dupRecvTotal += dupRecv;
|
||||
|
||||
numPeers++;
|
||||
}
|
||||
|
||||
buf.append("<tr><td colspan=\"14\"><hr /></td></tr>\n");
|
||||
buf.append(" <tr><td colspan=\"2\"><b>Total</b></td>");
|
||||
buf.append(" <td>");
|
||||
buf.append(formatKBps(bpsIn)).append("KBps/").append(formatKBps(bpsOut));
|
||||
buf.append("KBps</td>");
|
||||
buf.append(" <td>").append(numPeers > 0 ? DataHelper.formatDuration(uptimeMsTotal/numPeers) : "0s");
|
||||
buf.append("</td><td> </td>\n");
|
||||
buf.append(" <td>");
|
||||
buf.append(numPeers > 0 ? cwinTotal/(numPeers*1024) + "K" : "0K");
|
||||
buf.append("</td><td> </td>\n");
|
||||
buf.append(" <td>");
|
||||
buf.append(numPeers > 0 ? rttTotal/numPeers : 0);
|
||||
buf.append("</td><td> </td><td>");
|
||||
buf.append(numPeers > 0 ? rtoTotal/numPeers : 0);
|
||||
buf.append("</td>\n <td>");
|
||||
buf.append(sendTotal).append("</td><td>").append(recvTotal).append("</td>\n");
|
||||
buf.append(" <td>").append(resentTotal);
|
||||
buf.append("</td><td>").append(dupRecvTotal).append("</td>\n");
|
||||
buf.append(" </tr>\n");
|
||||
out.write(buf.toString());
|
||||
buf.setLength(0);
|
||||
|
||||
out.write("</table>\n");
|
||||
|
||||
buf.append("<b>Average clock skew, UDP peers:");
|
||||
@ -1278,4 +1346,23 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final String BADIPS[] = new String[] { "192.168.0.1", "127.0.0.1", "10.3.4.5", "172.16.3.4", "224.5.6.7" };
|
||||
private static final String GOODIPS[] = new String[] { "192.167.0.1", "126.0.0.1", "11.3.4.5", "172.15.3.4", "223.5.6.7" };
|
||||
public static void main(String args[]) {
|
||||
for (int i = 0; i < BADIPS.length; i++) {
|
||||
try {
|
||||
InetAddress addr = InetAddress.getByName(BADIPS[i]);
|
||||
boolean routable = isPubliclyRoutable(addr.getAddress());
|
||||
System.out.println("Routable: " + routable + " (" + BADIPS[i] + ")");
|
||||
} catch (Exception e) { e.printStackTrace(); }
|
||||
}
|
||||
for (int i = 0; i < GOODIPS.length; i++) {
|
||||
try {
|
||||
InetAddress addr = InetAddress.getByName(GOODIPS[i]);
|
||||
boolean routable = isPubliclyRoutable(addr.getAddress());
|
||||
System.out.println("Routable: " + routable + " (" + GOODIPS[i] + ")");
|
||||
} catch (Exception e) { e.printStackTrace(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user