diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java index cb9cbaadaa..3d44226902 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java @@ -284,7 +284,7 @@ public class I2PTunnel implements Logging, EventDispatcher { l.log("textserver "); l.log("genkeys []"); l.log("gentextkeys"); - l.log("client |file:"); + l.log("client [,"); l.log("httpclient "); l.log("lookup "); l.log("quit"); @@ -449,9 +449,11 @@ public class I2PTunnel implements Logging, EventDispatcher { notifyEvent("clientTaskId", new Integer(-1)); } } else { - l.log("client |file:"); + l.log("client [,]|file:"); l.log(" creates a client that forwards port to the pubkey.\n" - + " use 0 as port to get a free port assigned."); + + " use 0 as port to get a free port assigned. If you specify\n" + + " a comma delimited list of pubkeys, it will rotate among them\n" + + " randomlyl"); notifyEvent("clientTaskId", new Integer(-1)); } } diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java index a2163f1f2c..4739a07f47 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java @@ -4,7 +4,11 @@ package net.i2p.i2ptunnel; 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.Destination; @@ -15,15 +19,17 @@ public class I2PTunnelClient extends I2PTunnelClientBase { private static final Log _log = new Log(I2PTunnelClient.class); - protected Destination dest; + /** 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; /** + * @param destinations comma delimited list of peers we target * @throws IllegalArgumentException if the I2PTunnel does not contain * valid config to contact the router */ - public I2PTunnelClient(int localPort, String destination, Logging l, + public I2PTunnelClient(int localPort, String destinations, Logging l, boolean ownDest, EventDispatcher notifyThis, I2PTunnel tunnel) throws IllegalArgumentException { super(localPort, ownDest, l, notifyThis, "SynSender", tunnel); @@ -33,19 +39,28 @@ public class I2PTunnelClient extends I2PTunnelClientBase { return; } - try { - dest = I2PTunnel.destFromName(destination); - if (dest == null) { - l.log("Could not resolve " + destination + "."); - return; + 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 + "\""); } - } catch (DataFormatException e) { - l.log("Bad format in destination \"" + destination + "\"."); + } + + if (dests.size() <= 0) { + l.log("No target destinations found"); notifyEvent("openClientResult", "error"); return; } - setName(getLocalPort() + " -> " + destination); + setName(getLocalPort() + " -> " + destinations); startRunning(); @@ -56,6 +71,7 @@ public class I2PTunnelClient extends I2PTunnelClientBase { public long getReadTimeout() { return readTimeout; } protected void clientConnectionRun(Socket s) { + Destination dest = pickDestination(); I2PSocket i2ps = null; try { i2ps = createI2PSocket(dest); @@ -72,4 +88,17 @@ public class I2PTunnelClient extends I2PTunnelClientBase { } } } + + 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); + } } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java new file mode 100644 index 0000000000..246ecb8e7d --- /dev/null +++ b/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java @@ -0,0 +1,129 @@ +package net.i2p.router.web; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; + +import java.net.URL; +import java.net.URLConnection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import net.i2p.I2PAppContext; +import net.i2p.util.I2PThread; +import net.i2p.util.Log; + +/** + * Handler to deal with reseed requests. This reseed from the URL + * http://dev.i2p.net/i2pdb/ unless the java env property "i2p.reseedURL" is + * set. It always writes to ./netDb/, so don't mess with that. + * + */ +public class ReseedHandler { + private static ReseedRunner _reseedRunner = new ReseedRunner(); + + public void setReseedNonce(String nonce) { + if (nonce == null) return; + if (nonce.equals(System.getProperty("net.i2p.router.web.ReseedHandler.nonce")) || + nonce.equals(System.getProperty("net.i2p.router.web.ReseedHandler.noncePrev"))) { + synchronized (_reseedRunner) { + if (_reseedRunner.isRunning()) { + return; + } else { + System.setProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "true"); + I2PThread reseed = new I2PThread(_reseedRunner, "Reseed"); + reseed.start(); + } + } + } + } + + public static class ReseedRunner implements Runnable { + private boolean _isRunning; + public ReseedRunner() { _isRunning = false; } + public boolean isRunning() { return _isRunning; } + public void run() { + _isRunning = true; + reseed(); + System.setProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "false"); + _isRunning = false; + } + } + + private static final String DEFAULT_SEED_URL = "http://dev.i2p.net/i2pdb/"; + /** + * Reseed has been requested, so lets go ahead and do it. Fetch all of + * the routerInfo-*.dat files from the specified URL (or the default) and + * save them into this router's netDb dir. + * + */ + private static void reseed() { + String seedURL = System.getProperty("i2p.reseedURL", DEFAULT_SEED_URL); + if ( (seedURL == null) || (seedURL.trim().length() <= 0) ) + seedURL = DEFAULT_SEED_URL; + try { + URL dir = new URL(seedURL); + String content = new String(readURL(dir)); + Set urls = new HashSet(); + int cur = 0; + while (true) { + int start = content.indexOf("href=\"routerInfo-", cur); + if (start < 0) + break; + + int end = content.indexOf(".dat\">", start); + String name = content.substring(start+"href=\"routerInfo-".length(), end); + urls.add(name); + cur = end + 1; + } + + int fetched = 0; + int errors = 0; + for (Iterator iter = urls.iterator(); iter.hasNext(); ) { + try { + fetchSeed(seedURL, (String)iter.next()); + fetched++; + } catch (Exception e) { + errors++; + } + } + } catch (Throwable t) { + I2PAppContext.getGlobalContext().logManager().getLog(ReseedHandler.class).error("Error reseeding", t); + } + } + + private static void fetchSeed(String seedURL, String peer) throws Exception { + URL url = new URL(seedURL + (seedURL.endsWith("/") ? "" : "/") + "routerInfo-" + peer + ".dat"); + + byte data[] = readURL(url); + writeSeed(peer, data); + } + + private static byte[] readURL(URL url) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); + URLConnection con = url.openConnection(); + InputStream in = con.getInputStream(); + byte buf[] = new byte[1024]; + while (true) { + int read = in.read(buf); + if (read < 0) + break; + baos.write(buf, 0, read); + } + in.close(); + return baos.toByteArray(); + } + + private static void writeSeed(String name, byte data[]) throws Exception { + String dirName = "netDb"; // _context.getProperty("router.networkDatabase.dbDir", "netDb"); + File netDbDir = new File(dirName); + if (!netDbDir.exists()) { + boolean ok = netDbDir.mkdirs(); + } + FileOutputStream fos = new FileOutputStream(new File(netDbDir, "routerInfo-" + name + ".dat")); + fos.write(data); + fos.close(); + } +} diff --git a/apps/routerconsole/jsp/config.jsp b/apps/routerconsole/jsp/config.jsp index b195f55159..93315d16ec 100644 --- a/apps/routerconsole/jsp/config.jsp +++ b/apps/routerconsole/jsp/config.jsp @@ -28,44 +28,58 @@ " /> - External hostname/IP address: - " /> -
- Externally reachable TCP port: + TCP port: " />
- The hostname/IP address and TCP port must be reachable from the outside world. If - you are behind a firewall or NAT, this means you must poke a hole for this port. If - you are using DHCP and do not have a static IP address, you should either use a service like - dyndns or leave the hostname blank. If you leave it blank, - your router will autodetect the 'correct' IP address by asking a peer (and unconditionally - believing them if the address is routable and you don't have any established connections yet). - The "guess" functionality makes an HTTP request - to www.whatismyip.com. -
- Enable internal time synchronization? name="enabletimesync" />
- If disabled, your machine must be NTP synchronized - your clock must always - be within a few seconds of "correct". + You must poke a hole in your firewall or NAT (if applicable) so that you can receive inbound TCP + connections on it. Nothing will work if you don't. Sorry. We know how to make it so + this restriction won't be necessary, but its later on in the + roadmap and we only have so many coder-hours (but if you want + to help, please get involved!)
+ Bandwidth limiter
- Inbound rate: + Inbound rate: " /> KBytes per second
- Inbound burst duration: + Inbound burst duration:
- Outbound rate: + Outbound rate: " /> KBytes per second
- Outbound burst duration: + Outbound burst duration:
A negative rate means there is no limit

- Reseed (from ): -
- May take some time to download the peer references + Enable internal time synchronization? name="enabletimesync" />
+ If disabled, your machine must be NTP synchronized - your clock must always + be within a few seconds of "correct". You will need to be able to send outbound UDP + packets on port 123 to one of the pool.ntp.org machines (or some other SNTP server).

- Changing the hostname or TCP port will force a 'soft restart' - dropping your connections - and clients as if the router was stopped and restarted. Please be patient - it may take + Changing the TCP port will force a 'soft restart' - dropping your connections and clients as + if the router was stopped and restarted. Please be patient - it may take a few seconds to complete. +
+ Advanced network config: +

+ There are two other network settings, but no one reads this text so there's no reason + to tell you about them. In case you actually do read this, here's the deal: by default, + I2P will attempt to guess your IP address by having whomever it talks to tell it what + address they think you are. If and only if you have no working TCP connections and you + have not overridden the IP address, your router will believe them. If that doesn't sound + ok to you, thats fine - go to the advanced config page + and add "i2np.tcp.hostname=yourHostname", then go to the + service page and do a graceful restart. We used to make + people enter a hostname/IP address on this page, but too many people got it wrong ;)

+ +

The other advanced network option has to do with reseeding - you should never need to + reseed your router as long as you can find at least one other peer on the network. However, + when you do need to reseed, a link will show up on the left hand side which will + fetch all of the routerInfo-* files from http://dev.i2p.net/i2pdb/. That URL is just an + apache folder pointing at the netDb/ directory of a router - anyone can run one, and you can + configure your router to seed off an alternate URL by adding the java environmental property + "i2p.reseedURL=someURL" (e.g. java -Di2p.reseedURL=http://dev.i2p.net/i2pdb/ ...). You can + also do it manually by getting routerInfo-*.dat files from someone (a friend, someone on IRC, + whatever) and saving them to your netDb/ directory.

diff --git a/apps/routerconsole/jsp/summary.jsp b/apps/routerconsole/jsp/summary.jsp index 144f35dacb..aedbf44fe1 100644 --- a/apps/routerconsole/jsp/summary.jsp +++ b/apps/routerconsole/jsp/summary.jsp @@ -2,6 +2,9 @@ " /> + + +
General
Ident:
@@ -16,8 +19,27 @@ High capacity:
Well integrated:
Failing:
- Shitlisted:
-
+ Shitlisted:
<% + if (helper.getActivePeers() <= 0) { + %>check your NAT/firewall
<% + } + if (helper.getActiveProfiles() <= 4) { // 4 is the min fallback + if ("true".equals(System.getProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "false"))) { + out.print(" reseeding"); + } else { + long nonce = new java.util.Random().nextLong(); + String prev = System.getProperty("net.i2p.router.web.ReseedHandler.nonce"); + if (prev != null) System.setProperty("net.i2p.router.web.ReseedHandler.noncePrev", prev); + System.setProperty("net.i2p.router.web.ReseedHandler.nonce", nonce+""); + String uri = request.getRequestURI(); + if (uri.indexOf('?') > 0) + uri = uri + "&reseedNonce=" + nonce; + else + uri = uri + "?reseedNonce=" + nonce; + out.print(" reseed"); + } + } + %>
Bandwidth in/out
1m: /KBps
diff --git a/core/java/src/net/i2p/data/DataHelper.java b/core/java/src/net/i2p/data/DataHelper.java index 6a32f57a31..dc7e92e374 100644 --- a/core/java/src/net/i2p/data/DataHelper.java +++ b/core/java/src/net/i2p/data/DataHelper.java @@ -154,6 +154,9 @@ public class DataHelper { while ( (line = in.readLine()) != null) { if (line.trim().length() <= 0) continue; if (line.charAt(0) == '#') continue; + if (line.charAt(0) == ';') continue; + if (line.indexOf('#') > 0) // trim off any end of line comment + line = line.substring(0, line.indexOf('#')).trim(); int split = line.indexOf('='); if (split <= 0) continue; String key = line.substring(0, split); @@ -311,7 +314,7 @@ public class DataHelper { throw new IllegalArgumentException("wtf, fromLong got a negative? " + rv + ": offset="+ offset +" numBytes="+numBytes); return rv; } - + public static void main(String args[]) { for (int i = 0; i <= 0xFF; i++) testLong(1, i); diff --git a/history.txt b/history.txt index 8cdc5b6d5d..49e9ce30bb 100644 --- a/history.txt +++ b/history.txt @@ -1,4 +1,13 @@ -$Id: history.txt,v 1.76 2004/11/19 18:04:27 jrandom Exp $ +$Id: history.txt,v 1.77 2004/11/20 23:08:14 jrandom Exp $ + +2004-11-21 jrandom + * Allow end of line comments in the hosts.txt and other config files, + using '#' to begin the comments (thanks susi!) + * Add support to I2PTunnel's 'client' feature for picking between multiple + target destinations (e.g. 'client 6668 irc.duck.i2p,irc.baffled.i2p') + * Add a quick link on the left hand nav to reseed if there aren't enough + known peers, as well as link to the config page if there are no active + peers. Revised config page accordingly. 2004-11-21 jrandom * Destroy ElGamal/AES+SessionTag keys after 15 minutes of inactivity diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index d88cdf641a..bf30d4d53b 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -15,9 +15,9 @@ import net.i2p.CoreVersion; * */ public class RouterVersion { - public final static String ID = "$Revision: 1.81 $ $Date: 2004/11/19 18:04:27 $"; + public final static String ID = "$Revision: 1.82 $ $Date: 2004/11/20 23:08:14 $"; public final static String VERSION = "0.4.1.4"; - public final static long BUILD = 10; + public final static long BUILD = 11; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION); System.out.println("Router ID: " + RouterVersion.ID);