diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java index cb9cbaadaa58ab0983fb3be8fb21f6f260e7910a..3d4422690268d501c5fdde5ab35a5b9d33ecff36 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 <host> <port> <privkey>"); l.log("genkeys <privkeyfile> [<pubkeyfile>]"); l.log("gentextkeys"); - l.log("client <port> <pubkey>|file:<pubkeyfile>"); + l.log("client <port> <pubkey>[,<pubkey,...]|file:<pubkeyfile>"); l.log("httpclient <port>"); l.log("lookup <name>"); l.log("quit"); @@ -449,9 +449,11 @@ public class I2PTunnel implements Logging, EventDispatcher { notifyEvent("clientTaskId", new Integer(-1)); } } else { - l.log("client <port> <pubkey>|file:<pubkeyfile>"); + l.log("client <port> <pubkey>[,<pubkey>]|file:<pubkeyfile>"); 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 a2163f1f2cd29e6168fc9869dc1a382c7d4c5dd7..4739a07f47bcec19361a09363e841c35967c4898 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 0000000000000000000000000000000000000000..246ecb8e7d2b9561e7409f26d8a3f756b2a17c92 --- /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 b195f551597c3225f5c048ae3e27207a8fce462d..93315d16ecd9b2a9393b84f3230ec250c3332178 100644 --- a/apps/routerconsole/jsp/config.jsp +++ b/apps/routerconsole/jsp/config.jsp @@ -28,44 +28,58 @@ <input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigNetHandler.nonce")%>" /> <input type="hidden" name="action" value="blah" /> - <b>External hostname/IP address:</b> - <input name="hostname" type="text" size="32" value="<jsp:getProperty name="nethelper" property="hostname" />" /> - <input type="submit" name="guesshost" value="Guess" /><br /> - <b>Externally reachable TCP port:</b> + TCP port: <input name="port" type="text" size="4" value="<jsp:getProperty name="nethelper" property="port" />" /> <br /> - <i>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 - <a href="http://dyndns.org/">dyndns</a> 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 <a href="http://www.whatismyip.com/">www.whatismyip.com</a>.</i> - <hr /> - <b>Enable internal time synchronization?</b> <input type="checkbox" <jsp:getProperty name="nethelper" property="enableTimeSyncChecked" /> name="enabletimesync" /><br /> - <i>If disabled, your machine <b>must</b> be NTP synchronized - your clock must always - be within a few seconds of "correct".</i> + <b>You must poke a hole in your firewall or NAT (if applicable) so that you can receive inbound TCP + connections on it.</b> 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 + <a href="http://www.i2p.net/roadmap">roadmap</a> and we only have so many coder-hours (but if you want + to help, please <a href="http://www.i2p.net/getinvolved">get involved!</a>) <hr /> + <b>Bandwidth limiter</b><br /> - <b>Inbound rate</b>: + Inbound rate: <input name="inboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="inboundRate" />" /> KBytes per second<br /> - <b>Inbound burst duration:</b> + Inbound burst duration: <jsp:getProperty name="nethelper" property="inboundBurstFactorBox" /><br /> - <b>Outbound rate:</b> + Outbound rate: <input name="outboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="outboundRate" />" /> KBytes per second<br /> - <b>Outbound burst duration:</b> + Outbound burst duration: <jsp:getProperty name="nethelper" property="outboundBurstFactorBox" /><br /> <i>A negative rate means there is no limit</i><br /> <hr /> - <b>Reseed</b> (from <input name="reseedfrom" type="text" size="40" value="http://dev.i2p.net/i2pdb/" />): - <input type="submit" name="reseed" value="now" /><br /> - <i>May take some time to download the peer references</i> + Enable internal time synchronization? <input type="checkbox" <jsp:getProperty name="nethelper" property="enableTimeSyncChecked" /> name="enabletimesync" /><br /> + <i>If disabled, your machine <b>must</b> 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).</i> <hr /> <input type="submit" name="save" value="Save changes" /> <input type="reset" value="Cancel" /><br /> - <i>Changing the hostname or TCP port will force a 'soft restart' - dropping your connections - and clients as if the router was stopped and restarted. <b>Please be patient</b> - it may take + <i>Changing the TCP port will force a 'soft restart' - dropping your connections and clients as + if the router was stopped and restarted. <b>Please be patient</b> - it may take a few seconds to complete.</i> </form> + <hr /> + <b>Advanced network config:</b> + <p> + 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 <a href="/configadvanced.jsp">advanced config</a> page + and add "i2np.tcp.hostname=yourHostname", then go to the + <a href="/configservice.jsp">service</a> 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 ;)</p> + + <p>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.</p> </div> </body> diff --git a/apps/routerconsole/jsp/summary.jsp b/apps/routerconsole/jsp/summary.jsp index 144f35dacbd747c68ee9d1c9fed1743ea4347e3a..aedbf44fe1026624d0ba42854aff6ca94075f6fc 100644 --- a/apps/routerconsole/jsp/summary.jsp +++ b/apps/routerconsole/jsp/summary.jsp @@ -2,6 +2,9 @@ <jsp:useBean class="net.i2p.router.web.SummaryHelper" id="helper" scope="request" /> <jsp:setProperty name="helper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> +<jsp:useBean class="net.i2p.router.web.ReseedHandler" id="reseed" scope="request" /> +<jsp:setProperty name="reseed" property="*" /> + <div class="routersummary"> <u><b>General</b></u><br /> <b>Ident:</b> <jsp:getProperty name="helper" property="ident" /><br /> @@ -16,8 +19,27 @@ <b>High capacity:</b> <jsp:getProperty name="helper" property="highCapacityPeers" /><br /> <b>Well integrated:</b> <jsp:getProperty name="helper" property="wellIntegratedPeers" /><br /> <b>Failing:</b> <jsp:getProperty name="helper" property="failingPeers" /><br /> - <b>Shitlisted:</b> <jsp:getProperty name="helper" property="shitlistedPeers" /><br /> - <hr /> + <b>Shitlisted:</b> <jsp:getProperty name="helper" property="shitlistedPeers" /><br /><% + if (helper.getActivePeers() <= 0) { + %><b><a href="config.jsp">check your NAT/firewall</a></b><br /><% + } + if (helper.getActiveProfiles() <= 4) { // 4 is the min fallback + if ("true".equals(System.getProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "false"))) { + out.print(" <i>reseeding</i>"); + } 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(" <a href=\"" + uri + "\">reseed</a>"); + } + } + %><hr /> <u><b>Bandwidth in/out</b></u><br /> <b>1m:</b> <jsp:getProperty name="helper" property="inboundMinuteKBps" />/<jsp:getProperty name="helper" property="outboundMinuteKBps" />KBps<br /> diff --git a/core/java/src/net/i2p/data/DataHelper.java b/core/java/src/net/i2p/data/DataHelper.java index 6a32f57a31e86a0ad37a717562a673d13ab7e0d4..dc7e92e374152ac2692af567414e44a452e47e4a 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 8cdc5b6d5d732dbd236117911b862b87eeda7e4e..49e9ce30bb16209dc53e4cc33b45bbf87a684b27 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 d88cdf641a51963c1f19f32f334d74b8c176eb11..bf30d4d53be7f07bd8f3cb418255eae703f6f344 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);