diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/WebEditPageFormGenerator.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/WebEditPageFormGenerator.java deleted file mode 100644 index 3fcf9312d6dc7780954bd0fa46252768d1cea860..0000000000000000000000000000000000000000 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/WebEditPageFormGenerator.java +++ /dev/null @@ -1,421 +0,0 @@ -package net.i2p.i2ptunnel; - -import java.util.Iterator; -import java.util.Properties; -import java.util.Random; -import java.util.StringTokenizer; - -/** - * Uuuugly code to generate the edit/add forms for the various - * I2PTunnel types (httpclient/client/server) - * - */ -class WebEditPageFormGenerator { - private static final String SELECT_TYPE_FORM = - "<form action=\"edit.jsp\"> Type of tunnel: <select name=\"type\">" + - "<option value=\"httpclient\">HTTP proxy</option>" + - "<option value=\"client\">Client tunnel</option>" + - "<option value=\"server\">Server tunnel</option>" + - "<option value=\"httpserver\">HTTP server tunnel</option>" + - "</select> <input type=\"submit\" value=\"GO\" />" + - "</form>\n"; - - /** - * Retrieve the form requested - * - */ - public static String getForm(WebEditPageHelper helper) { - TunnelController controller = helper.getTunnelController(); - - if ( (helper.getType() == null) && (controller == null) ) - return SELECT_TYPE_FORM; - - String id = helper.getNum(); - String type = helper.getType(); - if (controller != null) - type = controller.getType(); - - if ("httpclient".equals(type)) - return getEditHttpClientForm(controller, id); - else if ("client".equals(type)) - return getEditClientForm(controller, id); - else if ("server".equals(type)) - return getEditServerForm(controller, id); - else if ("httpserver".equals(type)) - return getEditHttpServerForm(controller, id); - else - return "WTF, unknown type [" + type + "]"; - } - - private static String getEditHttpClientForm(TunnelController controller, String id) { - StringBuffer buf = new StringBuffer(1024); - addGeneral(buf, controller, id); - buf.append("<b>Type:</b> <i>HTTP proxy</i><input type=\"hidden\" name=\"type\" value=\"httpclient\" /><br />\n"); - - addListeningOn(buf, controller, 4444); - - buf.append("<b>Outproxies:</b> <input type=\"text\" name=\"proxyList\" size=\"20\" "); - if ( (controller != null) && (controller.getProxyList() != null) ) - buf.append("value=\"").append(controller.getProxyList()).append("\" "); - else - buf.append("value=\"squid.i2p\" "); - buf.append("/><br />\n"); - - addStreamingOptions(buf, controller); - - buf.append("<hr />Note: the following options are shared across all client tunnels and"); - buf.append(" HTTP proxies<br />\n"); - - addOptions(buf, controller); - buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n"); - buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n"); - buf.append(" <i>confirm removal:</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n"); - buf.append("</form>\n"); - return buf.toString(); - } - - private static String getEditClientForm(TunnelController controller, String id) { - StringBuffer buf = new StringBuffer(1024); - addGeneral(buf, controller, id); - buf.append("<b>Type:</b> <i>Client tunnel</i><input type=\"hidden\" name=\"type\" value=\"client\" /><br />\n"); - - addListeningOn(buf, controller, 2025 + new Random().nextInt(1000)); // 2025 since nextInt can be negative - - buf.append("<b>Target:</b> <input type=\"text\" size=\"40\" name=\"targetDestination\" "); - if ( (controller != null) && (controller.getTargetDestination() != null) ) - buf.append("value=\"").append(controller.getTargetDestination()).append("\" "); - buf.append(" /> (either the hosts.txt name or the full base64 destination)<br />\n"); - - addStreamingOptions(buf, controller); - - buf.append("<hr />Note: the following options are shared across all client tunnels and"); - buf.append(" HTTP proxies<br />\n"); - - addOptions(buf, controller); - buf.append("<input type=\"submit\" name=\"action\" value=\"Save\"><br />\n"); - buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n"); - buf.append(" <i>confirm removal:</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n"); - buf.append("</form>\n"); - return buf.toString(); - } - - private static String getEditServerForm(TunnelController controller, String id) { - StringBuffer buf = new StringBuffer(1024); - addGeneral(buf, controller, id); - buf.append("<b>Type:</b> <i>Server tunnel</i><input type=\"hidden\" name=\"type\" value=\"server\" /><br />\n"); - - buf.append("<b>Target host:</b> <input type=\"text\" size=\"40\" name=\"targetHost\" "); - if ( (controller != null) && (controller.getTargetHost() != null) ) - buf.append("value=\"").append(controller.getTargetHost()).append("\" "); - else - buf.append("value=\"127.0.0.1\" "); - buf.append(" /><br />\n"); - - buf.append("<b>Target port:</b> <input type=\"text\" size=\"4\" name=\"targetPort\" "); - if ( (controller != null) && (controller.getTargetPort() != null) ) - buf.append("value=\"").append(controller.getTargetPort()).append("\" "); - else - buf.append("value=\"80\" "); - buf.append(" /><br />\n"); - - buf.append("<b>Private key file:</b> <input type=\"text\" name=\"privKeyFile\" value=\""); - if ( (controller != null) && (controller.getPrivKeyFile() != null) ) { - buf.append(controller.getPrivKeyFile()).append("\" /><br />"); - } else { - buf.append("myServer.privKey\" /><br />"); - buf.append("<input type=\"hidden\" name=\"privKeyGenerate\" value=\"true\" />"); - } - - addStreamingOptions(buf, controller); - - addOptions(buf, controller); - buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n"); - buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n"); - buf.append(" <i>confirm removal:</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n"); - buf.append("</form>\n"); - return buf.toString(); - } - - private static String getEditHttpServerForm(TunnelController controller, String id) { - StringBuffer buf = new StringBuffer(1024); - addGeneral(buf, controller, id); - buf.append("<b>Type:</b> <i>HTTP server tunnel</i><input type=\"hidden\" name=\"type\" value=\"httpserver\" /><br />\n"); - - buf.append("<b>Target host:</b> <input type=\"text\" size=\"40\" name=\"targetHost\" "); - if ( (controller != null) && (controller.getTargetHost() != null) ) - buf.append("value=\"").append(controller.getTargetHost()).append("\" "); - else - buf.append("value=\"127.0.0.1\" "); - buf.append(" /><br />\n"); - - buf.append("<b>Target port:</b> <input type=\"text\" size=\"4\" name=\"targetPort\" "); - if ( (controller != null) && (controller.getTargetPort() != null) ) - buf.append("value=\"").append(controller.getTargetPort()).append("\" "); - else - buf.append("value=\"80\" "); - buf.append(" /><br />\n"); - - buf.append("<b>Website hostname:</b> <input type=\"text\" size=\"16\" name=\"spoofedHost\" "); - if ( (controller != null) && (controller.getSpoofedHost() != null) ) - buf.append("value=\"").append(controller.getSpoofedHost()).append("\" "); - else - buf.append("value=\"mysite.i2p\" "); - buf.append(" /><br />\n"); - - buf.append("<b>Private key file:</b> <input type=\"text\" name=\"privKeyFile\" value=\""); - if ( (controller != null) && (controller.getPrivKeyFile() != null) ) { - buf.append(controller.getPrivKeyFile()).append("\" /><br />"); - } else { - buf.append("myServer.privKey\" /><br />"); - buf.append("<input type=\"hidden\" name=\"privKeyGenerate\" value=\"true\" />"); - } - - addStreamingOptions(buf, controller); - - addOptions(buf, controller); - buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n"); - buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n"); - buf.append(" <i>confirm removal:</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n"); - buf.append("</form>\n"); - return buf.toString(); - } - - /** - * Start off the form and add some common fields (name, num, description) - * - * @param buf where to shove the form - * @param controller tunnel in question, or null if we're creating a new tunnel - * @param id index into the current list of tunnelControllerGroup.getControllers() list - * (or null if we are generating an 'add' form) - */ - private static void addGeneral(StringBuffer buf, TunnelController controller, String id) { - buf.append("<form action=\"edit.jsp\">"); - if (id != null) - buf.append("<input type=\"hidden\" name=\"num\" value=\"").append(id).append("\" />"); - long nonce = new Random().nextLong(); - System.setProperty(WebEditPageHelper.class.getName() + ".nonce", nonce+""); - buf.append("<input type=\"hidden\" name=\"nonce\" value=\"").append(nonce).append("\" />"); - - buf.append("<b>Name:</b> <input type=\"text\" name=\"name\" size=\"20\" "); - if ( (controller != null) && (controller.getName() != null) ) - buf.append("value=\"").append(controller.getName()).append("\" "); - buf.append("/><br />\n"); - - buf.append("<b>Description:</b> <input type=\"text\" name=\"description\" size=\"60\" "); - if ( (controller != null) && (controller.getDescription() != null) ) - buf.append("value=\"").append(controller.getDescription()).append("\" "); - buf.append("/><br />\n"); - - buf.append("<b>Start automatically?</b> \n"); - buf.append("<input type=\"checkbox\" name=\"startOnLoad\" value=\"true\" "); - if ( (controller != null) && (controller.getStartOnLoad()) ) - buf.append(" checked=\"true\" />\n<br />\n"); - else - buf.append(" />\n<br />\n"); - - } - - /** - * Generate the fields asking for what port and interface the tunnel should - * listen on. - * - * @param buf where to shove the form - * @param controller tunnel in question, or null if we're creating a new tunnel - * @param defaultPort if we are creating a new tunnel, default the form to the given port - */ - private static void addListeningOn(StringBuffer buf, TunnelController controller, int defaultPort) { - buf.append("<b>Listening on port:</b> <input type=\"text\" name=\"port\" size=\"20\" "); - if ( (controller != null) && (controller.getListenPort() != null) ) - buf.append("value=\"").append(controller.getListenPort()).append("\" "); - else - buf.append("value=\"").append(defaultPort).append("\" "); - buf.append("/><br />\n"); - - String selectedOn = null; - if ( (controller != null) && (controller.getListenOnInterface() != null) ) - selectedOn = controller.getListenOnInterface(); - - buf.append("<b>Reachable by:</b> "); - buf.append("<select name=\"reachableBy\">"); - buf.append("<option value=\"127.0.0.1\" "); - if ( (selectedOn != null) && ("127.0.0.1".equals(selectedOn)) ) - buf.append("selected=\"true\" "); - buf.append(">Locally (127.0.0.1)</option>\n"); - buf.append("<option value=\"0.0.0.0\" "); - if ( (selectedOn != null) && ("0.0.0.0".equals(selectedOn)) ) - buf.append("selected=\"true\" "); - buf.append(">Everyone (0.0.0.0)</option>\n"); - buf.append("</select> "); - buf.append("Other: <input type=\"text\" name=\"reachableByOther\" value=\""); - if ( (selectedOn != null) && (!"127.0.0.1".equals(selectedOn)) && (!"0.0.0.0".equals(selectedOn)) ) - buf.append(selectedOn); - buf.append("\"><br />\n"); - } - - private static void addStreamingOptions(StringBuffer buf, TunnelController controller) { - int connectDelay = 0; - int maxWindowSize = -1; - - Properties opts = getOptions(controller); - if (opts != null) { - String delay = opts.getProperty("i2p.streaming.connectDelay"); - if (delay != null) { - try { - connectDelay = Integer.parseInt(delay); - } catch (NumberFormatException nfe) { - connectDelay = 0; - } - } - String max = opts.getProperty("i2p.streaming.maxWindowSize"); - if (max != null) { - try { - maxWindowSize = Integer.parseInt(max); - } catch (NumberFormatException nfe) { - maxWindowSize = -1; - } - } - } - - buf.append("<b>Delay connection briefly? </b> "); - buf.append("<input type=\"checkbox\" name=\"connectDelay\" value=\""); - buf.append((connectDelay > 0 ? connectDelay : 1000)).append("\" "); - if (connectDelay > 0) - buf.append("checked=\"true\" "); - buf.append("/> (useful for brief request/response connections)<br />\n"); - - buf.append("<b>Communication profile:</b>"); - buf.append("<select name=\"profile\">"); - if (maxWindowSize <= 0) - buf.append("<option value=\"interactive\">Interactive</option><option value=\"bulk\" selected=\"true\">Bulk</option>"); - else - buf.append("<option value=\"interactive\" selected=\"true\">Interactive</option><option value=\"bulk\">Bulk</option>"); - buf.append("</select><br />\n"); - } - - /** - * Add fields for customizing the I2PSession options, including helpers for - * tunnel depth and count, as well as I2CP host and port. - * - * @param buf where to shove the form - * @param controller tunnel in question, or null if we're creating a new tunnel - */ - private static void addOptions(StringBuffer buf, TunnelController controller) { - int tunnelDepth = 2; - int numTunnels = 2; - Properties opts = getOptions(controller); - if (opts != null) { - String depth = opts.getProperty("inbound.length"); - if (depth != null) { - try { - tunnelDepth = Integer.parseInt(depth); - } catch (NumberFormatException nfe) { - tunnelDepth = 2; - } - } - String num = opts.getProperty("inbound.quantity"); - if (num != null) { - try { - numTunnels = Integer.parseInt(num); - } catch (NumberFormatException nfe) { - numTunnels = 2; - } - } - } - - buf.append("<b>Tunnel depth:</b> "); - buf.append("<select name=\"tunnelDepth\">"); - buf.append("<option value=\"0\" "); - if (tunnelDepth == 0) buf.append(" selected=\"true\" "); - buf.append(">0 hop tunnel (low anonymity, low latency)</option>"); - buf.append("<option value=\"1\" "); - if (tunnelDepth == 1) buf.append(" selected=\"true\" "); - buf.append(">1 hop tunnel (medium anonymity, medium latency)</option>"); - buf.append("<option value=\"2\" "); - if (tunnelDepth == 2) buf.append(" selected=\"true\" "); - buf.append(">2 hop tunnel (high anonymity, high latency)</option>"); - if (tunnelDepth > 2) { - buf.append("<option value=\"").append(tunnelDepth).append("\" selected=\"true\" >"); - buf.append(tunnelDepth); - buf.append(" hop tunnel (custom)</option>"); - } - buf.append("</select><br />\n"); - - buf.append("<b>Tunnel count:</b> "); - buf.append("<select name=\"tunnelCount\">"); - buf.append("<option value=\"1\" "); - if (numTunnels == 1) buf.append(" selected=\"true\" "); - buf.append(">1 inbound tunnel (low bandwidth usage, less reliability)</option>"); - buf.append("<option value=\"2\" "); - if (numTunnels == 2) buf.append(" selected=\"true\" "); - buf.append(">2 inbound tunnels (standard bandwidth usage, standard reliability)</option>"); - buf.append("<option value=\"3\" "); - if (numTunnels == 3) buf.append(" selected=\"true\" "); - buf.append(">3 inbound tunnels (higher bandwidth usage, higher reliability)</option>"); - - if (numTunnels > 3) { - buf.append("<option value=\"").append(numTunnels).append("\" selected=\"true\" >"); - buf.append(numTunnels); - buf.append(" inbound tunnels (custom)</option>"); - } - buf.append("</select><br />\n"); - - buf.append("<b>I2CP host:</b> "); - buf.append("<input type=\"text\" name=\"clientHost\" size=\"20\" value=\""); - if ( (controller != null) && (controller.getI2CPHost() != null) ) - buf.append(controller.getI2CPHost()); - else - buf.append("127.0.0.1"); - buf.append("\" /><br />\n"); - buf.append("<b>I2CP port:</b> "); - buf.append("<input type=\"text\" name=\"clientPort\" size=\"20\" value=\""); - if ( (controller != null) && (controller.getI2CPPort() != null) ) - buf.append(controller.getI2CPPort()); - else - buf.append("7654"); - buf.append("\" /><br />\n"); - - buf.append("<b>Other custom options:</b> \n"); - buf.append("<input type=\"text\" name=\"customOptions\" size=\"60\" value=\""); - if (opts != null) { - int i = 0; - for (Iterator iter = opts.keySet().iterator(); iter.hasNext(); ) { - String key = (String)iter.next(); - String val = opts.getProperty(key); - if ("inbound.length".equals(key)) continue; - if ("outbound.length".equals(key)) continue; - if ("inbound.quantity".equals(key)) continue; - if ("outbound.quantity".equals(key)) continue; - if ("inbound.nickname".equals(key)) continue; - if ("outbound.nickname".equals(key)) continue; - if ("i2p.streaming.connectDelay".equals(key)) continue; - if ("i2p.streaming.maxWindowSize".equals(key)) continue; - if (i != 0) buf.append(' '); - buf.append(key).append('=').append(val); - i++; - } - } - buf.append("\" /><br />\n"); - } - - /** - * Retrieve the client options from the tunnel - * - * @return map of name=val to be used as I2P session options - */ - private static Properties getOptions(TunnelController controller) { - if (controller == null) return null; - String opts = controller.getClientOptions(); - StringTokenizer tok = new StringTokenizer(opts); - Properties props = new Properties(); - while (tok.hasMoreTokens()) { - String pair = tok.nextToken(); - int eq = pair.indexOf('='); - if ( (eq <= 0) || (eq >= pair.length()) ) - continue; - String key = pair.substring(0, eq); - String val = pair.substring(eq+1); - props.setProperty(key, val); - } - return props; - } -} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/WebStatusPageHelper.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/WebStatusPageHelper.java deleted file mode 100644 index 0137f81c65118f5867253c331af901ae04345ee0..0000000000000000000000000000000000000000 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/WebStatusPageHelper.java +++ /dev/null @@ -1,213 +0,0 @@ -package net.i2p.i2ptunnel; - -import java.util.List; -import net.i2p.I2PAppContext; -import net.i2p.util.Log; - -/** - * Ugly hack to let the web interface access the list of known tunnels and - * control their operation. Any data submitted by setting properties are - * acted upon by calling getActionResults() (which returns any messages - * generated). In addition, the getSummaryList() generates the html for - * summarizing all of the tunnels known, including both their status and the - * links to edit, stop, or start them. - * - */ -public class WebStatusPageHelper { - private I2PAppContext _context; - private Log _log; - private String _action; - private int _controllerNum; - private long _nonce; - - public WebStatusPageHelper() { - _context = I2PAppContext.getGlobalContext(); - _action = null; - _controllerNum = -1; - _log = _context.logManager().getLog(WebStatusPageHelper.class); - } - - public void setAction(String action) { - _action = action; - } - public void setNum(String num) { - if (num != null) { - try { - _controllerNum = Integer.parseInt(num); - } catch (NumberFormatException nfe) { - _controllerNum = -1; - } - } - } - public void setNonce(long nonce) { _nonce = nonce; } - public void setNonce(String nonce) { - if (nonce != null) { - try { - _nonce = Long.parseLong(nonce); - } catch (NumberFormatException nfe) {} - } - } - - public String getActionResults() { - try { - return processAction(); - } catch (Throwable t) { - _log.log(Log.CRIT, "Internal error processing web status", t); - return "Internal error processing request - " + t.getMessage(); - } - } - - public String getSummaryList() { - TunnelControllerGroup group = TunnelControllerGroup.getInstance(); - if (group == null) - return "<b>I2PTunnel instances not yet started - please be patient</b>\n"; - - long nonce = _context.random().nextLong(); - StringBuffer buf = new StringBuffer(4*1024); - buf.append("<ul>"); - List tunnels = group.getControllers(); - for (int i = 0; i < tunnels.size(); i++) { - buf.append("<li>\n"); - getSummary(buf, i, (TunnelController)tunnels.get(i), nonce); - buf.append("</li>\n"); - } - buf.append("</ul>"); - - buf.append("<hr /><form action=\"index.jsp\" method=\"GET\">\n"); - buf.append("<input type=\"hidden\" name=\"nonce\" value=\"").append(nonce).append("\" />\n"); - buf.append("<input type=\"submit\" name=\"action\" value=\"Stop all\" />\n"); - buf.append("<input type=\"submit\" name=\"action\" value=\"Start all\" />\n"); - buf.append("<input type=\"submit\" name=\"action\" value=\"Restart all\" />\n"); - buf.append("<input type=\"submit\" name=\"action\" value=\"Reload config\" />\n"); - buf.append("</form>\n"); - - System.setProperty(getClass().getName() + ".nonce", nonce+""); - - return buf.toString(); - } - - private void getSummary(StringBuffer buf, int num, TunnelController controller, long nonce) { - buf.append("<b>").append(controller.getName()).append("</b>: "); - if (controller.getIsRunning()) { - buf.append("<i>running</i> "); - buf.append("<a href=\"index.jsp?num=").append(num); - buf.append("&nonce=").append(nonce); - buf.append("&action=stop\">stop</a> "); - } else if (controller.getIsStarting()) { - buf.append("<i>startup in progress (please be patient)</i>"); - } else { - buf.append("<i>not running</i> "); - buf.append("<a href=\"index.jsp?num=").append(num); - buf.append("&nonce=").append(nonce); - buf.append("&action=start\">start</a> "); - } - buf.append("<a href=\"edit.jsp?num=").append(num).append("\">edit</a> "); - buf.append("<br />\n"); - controller.getSummary(buf); - } - - private String processAction() { - if ( (_action == null) || (_action.trim().length() <= 0) ) - return getMessages(); - String expected = System.getProperty(getClass().getName() + ".nonce"); - if ( (expected == null) || (!expected.equals(Long.toString(_nonce))) ) - return "<b>Invalid nonce, are you being spoofed?</b>"; - if ("Stop all".equals(_action)) - return stopAll(); - else if ("Start all".equals(_action)) - return startAll(); - else if ("Restart all".equals(_action)) - return restartAll(); - else if ("Reload config".equals(_action)) - return reloadConfig(); - else if ("stop".equals(_action)) - return stop(); - else if ("start".equals(_action)) - return start(); - else - return "Action <i>" + _action + "</i> unknown"; - } - private String stopAll() { - TunnelControllerGroup group = TunnelControllerGroup.getInstance(); - if (group == null) - return "<b>I2PTunnel instances not yet started - please be patient</b>\n"; - - List msgs = group.stopAllControllers(); - return getMessages(msgs); - } - private String startAll() { - TunnelControllerGroup group = TunnelControllerGroup.getInstance(); - if (group == null) - return "<b>I2PTunnel instances not yet started - please be patient</b>\n"; - - List msgs = group.startAllControllers(); - return getMessages(msgs); - } - private String restartAll() { - TunnelControllerGroup group = TunnelControllerGroup.getInstance(); - if (group == null) - return "<b>I2PTunnel instances not yet started - please be patient</b>\n"; - - List msgs = group.restartAllControllers(); - return getMessages(msgs); - } - private String reloadConfig() { - TunnelControllerGroup group = TunnelControllerGroup.getInstance(); - if (group == null) - return "<b>I2PTunnel instances not yet started - please be patient</b>\n"; - - group.reloadControllers(); - return "Config reloaded"; - } - private String start() { - TunnelControllerGroup group = TunnelControllerGroup.getInstance(); - if (group == null) - return "<b>I2PTunnel instances not yet started - please be patient</b>\n"; - - if (_controllerNum < 0) return "Invalid tunnel"; - - List controllers = group.getControllers(); - if (_controllerNum >= controllers.size()) return "Invalid tunnel"; - TunnelController controller = (TunnelController)controllers.get(_controllerNum); - controller.startTunnelBackground(); - return getMessages(controller.clearMessages()); - } - - private String stop() { - TunnelControllerGroup group = TunnelControllerGroup.getInstance(); - if (group == null) - return "<b>I2PTunnel instances not yet started - please be patient</b>\n"; - - if (_controllerNum < 0) return "Invalid tunnel"; - - List controllers = group.getControllers(); - if (_controllerNum >= controllers.size()) return "Invalid tunnel"; - TunnelController controller = (TunnelController)controllers.get(_controllerNum); - controller.stopTunnel(); - return getMessages(controller.clearMessages()); - } - - private String getMessages() { - TunnelControllerGroup group = TunnelControllerGroup.getInstance(); - if (group == null) - return ""; - - return getMessages(group.clearAllMessages()); - } - - private String getMessages(List msgs) { - if (msgs == null) return ""; - int num = msgs.size(); - switch (num) { - case 0: return ""; - case 1: return (String)msgs.get(0); - default: - StringBuffer buf = new StringBuffer(512); - buf.append("<ul>"); - for (int i = 0; i < num; i++) - buf.append("<li>").append((String)msgs.get(i)).append("</li>\n"); - buf.append("</ul>\n"); - return buf.toString(); - } - } -} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java new file mode 100644 index 0000000000000000000000000000000000000000..e72fe61666753b9b8578b362a873d3ec77dd0b46 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java @@ -0,0 +1,225 @@ +package net.i2p.i2ptunnel.web; +/* + * free (adj.): unencumbered; not under the control of others + * Written by jrandom in 2005 and released into the public domain + * with no warranty of any kind, either expressed or implied. + * It probably won't make your computer catch on fire, or eat + * your children, but it might. Use at your own risk. + * + */ + +import java.util.Iterator; +import java.util.List; +import java.util.Properties; +import java.util.StringTokenizer; + +import net.i2p.I2PAppContext; +import net.i2p.i2ptunnel.TunnelController; +import net.i2p.i2ptunnel.TunnelControllerGroup; +import net.i2p.util.Log; + +/** + * Ugly little accessor for the edit page + */ +public class EditBean extends IndexBean { + public EditBean() { super(); } + + public static boolean staticIsClient(int tunnel) { + TunnelControllerGroup group = TunnelControllerGroup.getInstance(); + List controllers = group.getControllers(); + if (controllers.size() > tunnel) { + TunnelController cur = (TunnelController)controllers.get(tunnel); + if (cur == null) return false; + return ( ("client".equals(cur.getType())) || ("httpclient".equals(cur.getType())) ); + } else { + return false; + } + } + + public String getInternalType(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun != null) + return tun.getType(); + else + return ""; + } + + public String getTargetHost(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun != null) + return tun.getTargetHost(); + else + return ""; + } + public String getTargetPort(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun != null) + return tun.getTargetPort(); + else + return ""; + } + public String getSpoofedHost(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun != null) + return tun.getSpoofedHost(); + else + return ""; + } + public String getPrivateKeyFile(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun != null) + return tun.getPrivKeyFile(); + else + return ""; + } + + public boolean startAutomatically(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun != null) + return tun.getStartOnLoad(); + else + return false; + } + + public boolean shouldDelay(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun != null) { + Properties opts = getOptions(tun); + if (opts != null) { + String delay = opts.getProperty("i2p.streaming.connectDelay"); + if ( (delay == null) || ("0".equals(delay)) ) + return false; + else + return true; + } else { + return false; + } + } else { + return false; + } + } + + public boolean isInteractive(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun != null) { + Properties opts = getOptions(tun); + if (opts != null) { + String wsiz = opts.getProperty("i2p.streaming.maxWindowSize"); + if ( (wsiz == null) || (!"1".equals(wsiz)) ) + return false; + else + return true; + } else { + return false; + } + } else { + return false; + } + } + + public int getTunnelDepth(int tunnel, int defaultLength) { + TunnelController tun = getController(tunnel); + if (tun != null) { + Properties opts = getOptions(tun); + if (opts != null) { + String len = opts.getProperty("inbound.length"); + if (len == null) return defaultLength; + try { + return Integer.parseInt(len); + } catch (NumberFormatException nfe) { + return defaultLength; + } + } else { + return defaultLength; + } + } else { + return defaultLength; + } + } + + public int getTunnelCount(int tunnel, int defaultCount) { + TunnelController tun = getController(tunnel); + if (tun != null) { + Properties opts = getOptions(tun); + if (opts != null) { + String len = opts.getProperty("inbound.quantity"); + if (len == null) return defaultCount; + try { + return Integer.parseInt(len); + } catch (NumberFormatException nfe) { + return defaultCount; + } + } else { + return defaultCount; + } + } else { + return defaultCount; + } + } + + public String getI2CPHost(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun != null) + return tun.getI2CPHost(); + else + return "localhost"; + } + + public String getI2CPPort(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun != null) + return tun.getI2CPPort(); + else + return "7654"; + } + + public String getCustomOptions(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun != null) { + Properties opts = getOptions(tun); + if (opts == null) return ""; + StringBuffer buf = new StringBuffer(64); + int i = 0; + for (Iterator iter = opts.keySet().iterator(); iter.hasNext(); ) { + String key = (String)iter.next(); + String val = opts.getProperty(key); + if ("inbound.length".equals(key)) continue; + if ("outbound.length".equals(key)) continue; + if ("inbound.quantity".equals(key)) continue; + if ("outbound.quantity".equals(key)) continue; + if ("inbound.nickname".equals(key)) continue; + if ("outbound.nickname".equals(key)) continue; + if ("i2p.streaming.connectDelay".equals(key)) continue; + if ("i2p.streaming.maxWindowSize".equals(key)) continue; + if (i != 0) buf.append(' '); + buf.append(key).append('=').append(val); + i++; + } + return buf.toString(); + } else { + return ""; + } + } + + /** + * Retrieve the client options from the tunnel + * + * @return map of name=val to be used as I2P session options + */ + private static Properties getOptions(TunnelController controller) { + if (controller == null) return null; + String opts = controller.getClientOptions(); + StringTokenizer tok = new StringTokenizer(opts); + Properties props = new Properties(); + while (tok.hasMoreTokens()) { + String pair = tok.nextToken(); + int eq = pair.indexOf('='); + if ( (eq <= 0) || (eq >= pair.length()) ) + continue; + String key = pair.substring(0, eq); + String val = pair.substring(eq+1); + props.setProperty(key, val); + } + return props; + } +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/WebEditPageHelper.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java similarity index 52% rename from apps/i2ptunnel/java/src/net/i2p/i2ptunnel/WebEditPageHelper.java rename to apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java index 3f42c682d5b91ed7958c43574c25beea06dbf11e..c2120eb2b24aafe63d9f1ab52ad1aa2f75738139 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/WebEditPageHelper.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java @@ -1,28 +1,38 @@ -package net.i2p.i2ptunnel; +package net.i2p.i2ptunnel.web; +/* + * free (adj.): unencumbered; not under the control of others + * Written by jrandom in 2005 and released into the public domain + * with no warranty of any kind, either expressed or implied. + * It probably won't make your computer catch on fire, or eat + * your children, but it might. Use at your own risk. + * + */ import java.util.List; import java.util.Properties; import java.util.StringTokenizer; import net.i2p.I2PAppContext; +import net.i2p.i2ptunnel.TunnelController; +import net.i2p.i2ptunnel.TunnelControllerGroup; import net.i2p.util.Log; /** - * UUUUuuuuuugly glue code to handle bean interaction from the web, process - * that data, and spit out the results (or the form requested). The basic - * usage is to set any of the fields with data then query the bean via - * getActionResults() which triggers the request processing (taking all the - * provided data, doing what needs to be done) and returns the results of those - * activites. Then a subsequent call to getEditForm() generates the HTML form - * to either edit the currently selected tunnel (if specified) or add a new one. - * This functionality is delegated to the WebEditPageFormGenerator. + * Simple accessor for exposing tunnel info, but also an ugly form handler * */ -public class WebEditPageHelper { - private Log _log; +public class IndexBean { + protected I2PAppContext _context; + protected Log _log; + protected TunnelControllerGroup _group; private String _action; + private int _tunnel; + private long _prevNonce; + private long _curNonce; + private long _nextNonce; + private String _passphrase; + private String _type; - private String _id; private String _name; private String _description; private String _i2cpHost; @@ -44,30 +54,306 @@ public class WebEditPageHelper { private boolean _startOnLoad; private boolean _privKeyGenerate; private boolean _removeConfirmed; - private long _nonce; - public WebEditPageHelper() { + public static final int RUNNING = 1; + public static final int STARTING = 2; + public static final int NOT_RUNNING = 3; + + public static final String PROP_TUNNEL_PASSPHRASE = "i2ptunnel.passphrase"; + static final String PROP_NONCE = IndexBean.class.getName() + ".nonce"; + static final String CLIENT_NICKNAME = "shared clients"; + + public IndexBean() { + _context = I2PAppContext.getGlobalContext(); + _log = _context.logManager().getLog(IndexBean.class); + _group = TunnelControllerGroup.getInstance(); _action = null; - _type = null; - _id = null; - _removeConfirmed = false; - _log = I2PAppContext.getGlobalContext().logManager().getLog(WebEditPageHelper.class); + _tunnel = -1; + _curNonce = -1; + _prevNonce = -1; + try { + String nonce = System.getProperty(PROP_NONCE); + if (nonce != null) + _prevNonce = Long.parseLong(nonce); + } catch (NumberFormatException nfe) {} + _nextNonce = _context.random().nextLong(); + System.setProperty(PROP_NONCE, Long.toString(_nextNonce)); } + public long getNextNonce() { return _nextNonce; } public void setNonce(String nonce) { - if (nonce != null) { - try { - _nonce = Long.parseLong(nonce); - } catch (NumberFormatException nfe) {} + if ( (nonce == null) || (nonce.trim().length() <= 0) ) return; + try { + _curNonce = Long.parseLong(nonce); + } catch (NumberFormatException nfe) { + _curNonce = -1; + } + } + public void setPassphrase(String phrase) { + _passphrase = phrase; + } + + public void setAction(String action) { + if ( (action == null) || (action.trim().length() <= 0) ) return; + _action = action; + } + public void setTunnel(String tunnel) { + if ( (tunnel == null) || (tunnel.trim().length() <= 0) ) return; + try { + _tunnel = Integer.parseInt(tunnel); + } catch (NumberFormatException nfe) { + _tunnel = -1; } } + private boolean validPassphrase(String proposed) { + if (proposed == null) return false; + String pass = _context.getProperty(PROP_TUNNEL_PASSPHRASE); + if ( (pass != null) && (pass.trim().length() > 0) ) + return pass.trim().equals(proposed.trim()); + else + return false; + } + + private String processAction() { + if ( (_action == null) || (_action.trim().length() <= 0) ) + return ""; + if ( (_prevNonce != _curNonce) && (!validPassphrase(_passphrase)) ) + return "Invalid nonce, are you being spoofed?"; + if ("Stop all tunnels".equals(_action)) + return stopAll(); + else if ("Start all tunnels".equals(_action)) + return startAll(); + else if ("Restart all".equals(_action)) + return restartAll(); + else if ("Reload config".equals(_action)) + return reloadConfig(); + else if ("stop".equals(_action)) + return stop(); + else if ("start".equals(_action)) + return start(); + else if ("Save changes".equals(_action)) + return saveChanges(); + else if ("Delete this proxy".equals(_action)) + return deleteTunnel(); + else + return "Action " + _action + " unknown"; + } + private String stopAll() { + if (_group == null) return ""; + List msgs = _group.stopAllControllers(); + return getMessages(msgs); + } + private String startAll() { + if (_group == null) return ""; + List msgs = _group.startAllControllers(); + return getMessages(msgs); + } + private String restartAll() { + if (_group == null) return ""; + List msgs = _group.restartAllControllers(); + return getMessages(msgs); + } + private String reloadConfig() { + if (_group == null) return ""; + + _group.reloadControllers(); + return "Config reloaded"; + } + private String start() { + if (_tunnel < 0) return "Invalid tunnel"; + + List controllers = _group.getControllers(); + if (_tunnel >= controllers.size()) return "Invalid tunnel"; + TunnelController controller = (TunnelController)controllers.get(_tunnel); + controller.startTunnelBackground(); + return ""; + } + + private String stop() { + if (_tunnel < 0) return "Invalid tunnel"; + + List controllers = _group.getControllers(); + if (_tunnel >= controllers.size()) return "Invalid tunnel"; + TunnelController controller = (TunnelController)controllers.get(_tunnel); + controller.stopTunnel(); + return ""; + } + + private String saveChanges() { + TunnelController cur = getController(_tunnel); + + Properties config = getConfig(); + if (config == null) + return "Invalid params"; + + if (cur == null) { + // creating new + cur = new TunnelController(config, "", true); + _group.addController(cur); + if (cur.getStartOnLoad()) + cur.startTunnelBackground(); + } else { + cur.setConfig(config, ""); + } + + if ("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(); + for (int i = 0; i < controllers.size(); i++) { + TunnelController c = (TunnelController)controllers.get(i); + if (c == cur) continue; + if ("httpclient".equals(c.getType()) || "client".equals(c.getType())) { + Properties cOpt = c.getConfig(""); + if (_tunnelCount != null) { + cOpt.setProperty("option.inbound.quantity", _tunnelCount); + cOpt.setProperty("option.outbound.quantity", _tunnelCount); + } + if (_tunnelDepth != null) { + cOpt.setProperty("option.inbound.length", _tunnelDepth); + cOpt.setProperty("option.outbound.length", _tunnelDepth); + } + cOpt.setProperty("option.inbound.nickname", CLIENT_NICKNAME); + cOpt.setProperty("option.outbound.nickname", CLIENT_NICKNAME); + + c.setConfig(cOpt, ""); + } + } + } + + List msgs = doSave(); + msgs.add(0, "Changes saved"); + return getMessages(msgs); + } + private List doSave() { + _group.saveConfig(); + return _group.clearAllMessages(); + } + private String deleteTunnel() { + if (!_removeConfirmed) + return "Please confirm removal"; + + TunnelController cur = getController(_tunnel); + if (cur == null) + return "Invalid tunnel number"; + + List msgs = _group.removeController(cur); + msgs.addAll(doSave()); + return getMessages(msgs); + } + /** - * Used for form submit - either "Save" or Remove" + * Executes any action requested (start/stop/etc) and dump out the + * messages. + * */ - public void setAction(String action) { - _action = (action != null ? action.trim() : null); + public String getMessages() { + if (_group == null) + return ""; + + StringBuffer buf = new StringBuffer(512); + if (_action != null) { + try { + buf.append(processAction()).append("\n"); + } catch (Exception e) { + _log.log(Log.CRIT, "Error processing " + _action, e); + } + } + getMessages(_group.clearAllMessages(), buf); + return buf.toString(); + } + + //// + // The remaining methods are simple bean props for the jsp to query + //// + + public int getTunnelCount() { + if (_group == null) return 0; + return _group.getControllers().size(); + } + + public boolean isClient(int tunnelNum) { + TunnelController cur = getController(tunnelNum); + if (cur == null) return false; + return ( ("client".equals(cur.getType())) || ("httpclient".equals(cur.getType())) ); + } + + public String getTunnelName(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun != null) + return tun.getName(); + else + return ""; + } + + public String getClientPort(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun != null) + return tun.getListenPort(); + else + return ""; + } + + public String getTunnelType(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun != null) + return getTypeName(tun.getType()); + else + return ""; + } + + public String getTypeName(String internalType) { + if ("client".equals(internalType)) return "Client proxy"; + else if ("httpclient".equals(internalType)) return "HTTP proxy"; + else if ("server".equals(internalType)) return "Server"; + else if ("httpserver".equals(internalType)) return "HTTP server"; + else return internalType; + } + + public String getClientInterface(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun != null) + return tun.getListenOnInterface(); + else + return ""; + } + + public int getTunnelStatus(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun == null) return NOT_RUNNING; + if (tun.getIsRunning()) return RUNNING; + else if (tun.getIsStarting()) return STARTING; + else return NOT_RUNNING; + } + + public String getTunnelDescription(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun != null) + return tun.getDescription(); + else + return ""; } + + public String getClientDestination(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun == null) return ""; + if ("client".equals(tun.getType())) return tun.getTargetDestination(); + else return tun.getProxyList(); + } + + public String getServerTarget(int tunnel) { + TunnelController tun = getController(tunnel); + if (tun != null) + return tun.getTargetHost() + ':' + tun.getTargetPort(); + else + return ""; + } + + /// + /// bean props for form submission + /// + /** * What type of tunnel (httpclient, client, or server). This is * required when adding a new tunnel. @@ -76,17 +362,7 @@ public class WebEditPageHelper { public void setType(String type) { _type = (type != null ? type.trim() : null); } - /** - * Which particular tunnel should be edited (index into the current - * TunnelControllerGroup's getControllers() list). This is required - * when editing a tunnel, but not when adding a new one. - * - */ - public void setNum(String id) { - _id = (id != null ? id.trim() : null); - } String getType() { return _type; } - String getNum() { return _id; } /** Short name of the tunnel */ public void setName(String name) { @@ -158,14 +434,6 @@ public class WebEditPageHelper { public void setPrivKeyFile(String file) { _privKeyFile = (file != null ? file.trim() : null); } - /** - * If called with any value, we want to generate a new destination - * for this server tunnel. This won't cause any existing private keys - * to be overwritten, however. - */ - public void setPrivKeyGenerate(String moo) { - _privKeyGenerate = true; - } /** * If called with any value (and the form submitted with action=Remove), * we really do want to stop and remove the tunnel. @@ -186,145 +454,7 @@ public class WebEditPageHelper { public void setProfile(String profile) { _profile = profile; } - - /** - * Process the form and display any resulting messages - * - */ - public String getActionResults() { - try { - return processAction(); - } catch (Throwable t) { - _log.log(Log.CRIT, "Internal error processing request", t); - return "Internal error - " + t.getMessage(); - } - } - - /** - * Generate an HTML form to edit / create a tunnel according to the - * specified fields - */ - public String getEditForm() { - try { - return WebEditPageFormGenerator.getForm(this); - } catch (Throwable t) { - _log.log(Log.CRIT, "Internal error retrieving edit form", t); - return "Internal error - " + t.getMessage(); - } - } - /** - * Retrieve the tunnel pointed to by the current id - * - */ - TunnelController getTunnelController() { - if (_id == null) return null; - int id = -1; - try { - id = Integer.parseInt(_id); - List controllers = TunnelControllerGroup.getInstance().getControllers(); - if ( (id < 0) || (id >= controllers.size()) ) - return null; - else - return (TunnelController)controllers.get(id); - } catch (NumberFormatException nfe) { - if (_log.shouldLog(Log.WARN)) - _log.warn("Invalid tunnel id [" + _id + "]", nfe); - return null; - } - } - - private String processAction() { - if ( (_action == null) || (_action.trim().length() <= 0) ) - return ""; - String expected = System.getProperty(getClass().getName() + ".nonce"); - if ( (expected == null) || (!expected.equals(Long.toString(_nonce))) ) - return "<b>Invalid nonce, are you being spoofed?</b>"; - if ("Save".equals(_action)) - return save(); - else if ("Remove".equals(_action)) - return remove(); - else - return "Action <i>" + _action + "</i> unknown"; - } - - private String remove() { - if (!_removeConfirmed) - return "Please confirm removal"; - - TunnelController cur = getTunnelController(); - if (cur == null) - return "Invalid tunnel number"; - - List msgs = TunnelControllerGroup.getInstance().removeController(cur); - msgs.addAll(doSave()); - return getMessages(msgs); - } - - private String save() { - if (_type == null) - return "<b>Invalid form submission (no type?)</b>"; - Properties config = getConfig(); - if (config == null) - return "<b>Invalid params</b>"; - - TunnelController cur = getTunnelController(); - if (cur == null) { - // creating new - cur = new TunnelController(config, "", _privKeyGenerate); - TunnelControllerGroup.getInstance().addController(cur); - if (cur.getStartOnLoad()) - cur.startTunnelBackground(); - } else { - cur.setConfig(config, ""); - } - - if ("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 = TunnelControllerGroup.getInstance().getControllers(); - for (int i = 0; i < controllers.size(); i++) { - TunnelController c = (TunnelController)controllers.get(i); - if (c == cur) continue; - if ("httpclient".equals(c.getType()) || "client".equals(c.getType())) { - Properties cOpt = c.getConfig(""); - if (_tunnelCount != null) { - cOpt.setProperty("option.inbound.quantity", _tunnelCount); - cOpt.setProperty("option.outbound.quantity", _tunnelCount); - } - if (_tunnelDepth != null) { - cOpt.setProperty("option.inbound.length", _tunnelDepth); - cOpt.setProperty("option.outbound.length", _tunnelDepth); - } - // these are per-proxy settings, not per-session settings, and - // as such don't need to be shared. the values are propogated - // to the current tunnel's settings via cur.setConfig above - /* - if (_connectDelay) - cOpt.setProperty("option.i2p.streaming.connectDelay", "1000"); - else - cOpt.setProperty("option.i2p.streaming.connectDelay", "0"); - if ("interactive".equals(_profile)) - cOpt.setProperty("option.i2p.streaming.maxWindowSize", "1"); - else - cOpt.remove("option.i2p.streaming.maxWindowSize"); - */ - if (_name != null) { - cOpt.setProperty("option.inbound.nickname", _name); - cOpt.setProperty("option.outbound.nickname", _name); - } - c.setConfig(cOpt, ""); - } - } - } - - return getMessages(doSave()); - } - private List doSave() { - TunnelControllerGroup.getInstance().saveConfig(); - return TunnelControllerGroup.getInstance().clearAllMessages(); - } - /** * Based on all provided data, create a set of configuration parameters * suitable for use in a TunnelController. This will replace (not add to) @@ -344,6 +474,9 @@ public class WebEditPageHelper { config.setProperty("interface", _reachableBy); if (_proxyList != null) config.setProperty("proxyList", _proxyList); + + config.setProperty("option.inbound.nickname", CLIENT_NICKNAME); + config.setProperty("option.outbound.nickname", CLIENT_NICKNAME); } else if ("client".equals(_type)) { if (_port != null) config.setProperty("listenPort", _port); @@ -353,6 +486,9 @@ public class WebEditPageHelper { 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); } else if ("server".equals(_type)) { if (_targetHost != null) config.setProperty("targetHost", _targetHost); @@ -422,32 +558,43 @@ public class WebEditPageHelper { else config.setProperty("option.i2p.streaming.connectDelay", "0"); if (_name != null) { - config.setProperty("option.inbound.nickname", _name); - config.setProperty("option.outbound.nickname", _name); - } + if ( (!"client".equals(_type)) && (!"httpclient".equals(_type)) ) { + config.setProperty("option.inbound.nickname", _name); + config.setProperty("option.outbound.nickname", _name); + } else { + config.setProperty("option.inbound.nickname", CLIENT_NICKNAME); + config.setProperty("option.outbound.nickname", CLIENT_NICKNAME); + } + } if ("interactive".equals(_profile)) config.setProperty("option.i2p.streaming.maxWindowSize", "1"); else config.remove("option.i2p.streaming.maxWindowSize"); } - /** - * Pretty print the messages provided - * - */ + /// + /// + /// + + protected TunnelController getController(int tunnel) { + if (tunnel < 0) return null; + if (_group == null) return null; + List controllers = _group.getControllers(); + if (controllers.size() > tunnel) + return (TunnelController)controllers.get(tunnel); + else + return null; + } + private String getMessages(List msgs) { - if (msgs == null) return ""; - int num = msgs.size(); - switch (num) { - case 0: return ""; - case 1: return (String)msgs.get(0); - default: - StringBuffer buf = new StringBuffer(512); - buf.append("<ul>"); - for (int i = 0; i < num; i++) - buf.append("<li>").append((String)msgs.get(i)).append("</li>\n"); - buf.append("</ul>\n"); - return buf.toString(); + StringBuffer buf = new StringBuffer(128); + getMessages(msgs, buf); + return buf.toString(); + } + private void getMessages(List msgs, StringBuffer buf) { + if (msgs == null) return; + for (int i = 0; i < msgs.size(); i++) { + buf.append((String)msgs.get(i)).append("\n"); } } } diff --git a/apps/i2ptunnel/jsp/edit.jsp b/apps/i2ptunnel/jsp/edit.jsp index 04b3390887c3723f24a21d3a45ac91d39975163b..bbfff2fbca3b0a6a4a41748e565f116ef8525cc5 100644 --- a/apps/i2ptunnel/jsp/edit.jsp +++ b/apps/i2ptunnel/jsp/edit.jsp @@ -1,16 +1,26 @@ -<%@page contentType="text/html" %> +<%@page contentType="text/html" import="net.i2p.i2ptunnel.web.EditBean" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> - -<html><head> -<title>I2PTunnel edit</title> -</head><body> - -<a href="index.jsp">Back</a> - -<jsp:useBean class="net.i2p.i2ptunnel.WebEditPageHelper" id="helper" scope="request" /> -<jsp:setProperty name="helper" property="*" /> -<b><jsp:getProperty name="helper" property="actionResults" /></b> - -<jsp:getProperty name="helper" property="editForm" /> -</body> -</html> +<% String tun = request.getParameter("tunnel"); + if (tun != null) { + try { + int curTunnel = Integer.parseInt(tun); + if (EditBean.staticIsClient(curTunnel)) { + %><jsp:include page="editClient.jsp" /><% + } else { + %><jsp:include page="editServer.jsp" /><% + } + } catch (NumberFormatException nfe) { + %>Invalid tunnel parameter<% + } + } else { + String type = request.getParameter("type"); + int curTunnel = -1; + if ("client".equals(type) || "httpclient".equals(type)) { + %><jsp:include page="editClient.jsp" /><% + } else if ("server".equals(type) || "httpserver".equals(type)) { + %><jsp:include page="editServer.jsp" /><% + } else { + %>Invalid tunnel type<% + } + } +%> \ No newline at end of file diff --git a/apps/i2ptunnel/jsp/editClient.jsp b/apps/i2ptunnel/jsp/editClient.jsp new file mode 100644 index 0000000000000000000000000000000000000000..882a6941705cd5988c80c7a64bee5f0b9ce0f502 --- /dev/null +++ b/apps/i2ptunnel/jsp/editClient.jsp @@ -0,0 +1,280 @@ +<%@page contentType="text/html" %> +<jsp:useBean class="net.i2p.i2ptunnel.web.EditBean" id="editBean" scope="request" /> +<% String tun = request.getParameter("tunnel"); + int curTunnel = -1; + if (tun != null) { + try { + curTunnel = Integer.parseInt(tun); + } catch (NumberFormatException nfe) { + curTunnel = -1; + } + } +%> +<html> +<head> +<title>I2PTunnel Webmanager</title> +</head> +<body> +<form action="index.jsp"> +<input type="hidden" name="tunnel" value="<%=request.getParameter("tunnel")%>" /> +<input type="hidden" name="nonce" value="<%=editBean.getNextNonce()%>" /> +<table width="80%" align="center" border="0" cellpadding="1" cellspacing="1"> +<tr> +<td style="background-color:#000"> +<div style="background-color:#ffffed"> +<table width="100%" align="center" border="0" cellpadding="4" cellspacing="1"> +<tr> +<td colspan="2" align="center"> +<% if (curTunnel >= 0) { %> +<b>Edit proxy settings</b> +<% } else { %> +<b>New proxy settings</b> +<% } %> +</td> +</tr> +<tr> +<td><b>Name: </b> +</td> +<td> +<input type="text" size="30" maxlength="50" name="name" value="<%=editBean.getTunnelName(curTunnel)%>" /> +</td> +</tr> +<tr> +<td><b>Type: </b> +<td><% +if (curTunnel >= 0) { + %><%=editBean.getTunnelType(curTunnel)%> +<input type="hidden" name="type" value="<%=editBean.getInternalType(curTunnel)%>" /> +<% +} else { + %><%=editBean.getTypeName(request.getParameter("type"))%> +<input type="hidden" name="type" value="<%=request.getParameter("type")%>" /> +<% +} +%></td> +</tr> +<tr> +<td><b>Description: </b> +</td> +<td> +<input type="text" size="60" maxlength="80" name="description" value="<%=editBean.getTunnelDescription(curTunnel)%>" /> +</td> +</tr> +<tr> +<td><b>Start automatically?:</b> +</td> +<td> +<% if (editBean.startAutomatically(curTunnel)) { %> +<input value="1" type="checkbox" name="startOnLoad" checked="true" /> +<% } else { %> +<input value="1" type="checkbox" name="startOnLoad" /> +<% } %> +<i>(Check the Box for 'YES')</i> +</td> +</tr> +<tr> +<td> <b>Listening Port:</b> +</td> +<td> +<input type="text" size="6" maxlength="5" name="port" value="<%=editBean.getClientPort(curTunnel)%>" /> +</td> +</tr> +<tr> +<td><b> Accessable by:</b> +</td> +<td> +<select name="reachableBy"> +<% String clientInterface = editBean.getClientInterface(curTunnel); %> +<% if (("127.0.0.1".equals(clientInterface)) || (clientInterface == null) || (clientInterface.trim().length() <= 0)) { %> +<option value="127.0.0.1" selected="true">Locally (127.0.0.1)</option> +<option value="0.0.0.0">Everyone (0.0.0.0)</option> +<option value="other">LAN Hosts (Please specify your LAN address)</option> + +</select> + +<b>others:</b> +<input type="text" name="reachablyByOther" size="20" /> +<% } else if ("0.0.0.0".equals(clientInterface)) { %> +<option value="127.0.0.1">Locally (127.0.0.1)</option> +<option value="0.0.0.0" selected="true">Everyone (0.0.0.0)</option> +<option value="other">LAN Hosts (Please specify your LAN address)</option> + +</select> + +<b>others:</b> +<input type="text" name="reachablyByOther" size="20" /> +<% } else { %> +<option value="127.0.0.1">Locally (127.0.0.1)</option> +<option value="0.0.0.0">Everyone (0.0.0.0)</option> +<option value="other" selected="true">LAN Hosts (Please specify your LAN address)</option> + +</select> + +<b>others:</b> +<input type="text" name="reachablyByOther" size="20" value="<%=clientInterface%>" /> +<% } %> + +</td> +</tr> +<tr> +<% if ("httpclient".equals(editBean.getInternalType(curTunnel))) { %> +<td><b>Outproxies:</b> +<% } else { %> +<td><b>Target:</b> +<% } %> +</td> +<td> +<% if ("httpclient".equals(editBean.getInternalType(curTunnel))) { %> +<input type="text" name="proxyList" value="<%=editBean.getClientDestination(curTunnel)%>" /> +<% } else { %> +<input type="text" name="targetDestination" value="<%=editBean.getClientDestination(curTunnel)%>" /> +<% } %> +<i>(name or destination)</i> +</td> +</tr> +<tr> +<td> +<b>Delayed connect?</b> +</td> +<td> +<% if (editBean.shouldDelay(curTunnel)) { %> +<input type="checkbox" value="1000" name="connectDelay" checked="true" /> +<% } else { %> +<input type="checkbox" value="1000" name="connectDelay" /> +<% } %> +<i>(for request/response connections)</i> +</td> +</tr> +<tr> +<td><b>Profile:</b> +</td> +<td> +<select name="profile"> +<% if (editBean.isInteractive(curTunnel)) { %> +<option value="interactive" selected="true">interactive connection </option> +<option value="bulk">bulk connection (downloads/websites/BT) </option> +<% } else { %> +<option value="interactive">interactive connection </option> +<option value="bulk" selected="true">bulk connection (downloads/websites/BT) </option> +<% } %> +</select> +</td> +</tr> +<tr> +<td colspan="2" align="center"> +<b><hr size="1"> +Advanced networking options<br /> +<span style="color:#dd0000;">(Those are shared between ALL your Client proxies!)</span></b> +</td> +</tr> +<tr> +<td> +<b>Tunnel depth:</b> +</td> +<td><select name="tunnelDepth"> +<% int tunnelDepth = editBean.getTunnelDepth(curTunnel, 2); + switch (tunnelDepth) { + case 0: %> +<option value="0" selected="true">0 hop tunnel (low anonymity, low latency)</option> +<option value="1" >1 hop tunnel (medium anonymity, medium latency)</option> +<option value="2" >2 hop tunnel (high anonymity, high latency)</option> +<% break; + case 1: %> +<option value="0" >0 hop tunnel (low anonymity, low latency)</option> +<option value="1" selected="true">1 hop tunnel (medium anonymity, medium latency)</option> +<option value="2" >2 hop tunnel (high anonymity, high latency)</option> +<% break; + case 2: %> +<option value="0" >0 hop tunnel (low anonymity, low latency)</option> +<option value="1" >1 hop tunnel (medium anonymity, medium latency)</option> +<option value="2" selected="true">2 hop tunnel (high anonymity, high latency)</option> +<% break; + default: %> +<option value="0" >0 hop tunnel (low anonymity, low latency)</option> +<option value="1" >1 hop tunnel (medium anonymity, medium latency)</option> +<option value="2" >2 hop tunnel (high anonymity, high latency)</option> +<option value="<%=tunnelDepth%>" ><%=tunnelDepth%> hop tunnel</option> +<% } %> +</select> +</td> +</tr> +<tr> +<td><b>Tunnel count:</b> +</td> +<td> +<select name="tunnelCount"> +<% int tunnelCount = editBean.getTunnelCount(curTunnel, 2); + switch (tunnelCount) { + case 1: %> +<option value="1" selected="true" >1 inbound tunnel (low bandwidth usage, less reliability)</option> +<option value="2" >2 inbound tunnels (standard bandwidth usage, standard reliability)</option> +<option value="3" >3 inbound tunnels (higher bandwidth usage, higher reliability)</option> +<% break; + case 2: %> +<option value="1" >1 inbound tunnel (low bandwidth usage, less reliability)</option> +<option value="2" selected="true" >2 inbound tunnels (standard bandwidth usage, standard reliability)</option> +<option value="3" >3 inbound tunnels (higher bandwidth usage, higher reliability)</option> +<% break; + case 3: %> +<option value="1" >1 inbound tunnel (low bandwidth usage, less reliability)</option> +<option value="2" >2 inbound tunnels (standard bandwidth usage, standard reliability)</option> +<option value="3" selected="true" >3 inbound tunnels (higher bandwidth usage, higher reliability)</option> +<% break; + default: %> +<option value="1" >1 inbound tunnel (low bandwidth usage, less reliability)</option> +<option value="2" >2 inbound tunnels (standard bandwidth usage, standard reliability)</option> +<option value="3" >3 inbound tunnels (higher bandwidth usage, higher reliability)</option> +<option value="<%=tunnelDepth%>" ><%=tunnelDepth%> inbound tunnels</option> +<% } %> +</select> +</td> +<tr> +<td><b>I2CP host:</b> +</td> +<td> +<input type="text" name="clientHost" size="20" value="<%=editBean.getI2CPHost(curTunnel)%>" /> +</td> +</tr> +<tr> +<td><b>I2CP port:</b> +</td> +<td> +<input type="text" name="clientPort" size="20" value="<%=editBean.getI2CPPort(curTunnel)%>" /><br /> +</td> +</tr> +<tr> +<td><b>Custom options:</b> +</td> +<td> +<input type="text" name="customOptions" size="60" value="<%=editBean.getCustomOptions(curTunnel)%>" /> +</td> +</tr> +<tr> +<td colspan="2"> +<hr size="1"> +</td> +</tr> +<tr> +<td> +<b>Save:</b> +</td> +<td> +<input type="submit" name="action" value="Save changes" /> +</td> +</tr> +<tr> +<td><b>Delete?</b> +</td> +<td> +<input type="submit" name="action" value="Delete this proxy" /> +<b><span style="color:#dd0000;">confirm delete:</span></b> +<input type="checkbox" value="true" name="removeConfirm" /> +</td> +</tr> +</table> +</td> +</tr> +</table> +</form> +</body> +</html> diff --git a/apps/i2ptunnel/jsp/editServer.jsp b/apps/i2ptunnel/jsp/editServer.jsp new file mode 100644 index 0000000000000000000000000000000000000000..1381a8ee6bbdc9651d636f0a6aa8d583cdac0d9c --- /dev/null +++ b/apps/i2ptunnel/jsp/editServer.jsp @@ -0,0 +1,229 @@ +<%@page contentType="text/html" %> +<jsp:useBean class="net.i2p.i2ptunnel.web.EditBean" id="editBean" scope="request" /> +<% String tun = request.getParameter("tunnel"); + int curTunnel = -1; + if (tun != null) { + try { + curTunnel = Integer.parseInt(tun); + } catch (NumberFormatException nfe) { + curTunnel = -1; + } + } +%> +<html> +<head> +<title>I2PTunnel Webmanager</title> +</head> +<body> +<form action="index.jsp"> +<input type="hidden" name="tunnel" value="<%=request.getParameter("tunnel")%>" /> +<input type="hidden" name="nonce" value="<%=editBean.getNextNonce()%>" /> +<table width="80%" align="center" border="0" cellpadding="1" cellspacing="1"> +<tr> +<td style="background-color:#000"> +<div style="background-color:#ffffed"> +<table width="100%" align="center" border="0" cellpadding="4" cellspacing="1"> +<tr> +<td colspan="2" align="center"> +<% if (curTunnel >= 0) { %> +<b>Edit server settings</b> +<% } else { %> +<b>New server settings</b> +<% } %> +</td> +</tr> +<tr> +<td><b>Name: </b> +</td> +<td> +<input type="text" size="30" maxlength="50" name="name" value="<%=editBean.getTunnelName(curTunnel)%>" /> +</td> +</tr> +<tr> +<td><b>Type: </b> +<td><% +if (curTunnel >= 0) { + %><%=editBean.getTunnelType(curTunnel)%> +<input type="hidden" name="type" value="<%=editBean.getInternalType(curTunnel)%>" /> +<% +} else { + %><%=editBean.getTypeName(request.getParameter("type"))%> +<input type="hidden" name="type" value="<%=request.getParameter("type")%>" /> +<% +} +%></td> +</tr> +<tr> +<td><b>Description: </b> +</td> +<td> +<input type="text" size="60" maxlength="80" name="description" value="<%=editBean.getTunnelDescription(curTunnel)%>" /> +</td> +</tr> +<tr> +<td><b>Start automatically?:</b> +</td> +<td> +<% if (editBean.startAutomatically(curTunnel)) { %> +<input value="1" type="checkbox" name="startOnLoad" checked="true" /> +<% } else { %> +<input value="1" type="checkbox" name="startOnLoad" /> +<% } %> +<i>(Check the Box for 'YES')</i> +</td> +</tr> +<tr> +<td> <b>Target:</b> +</td> +<td> +Host: <input type="text" size="20" name="targetHost" value="<%=editBean.getTargetHost(curTunnel)%>" /> +Port: <input type="text" size="4" maxlength="4" name="targetPort" value="<%=editBean.getTargetPort(curTunnel)%>" /> +</td> +</tr> +<% String curType = editBean.getInternalType(curTunnel); + if ( (curType == null) || (curType.trim().length() <= 0) ) + curType = request.getParameter("type"); + if ("httpserver".equals(curType)) { %> +<tr> +<td><b>Website name:</b></td> +<td><input type="text" size="20" name="spoofedHost" value="<%=editBean.getSpoofedHost(curTunnel)%>" /> +</td></tr> +<% } %> +<tr> +<td><b>Private key file:</b> +</td> +<td><input type="text" size="30" name="privKeyFile" value="<%=editBean.getPrivateKeyFile(curTunnel)%>" /></td> +</tr> +<tr> +<td><b>Profile:</b> +</td> +<td> +<select name="profile"> +<% if (editBean.isInteractive(curTunnel)) { %> +<option value="interactive" selected="true">interactive connection </option> +<option value="bulk">bulk connection (downloads/websites/BT) </option> +<% } else { %> +<option value="interactive">interactive connection </option> +<option value="bulk" selected="true">bulk connection (downloads/websites/BT) </option> +<% } %> +</select> +</td> +</tr> +<tr> +<td colspan="2" align="center"> +<b><hr size="1"> +Advanced networking options<br /> +</b> +</td> +</tr> +<tr> +<td> +<b>Tunnel depth:</b> +</td> +<td><select name="tunnelDepth"> +<% int tunnelDepth = editBean.getTunnelDepth(curTunnel, 2); + switch (tunnelDepth) { + case 0: %> +<option value="0" selected="true">0 hop tunnel (low anonymity, low latency)</option> +<option value="1" >1 hop tunnel (medium anonymity, medium latency)</option> +<option value="2" >2 hop tunnel (high anonymity, high latency)</option> +<% break; + case 1: %> +<option value="0" >0 hop tunnel (low anonymity, low latency)</option> +<option value="1" selected="true">1 hop tunnel (medium anonymity, medium latency)</option> +<option value="2" >2 hop tunnel (high anonymity, high latency)</option> +<% break; + case 2: %> +<option value="0" >0 hop tunnel (low anonymity, low latency)</option> +<option value="1" >1 hop tunnel (medium anonymity, medium latency)</option> +<option value="2" selected="true">2 hop tunnel (high anonymity, high latency)</option> +<% break; + default: %> +<option value="0" >0 hop tunnel (low anonymity, low latency)</option> +<option value="1" >1 hop tunnel (medium anonymity, medium latency)</option> +<option value="2" >2 hop tunnel (high anonymity, high latency)</option> +<option value="<%=tunnelDepth%>" ><%=tunnelDepth%> hop tunnel</option> +<% } %> +</select> +</td> +</tr> +<tr> +<td><b>Tunnel count:</b> +</td> +<td> +<select name="tunnelCount"> +<% int tunnelCount = editBean.getTunnelCount(curTunnel, 2); + switch (tunnelCount) { + case 1: %> +<option value="1" selected="true" >1 inbound tunnel (low bandwidth usage, less reliability)</option> +<option value="2" >2 inbound tunnels (standard bandwidth usage, standard reliability)</option> +<option value="3" >3 inbound tunnels (higher bandwidth usage, higher reliability)</option> +<% break; + case 2: %> +<option value="1" >1 inbound tunnel (low bandwidth usage, less reliability)</option> +<option value="2" selected="true" >2 inbound tunnels (standard bandwidth usage, standard reliability)</option> +<option value="3" >3 inbound tunnels (higher bandwidth usage, higher reliability)</option> +<% break; + case 3: %> +<option value="1" >1 inbound tunnel (low bandwidth usage, less reliability)</option> +<option value="2" >2 inbound tunnels (standard bandwidth usage, standard reliability)</option> +<option value="3" selected="true" >3 inbound tunnels (higher bandwidth usage, higher reliability)</option> +<% break; + default: %> +<option value="1" >1 inbound tunnel (low bandwidth usage, less reliability)</option> +<option value="2" >2 inbound tunnels (standard bandwidth usage, standard reliability)</option> +<option value="3" >3 inbound tunnels (higher bandwidth usage, higher reliability)</option> +<option value="<%=tunnelDepth%>" ><%=tunnelDepth%> inbound tunnels</option> +<% } %> +</select> +</td> +<tr> +<td><b>I2CP host:</b> +</td> +<td> +<input type="text" name="clientHost" size="20" value="<%=editBean.getI2CPHost(curTunnel)%>" /> +</td> +</tr> +<tr> +<td><b>I2CP port:</b> +</td> +<td> +<input type="text" name="clientPort" size="20" value="<%=editBean.getI2CPPort(curTunnel)%>" /><br /> +</td> +</tr> +<tr> +<td><b>Custom options:</b> +</td> +<td> +<input type="text" name="customOptions" size="60" value="<%=editBean.getCustomOptions(curTunnel)%>" /> +</td> +</tr> +<tr> +<td colspan="2"> +<hr size="1"> +</td> +</tr> +<tr> +<td> +<b>Save:</b> +</td> +<td> +<input type="submit" name="action" value="Save changes" /> +</td> +</tr> +<tr> +<td><b>Delete?</b> +</td> +<td> +<input type="submit" name="action" value="Delete this proxy" /> +<b><span style="color:#dd0000;">confirm delete:</span></b> +<input type="checkbox" value="true" name="removeConfirm" /> +</td> +</tr> +</table> +</td> +</tr> +</table> +</form> +</body> +</html> diff --git a/apps/i2ptunnel/jsp/index.jsp b/apps/i2ptunnel/jsp/index.jsp index 71b4d95240f97b0a22e642f6cdaa3687cdb85180..6aadf69a86b9f95c4215c3233a6afce59f3bb7d4 100644 --- a/apps/i2ptunnel/jsp/index.jsp +++ b/apps/i2ptunnel/jsp/index.jsp @@ -1,26 +1,181 @@ -<%@page contentType="text/html" %> +<%@page contentType="text/html" import="net.i2p.i2ptunnel.web.IndexBean" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<jsp:useBean class="net.i2p.i2ptunnel.web.IndexBean" id="indexBean" scope="request" /> +<jsp:setProperty name="indexBean" property="*" /> -<html><head> -<title>I2PTunnel status</title> -</head><body> +<html> +<head> +<title>I2PTunnel Webmanager</title> +</head> +<body style="font-family: Verdana, Tahoma, Helvetica, sans-serif;font-size:12px;"> +<table width="90%" align="center" border="0" cellpadding="1" cellspacing="1"> +<tr> +<td style="background-color:#000"> +<div style="background-color:#ffffed"> +<table width="100%" align="center" border="0" cellpadding="4" cellspacing="1"> +<tr> +<td nowrap="true"><b>New Messages: </b><br /> +<a href="index.jsp">refresh</a> +</td> +<td> +<textarea rows="3" cols="60" readonly="true"><jsp:getProperty name="indexBean" property="messages" /></textarea> +</td> +</tr> +</table> +</td> +</tr> +</table> +<br /> +<table width="90%" align="center" border="0" cellpadding="1" cellspacing="1"> +<tr> +<td style="background-color:#000"> +<div style="background-color:#ffffed"> -<jsp:useBean class="net.i2p.i2ptunnel.WebStatusPageHelper" id="helper" scope="request" /> -<jsp:setProperty name="helper" property="*" /> -<h2>Messages since last page load:</h2> -<b><jsp:getProperty name="helper" property="actionResults" /></b> - -<jsp:getProperty name="helper" property="summaryList" /> +<table width="100%" align="center" border="0" cellpadding="4" cellspacing="1"> +<tr> +<td colspan="7" align="center" valign="middle" style="font-size:14px;"> +<b>Your Client Tunnels:</b><br /> +<hr size="1" /> +</td> +</tr> +<tr> +<td width="15%"><b>Name:</b></td> +<td><b>Port:</b></td> +<td><b>Type:</b></td> +<td><b>Interface:</b></td> +<td><b>Status:</b></td> +</tr> +<% for (int curClient = 0; curClient < indexBean.getTunnelCount(); curClient++) { + if (!indexBean.isClient(curClient)) continue; %> +<tr> +<td valign="top" align="left"> +<b><a href="edit.jsp?tunnel=<%=curClient%>"><%=indexBean.getTunnelName(curClient) %></a></b></td> +<td valign="top" align="left" nowrap="true"><%=indexBean.getClientPort(curClient) %></td> +<td valign="top" align="left" nowrap="true"><%=indexBean.getTunnelType(curClient) %></td> +<td valign="top" align="left" nowrap="true"><%=indexBean.getClientInterface(curClient) %></td> +<td valign="top" align="left" nowrap="true"><% + switch (indexBean.getTunnelStatus(curClient)) { + case IndexBean.STARTING: +%><b><span style="color:#339933">Starting...</span></b> +<a href="index.jsp?nonce=<%=indexBean.getNextNonce()%>&action=stop&tunnel=<%=curClient%>">[STOP]</a><% + break; + case IndexBean.RUNNING: +%><b><span style="color:#00dd00">Running</span></b> +<a href="index.jsp?nonce=<%=indexBean.getNextNonce()%>&action=stop&tunnel=<%=curClient%>">[STOP]</a><% + break; + case IndexBean.NOT_RUNNING: +%><b><span style="color:#dd0000">Not Running</span></b> +<a href="index.jsp?nonce=<%=indexBean.getNextNonce()%>&action=start&tunnel=<%=curClient%>">[START]</a><% + break; + } +%></td> +</tr> +<tr><td align="right" valign="top">Destination:</td> + <td colspan="4"><input align="left" size="40" valign="top" style="overflow: hidden" readonly="true" value="<%=indexBean.getClientDestination(curClient) %>" /></td></tr> +<tr> + <td valign="top" align="right">Description:</td> + <td valign="top" align="left" colspan="4"><%=indexBean.getTunnelDescription(curClient) %></td> +</tr> +<% } %> +</table> +</td> +</tr> +</table> +<br /> +<table width="90%" align="center" border="0" cellpadding="1" cellspacing="1"> +<tr> +<td style="background-color:#000"> +<div style="background-color:#ffffed"> +<table width="100%" align="center" border="0" cellpadding="4" cellspacing="1"> +<tr> +<td colspan="5" align="center" valign="middle" style="font-size:14px;"> +<b>Your Server Tunnels:</b><br /> +<hr size="1" /> +</td> +</tr> +<tr> +<td width="15%"><b>Name: </b> +</td> +<td> +<b>Points at:</b> +</td> +<td> +<b>Status:</b> +</td> +</tr> + +<% for (int curServer = 0; curServer < indexBean.getTunnelCount(); curServer++) { + if (indexBean.isClient(curServer)) continue; %> + +<tr> +<td valign="top"> +<b><a href="edit.jsp?tunnel=<%=curServer%>"><%=indexBean.getTunnelName(curServer)%></a></b> +</td> +<td valign="top"><%=indexBean.getServerTarget(curServer)%></td> +<td valign="top" nowrap="true"><% + switch (indexBean.getTunnelStatus(curServer)) { + case IndexBean.RUNNING: +%><b><span style="color:#00dd00">Running</span></b> +<a href="index.jsp?nonce=<%=indexBean.getNextNonce()%>&action=stop&tunnel=<%=curServer%>">[STOP]</a><% + break; + case IndexBean.NOT_RUNNING: +%><b><span style="color:#dd0000">Not Running</span></b> +<a href="index.jsp?nonce=<%=indexBean.getNextNonce()%>&action=start&tunnel=<%=curServer%>">[START]</a><% + break; + case IndexBean.STARTING: +%> +<b><span style="color:#339933">Starting...</span></b> +<a href="index.jsp?nonce=<%=indexBean.getNextNonce()%>&action=stop&tunnel=<%=curServer%>">[STOP]</a><% + break; + } +%> +</td> +</tr> +<tr><td valign="top" align="right">Description:</td> + <td valign="top" align="left" colspan="2"><%=indexBean.getTunnelDescription(curServer)%></td></tr> +<% } %> + +</table> +</td> +</tr> +</table> +<br /> +<table width="90%" align="center" border="0" cellpadding="1" cellspacing="1"> +<tr> +<td style="background-color:#000"> +<div style="background-color:#ffffed"> +<table width="100%" align="center" border="0" cellpadding="4" cellspacing="1"> +<tr> +<td colspan="2" align="center" valign="middle"> +<b>Operations Menu - Please chose from below!</b><br /><br /> +</td> +</tr> +<tr> +<form action="index.jsp" method="GET"> +<td > +<input type="hidden" name="nonce" value="<%=indexBean.getNextNonce()%>" /> +<input type="submit" name="action" value="Stop all tunnels" /> +<input type="submit" name="action" value="Start all tunnels" /> +<input type="submit" name="action" value="Restart all" /> +<input type="submit" name="action" value="Reload config" /> +</td> +</form> <form action="edit.jsp"> +<td > <b>Add new:</b> - <select name="type"> +<select name="type"> <option value="httpclient">HTTP proxy</option> <option value="client">Client tunnel</option> <option value="server">Server tunnel</option> - <option value="httpserver">HTTP server tunnel</option> - </select> <input type="submit" value="GO" /> + <option value="httpserver">HTTP server tunnel</option> +</select> <input type="submit" value="Create" /> +</td> </form> - +</tr> +</table> +</td> +</tr> +</table> </body> </html> diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java b/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java index 66841ada901809ba6509bcbf92c0b14af067f2be..4bd776e45f1e80f9d267b38f611f55a0c7430455 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/NewsFetcher.java @@ -11,6 +11,7 @@ import net.i2p.data.DataHelper; import net.i2p.router.RouterContext; import net.i2p.router.RouterVersion; import net.i2p.util.EepGet; +import net.i2p.util.FileUtil; import net.i2p.util.Log; /** @@ -26,6 +27,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener { public static final NewsFetcher getInstance() { return _instance; } private static final String NEWS_FILE = "docs/news.xml"; + private static final String TEMP_NEWS_FILE = "docs/news.xml.temp"; public NewsFetcher(I2PAppContext ctx) { _context = ctx; @@ -82,14 +84,18 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener { boolean shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue(); String proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST); String port = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT, ConfigUpdateHandler.DEFAULT_PROXY_PORT); + File tempFile = new File(TEMP_NEWS_FILE); + if (tempFile.exists()) + tempFile.delete(); + int proxyPort = -1; try { proxyPort = Integer.parseInt(port); EepGet get = null; if (shouldProxy) - get = new EepGet(_context, proxyHost, proxyPort, 10, NEWS_FILE, newsURL); + get = new EepGet(_context, proxyHost, proxyPort, 10, TEMP_NEWS_FILE, newsURL); else - get = new EepGet(_context, 10, NEWS_FILE, newsURL); + get = new EepGet(_context, 10, TEMP_NEWS_FILE, newsURL); get.addStatusListener(this); get.fetch(); } catch (Throwable t) { @@ -230,11 +236,20 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener { public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile) { if (_log.shouldLog(Log.INFO)) _log.info("News fetched from " + url); + + File temp = new File(TEMP_NEWS_FILE); + if (temp.exists()) { + boolean copied = FileUtil.copy(TEMP_NEWS_FILE, NEWS_FILE, true); + if (copied) + temp.delete(); + } checkForUpdates(); } public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) { if (_log.shouldLog(Log.ERROR)) _log.error("Failed to fetch the news from " + url); + File temp = new File(TEMP_NEWS_FILE); + temp.delete(); } } diff --git a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java index b904c3742af20239355714fc797c3d6c1c022798..cbf9fd0bb1f465a37503e3882b50bf4b083064e5 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java @@ -71,7 +71,7 @@ public class Connection { private long _lifetimeDupMessageSent; private long _lifetimeDupMessageReceived; - public static final long MAX_RESEND_DELAY = 30*1000; + public static final long MAX_RESEND_DELAY = 20*1000; public static final long MIN_RESEND_DELAY = 10*1000; /** wait up to 5 minutes after disconnection so we can ack/close packets */ diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java index 1adf4a3ceef9919d9a0fe0260706b833749a9fed..988723730a3fcf4114e391ef1e8592eb3ce9a2cc 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java @@ -82,7 +82,7 @@ public class ConnectionOptions extends I2PSocketOptionsImpl { setConnectDelay(getInt(opts, PROP_CONNECT_DELAY, -1)); setProfile(getInt(opts, PROP_PROFILE, PROFILE_BULK)); setMaxMessageSize(getInt(opts, PROP_MAX_MESSAGE_SIZE, 4*1024)); - setRTT(getInt(opts, PROP_INITIAL_RTT, 30*1000)); + setRTT(getInt(opts, PROP_INITIAL_RTT, 10*1000)); setReceiveWindow(getInt(opts, PROP_INITIAL_RECEIVE_WINDOW, 1)); setResendDelay(getInt(opts, PROP_INITIAL_RESEND_DELAY, 1000)); setSendAckDelay(getInt(opts, PROP_INITIAL_ACK_DELAY, 1000)); @@ -107,7 +107,7 @@ public class ConnectionOptions extends I2PSocketOptionsImpl { if (opts.containsKey(PROP_MAX_MESSAGE_SIZE)) setMaxMessageSize(getInt(opts, PROP_MAX_MESSAGE_SIZE, Packet.MAX_PAYLOAD_SIZE)); if (opts.containsKey(PROP_INITIAL_RTT)) - setRTT(getInt(opts, PROP_INITIAL_RTT, 30*1000)); + setRTT(getInt(opts, PROP_INITIAL_RTT, 10*1000)); if (opts.containsKey(PROP_INITIAL_RECEIVE_WINDOW)) setReceiveWindow(getInt(opts, PROP_INITIAL_RECEIVE_WINDOW, 1)); if (opts.containsKey(PROP_INITIAL_RESEND_DELAY)) diff --git a/core/java/src/net/i2p/CoreVersion.java b/core/java/src/net/i2p/CoreVersion.java index 96ffdd9df11034c22ca886c71c0067628b54d86b..6d1f72c7d6abe8e406ef408085d692df3dc8863d 100644 --- a/core/java/src/net/i2p/CoreVersion.java +++ b/core/java/src/net/i2p/CoreVersion.java @@ -14,8 +14,8 @@ package net.i2p; * */ public class CoreVersion { - public final static String ID = "$Revision: 1.31 $ $Date: 2005/03/18 17:34:53 $"; - public final static String VERSION = "0.5.0.4"; + public final static String ID = "$Revision: 1.32 $ $Date: 2005/03/24 02:29:28 $"; + public final static String VERSION = "0.5.0.5"; public static void main(String args[]) { System.out.println("I2P Core version: " + VERSION); diff --git a/core/java/src/net/i2p/crypto/HMACSHA256Generator.java b/core/java/src/net/i2p/crypto/HMACSHA256Generator.java index 920cce5dd39f3a5c1b84c7550d1fad6379f1ca38..be28c23556778137a28ed4a5bce65044682d5711 100644 --- a/core/java/src/net/i2p/crypto/HMACSHA256Generator.java +++ b/core/java/src/net/i2p/crypto/HMACSHA256Generator.java @@ -1,33 +1,101 @@ package net.i2p.crypto; +import java.util.Arrays; import net.i2p.I2PAppContext; import net.i2p.data.DataHelper; import net.i2p.data.Hash; import net.i2p.data.SessionKey; /** - * Calculate the HMAC-SHA256 of a key+message. Currently FAKE - returns a stupid - * kludgy hash: H(H(key) XOR H(data)). Fix me! + * Calculate the HMAC-SHA256 of a key+message. * */ public class HMACSHA256Generator { - public HMACSHA256Generator(I2PAppContext context) { // nop + private I2PAppContext _context; + public HMACSHA256Generator(I2PAppContext context) { + _context = context; } public static HMACSHA256Generator getInstance() { return I2PAppContext.getGlobalContext().hmac(); } + private static final int PAD_LENGTH = 64; + + private static final byte[] _IPAD = new byte[PAD_LENGTH]; + private static final byte[] _OPAD = new byte[PAD_LENGTH]; + static { + for (int i = 0; i < _IPAD.length; i++) { + _IPAD[i] = 0x36; + _OPAD[i] = 0x5C; + } + } + + + public Buffer createBuffer(int dataLen) { return new Buffer(dataLen); } + + public class Buffer { + private byte padded[]; + private byte innerBuf[]; + private SHA256EntryCache.CacheEntry innerEntry; + private byte rv[]; + private byte outerBuf[]; + private SHA256EntryCache.CacheEntry outerEntry; + + public Buffer(int dataLength) { + padded = new byte[PAD_LENGTH]; + innerBuf = new byte[dataLength + PAD_LENGTH]; + innerEntry = _context.sha().cache().acquire(innerBuf.length); + rv = new byte[Hash.HASH_LENGTH]; + outerBuf = new byte[Hash.HASH_LENGTH + PAD_LENGTH]; + outerEntry = _context.sha().cache().acquire(outerBuf.length); + } + + public void releaseCached() { + _context.sha().cache().release(innerEntry); + _context.sha().cache().release(outerEntry); + } + + public byte[] getHash() { return rv; } + } + /** - * This should calculate the HMAC/SHA256, but it DOESNT. Its just a kludge. - * Fix me. + * Calculate the HMAC of the data with the given key */ public Hash calculate(SessionKey key, byte data[]) { if ((key == null) || (key.getData() == null) || (data == null)) throw new NullPointerException("Null arguments for HMAC"); - - Hash hkey = SHA256Generator.getInstance().calculateHash(key.getData()); - Hash hdata = SHA256Generator.getInstance().calculateHash(data); - return SHA256Generator.getInstance().calculateHash(DataHelper.xor(hkey.getData(), hdata.getData())); + + Buffer buf = new Buffer(data.length); + calculate(key, data, buf); + Hash rv = new Hash(buf.rv); + buf.releaseCached(); + return rv; + } + + /** + * Calculate the HMAC of the data with the given key + */ + public void calculate(SessionKey key, byte data[], Buffer buf) { + // inner hash + padKey(key.getData(), _IPAD, buf.padded); + System.arraycopy(buf.padded, 0, buf.innerBuf, 0, PAD_LENGTH); + System.arraycopy(data, 0, buf.innerBuf, PAD_LENGTH, data.length); + + Hash h = _context.sha().calculateHash(buf.innerBuf, buf.innerEntry); + + // outer hash + padKey(key.getData(), _OPAD, buf.padded); + System.arraycopy(buf.padded, 0, buf.outerBuf, 0, PAD_LENGTH); + System.arraycopy(h.getData(), 0, buf.outerBuf, PAD_LENGTH, Hash.HASH_LENGTH); + + h = _context.sha().calculateHash(buf.outerBuf, buf.outerEntry); + System.arraycopy(h.getData(), 0, buf.rv, 0, Hash.HASH_LENGTH); + } + + private static final void padKey(byte key[], byte pad[], byte out[]) { + for (int i = 0; i < SessionKey.KEYSIZE_BYTES; i++) + out[i] = (byte) (key[i] ^ pad[i]); + Arrays.fill(out, SessionKey.KEYSIZE_BYTES, PAD_LENGTH, pad[0]); } } \ No newline at end of file diff --git a/core/java/src/net/i2p/data/Base64.java b/core/java/src/net/i2p/data/Base64.java index 85bbd2f282988cf915d61b14397f60ed58baa9bd..554d5ab926fd4e0c5edcef8252186bdab7c4f80c 100644 --- a/core/java/src/net/i2p/data/Base64.java +++ b/core/java/src/net/i2p/data/Base64.java @@ -329,6 +329,8 @@ public class Base64 { * replacing / with ~, and + with - */ private static String safeEncode(byte[] source, int off, int len, boolean useStandardAlphabet) { + if (len + off > source.length) + throw new ArrayIndexOutOfBoundsException("Trying to encode too much! source.len=" + source.length + " off=" + off + " len=" + len); String encoded = encodeBytes(source, off, len, false); if (useStandardAlphabet) { // noop diff --git a/core/java/src/net/i2p/data/LeaseSet.java b/core/java/src/net/i2p/data/LeaseSet.java index 214b7df7ed45b285f6fc05feaff5d3d28e6c4ee9..f2f54aab8511d3ae4ca5f75d0843e0a2d539b185 100644 --- a/core/java/src/net/i2p/data/LeaseSet.java +++ b/core/java/src/net/i2p/data/LeaseSet.java @@ -35,6 +35,7 @@ public class LeaseSet extends DataStructureImpl { private Signature _signature; private volatile Hash _currentRoutingKey; private volatile byte[] _routingKeyGenMod; + private boolean _receivedAsPublished; /** um, no lease can last more than a year. */ private final static long MAX_FUTURE_EXPIRATION = 365 * 24 * 60 * 60 * 1000L; @@ -47,6 +48,7 @@ public class LeaseSet extends DataStructureImpl { setRoutingKey(null); _leases = new ArrayList(); _routingKeyGenMod = null; + _receivedAsPublished = false; } public Destination getDestination() { @@ -72,6 +74,14 @@ public class LeaseSet extends DataStructureImpl { public void setSigningKey(SigningPublicKey key) { _signingKey = key; } + + /** + * If true, we received this LeaseSet by a remote peer publishing it to + * us, rather than by searching for it ourselves or locally creating it. + * + */ + public boolean getReceivedAsPublished() { return _receivedAsPublished; } + public void setReceivedAsPublished(boolean received) { _receivedAsPublished = received; } public void addLease(Lease lease) { if (lease == null) throw new IllegalArgumentException("erm, null lease"); diff --git a/core/java/test/net/i2p/crypto/HMACSHA256Bench.java b/core/java/test/net/i2p/crypto/HMACSHA256Bench.java new file mode 100644 index 0000000000000000000000000000000000000000..715c45ec0bd772c73ba1de48369886db469a3092 --- /dev/null +++ b/core/java/test/net/i2p/crypto/HMACSHA256Bench.java @@ -0,0 +1,112 @@ +package net.i2p.crypto; + +/* + * Copyright (c) 2003, TheCrypto + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the TheCrypto may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +import net.i2p.I2PAppContext; +import net.i2p.data.Hash; +import net.i2p.data.SessionKey; + +public class HMACSHA256Bench { + public static void main(String args[]) { + I2PAppContext ctx = I2PAppContext.getGlobalContext(); + SessionKey key = ctx.keyGenerator().generateSessionKey(); + Hash asdfs = HMACSHA256Generator.getInstance().calculate(key, "qwerty".getBytes()); + + int times = 100000; + long shorttime = 0; + long medtime = 0; + long longtime = 0; + long minShort = 0; + long maxShort = 0; + long minMed = 0; + long maxMed = 0; + long minLong = 0; + long maxLong = 0; + + long shorttime1 = 0; + long medtime1 = 0; + long longtime1 = 0; + long minShort1 = 0; + long maxShort1 = 0; + long minMed1 = 0; + long maxMed1 = 0; + long minLong1 = 0; + long maxLong1 = 0; + + byte[] smess = new String("abc").getBytes(); + StringBuffer buf = new StringBuffer(); + for (int x = 0; x < 2*1024; x++) { + buf.append("a"); + } + byte[] mmess = buf.toString().getBytes(); // new String("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq").getBytes(); + buf = new StringBuffer(); + for (int x = 0; x < 10000; x++) { + buf.append("a"); + } + byte[] lmess = buf.toString().getBytes(); + + HMACSHA256Generator.Buffer sbuf = ctx.hmac().createBuffer(smess.length); + HMACSHA256Generator.Buffer mbuf = ctx.hmac().createBuffer(mmess.length); + HMACSHA256Generator.Buffer lbuf = ctx.hmac().createBuffer(lmess.length); + + // warm up the engines + ctx.hmac().calculate(key, smess, sbuf); + ctx.hmac().calculate(key, mmess, mbuf); + ctx.hmac().calculate(key, lmess, lbuf); + + long before = System.currentTimeMillis(); + for (int x = 0; x < times; x++) + ctx.hmac().calculate(key, smess, sbuf); + long after = System.currentTimeMillis(); + + display(times, before, after, smess.length, "3 byte"); + + before = System.currentTimeMillis(); + for (int x = 0; x < times; x++) + ctx.hmac().calculate(key, mmess, mbuf); + after = System.currentTimeMillis(); + + display(times, before, after, mmess.length, "2KB"); + + before = System.currentTimeMillis(); + for (int x = 0; x < times; x++) + ctx.hmac().calculate(key, lmess, lbuf); + after = System.currentTimeMillis(); + + display(times, before, after, lmess.length, "10KB"); + } + + private static void display(int times, long before, long after, int len, String name) { + double rate = ((double)times)/(((double)after-(double)before)/1000.0d); + double kbps = ((double)len/1024.0d)*((double)times)/(((double)after-(double)before)/1000.0d); + System.out.println(name + " HMAC-SHA256 pulled " + kbps + "KBps or " + rate + " calcs per second"); + } +} + diff --git a/history.txt b/history.txt index 5af5f9ff1cba572f2d11a3c80bb8d092cf7cfaff..b7f507747b0e312adad7346a3286af39cbcf6bc5 100644 --- a/history.txt +++ b/history.txt @@ -1,4 +1,25 @@ -$Id: history.txt,v 1.181 2005/03/24 23:07:06 jrandom Exp $ +$Id: history.txt,v 1.182 2005/03/26 02:13:38 jrandom Exp $ + +* 2005-03-29 0.5.0.5 released + +2005-03-29 jrandom + * Decreased the initial RTT estimate to 10s to allow more retries. + * Increased the default netDb store replication factor from 2 to 6 to take + into consideration tunnel failures. + * Address some statistical anonymity attacks against the netDb that could + be mounted by an active internal adversary by only answering lookups for + leaseSets we received through an unsolicited store. + * Don't throttle lookup responses (we throttle enough elsewhere) + * Fix the NewsFetcher so that it doesn't incorrectly resume midway through + the file (thanks nickster!) + * Updated the I2PTunnel HTML (thanks postman!) + * Added support to the I2PTunnel pages for the URL parameter "passphrase", + which, if matched against the router.config "i2ptunnel.passphrase" value, + skips the nonce check. If the config prop doesn't exist or is blank, no + passphrase is accepted. + * Implemented HMAC-SHA256. + * Enable the tunnel batching with a 500ms delay by default + * Dropped compatability with 0.5.0.3 and earlier releases 2005-03-26 jrandom * Added some error handling and fairly safe to cache data to the streaming diff --git a/installer/install.xml b/installer/install.xml index b515ebcec0b03a21a92e61af49f3bb9563247140..d8306c253d58d20308c5d44858120df8c52ee178 100644 --- a/installer/install.xml +++ b/installer/install.xml @@ -4,7 +4,7 @@ <info> <appname>i2p</appname> - <appversion>0.5.0.4</appversion> + <appversion>0.5.0.5</appversion> <authors> <author name="I2P" email="support@i2p.net"/> </authors> diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 2a93e5091cb5b6fec3bf5ac2be4e42118c98f84c..d8a40488c0bf84644d3b24808d67194630c698c4 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.174 $ $Date: 2005/03/24 23:07:06 $"; - public final static String VERSION = "0.5.0.4"; - public final static long BUILD = 2; + public final static String ID = "$Revision: 1.175 $ $Date: 2005/03/26 02:13:38 $"; + public final static String VERSION = "0.5.0.5"; + public final static long BUILD = 0; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION); System.out.println("Router ID: " + RouterVersion.ID); diff --git a/router/java/src/net/i2p/router/StatisticsManager.java b/router/java/src/net/i2p/router/StatisticsManager.java index 10152286c9ba68faf5bb8f08b6fb7e39b5d5f2f8..e3983f655f494de880f6039e0fdfb09945d40ece 100644 --- a/router/java/src/net/i2p/router/StatisticsManager.java +++ b/router/java/src/net/i2p/router/StatisticsManager.java @@ -113,8 +113,9 @@ public class StatisticsManager implements Service { includeRate("tunnel.buildFailure", stats, new long[] { 60*60*1000 }); includeRate("tunnel.buildSuccess", stats, new long[] { 60*60*1000 }); - includeRate("tunnel.batchDelaySent", stats, new long[] { 10*60*1000, 60*60*1000 }); + //includeRate("tunnel.batchDelaySent", stats, new long[] { 10*60*1000, 60*60*1000 }); includeRate("tunnel.batchMultipleCount", stats, new long[] { 10*60*1000, 60*60*1000 }); + includeRate("tunnel.corruptMessage", stats, new long[] { 60*60*1000l, 3*60*60*1000l }); includeRate("router.throttleTunnelProbTestSlow", stats, new long[] { 60*60*1000 }); includeRate("router.throttleTunnelProbTooFast", stats, new long[] { 60*60*1000 }); diff --git a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java index b10f06cf8935d00cc2ef6c075f521107733876b7..73af5c9e361ca9d2d93e7e2475f260f367d1e1de 100644 --- a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java +++ b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java @@ -418,7 +418,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { long sendTime = getContext().clock().now() - _start; if (_log.shouldLog(Log.WARN)) _log.warn(getJobId() + ": Failed to send the message " + _clientMessageId + " after " - + sendTime + "ms", new Exception("Message send failure")); + + sendTime + "ms"); long messageDelay = getContext().throttle().getMessageDelay(); long tunnelLag = getContext().throttle().getTunnelLag(); diff --git a/router/java/src/net/i2p/router/networkdb/DatabaseLookupMessageHandler.java b/router/java/src/net/i2p/router/networkdb/DatabaseLookupMessageHandler.java index 29ee4f3fbd1c2da366263cede98d6e0902495b02..db696461a8f7669e0b5b3329b915592c4887a6e4 100644 --- a/router/java/src/net/i2p/router/networkdb/DatabaseLookupMessageHandler.java +++ b/router/java/src/net/i2p/router/networkdb/DatabaseLookupMessageHandler.java @@ -34,7 +34,7 @@ public class DatabaseLookupMessageHandler implements HandlerJobBuilder { public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) { _context.statManager().addRateData("netDb.lookupsReceived", 1, 0); - if (_context.throttle().acceptNetDbLookupRequest(((DatabaseLookupMessage)receivedMessage).getSearchKey())) { + if (true || _context.throttle().acceptNetDbLookupRequest(((DatabaseLookupMessage)receivedMessage).getSearchKey())) { return new HandleDatabaseLookupMessageJob(_context, (DatabaseLookupMessage)receivedMessage, from, fromHash); } else { if (_log.shouldLog(Log.INFO)) diff --git a/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java b/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java index 9e69e7254e52cadcca019fc6f73adf1382849e83..7de9ec65966e51f5ce5b56b23c5420de62ef7741 100644 --- a/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java +++ b/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java @@ -40,6 +40,7 @@ public class HandleDatabaseLookupMessageJob extends JobImpl { private RouterIdentity _from; private Hash _fromHash; private final static int MAX_ROUTERS_RETURNED = 3; + private final static int CLOSENESS_THRESHOLD = 10; // StoreJob.REDUNDANCY * 2 private final static int REPLY_TIMEOUT = 60*1000; private final static int MESSAGE_PRIORITY = 300; @@ -48,6 +49,9 @@ public class HandleDatabaseLookupMessageJob extends JobImpl { _log = getContext().logManager().getLog(HandleDatabaseLookupMessageJob.class); getContext().statManager().createRateStat("netDb.lookupsHandled", "How many netDb lookups have we handled?", "NetworkDatabase", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); getContext().statManager().createRateStat("netDb.lookupsMatched", "How many netDb lookups did we have the data for?", "NetworkDatabase", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + getContext().statManager().createRateStat("netDb.lookupsMatchedReceivedPublished", "How many netDb lookups did we have the data for that were published to us?", "NetworkDatabase", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + getContext().statManager().createRateStat("netDb.lookupsMatchedLocalClosest", "How many netDb lookups for local data were received where we are the closest peers?", "NetworkDatabase", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + getContext().statManager().createRateStat("netDb.lookupsMatchedLocalNotClosest", "How many netDb lookups for local data were received where we are NOT the closest peers?", "NetworkDatabase", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); _message = receivedMessage; _from = from; _fromHash = fromHash; @@ -65,26 +69,26 @@ public class HandleDatabaseLookupMessageJob extends JobImpl { + " (tunnel " + _message.getReplyTunnel() + ")"); } - if (getContext().netDb().lookupRouterInfoLocally(_message.getFrom()) == null) { - // hmm, perhaps don't always send a lookup for this... - // but for now, wtf, why not. we may even want to adjust it so that - // we penalize or benefit peers who send us that which we can or - // cannot lookup - getContext().netDb().lookupRouterInfo(_message.getFrom(), null, null, REPLY_TIMEOUT); - } - - // whatdotheywant? - handleRequest(fromKey); - } - - private void handleRequest(Hash fromKey) { LeaseSet ls = getContext().netDb().lookupLeaseSetLocally(_message.getSearchKey()); if (ls != null) { - // send that lease set to the _message.getFromHash peer - if (_log.shouldLog(Log.DEBUG)) - _log.debug("We do have key " + _message.getSearchKey().toBase64() - + " locally as a lease set. sending to " + fromKey.toBase64()); - sendData(_message.getSearchKey(), ls, fromKey, _message.getReplyTunnel()); + // only answer a request for a LeaseSet if it has been published + // to us, or, if its local, if we would have published to ourselves + if (ls.getReceivedAsPublished()) { + getContext().statManager().addRateData("netDb.lookupsMatchedReceivedPublished", 1, 0); + sendData(_message.getSearchKey(), ls, fromKey, _message.getReplyTunnel()); + } else { + Set routerInfoSet = getContext().netDb().findNearestRouters(_message.getSearchKey(), + CLOSENESS_THRESHOLD, + _message.getDontIncludePeers()); + if (getContext().clientManager().isLocal(ls.getDestination()) && + weAreClosest(routerInfoSet)) { + getContext().statManager().addRateData("netDb.lookupsMatchedLocalClosest", 1, 0); + sendData(_message.getSearchKey(), ls, fromKey, _message.getReplyTunnel()); + } else { + getContext().statManager().addRateData("netDb.lookupsMatchedLocalNotClosest", 1, 0); + sendClosest(_message.getSearchKey(), routerInfoSet, fromKey, _message.getReplyTunnel()); + } + } } else { RouterInfo info = getContext().netDb().lookupRouterInfoLocally(_message.getSearchKey()); if (info != null) { @@ -106,6 +110,17 @@ public class HandleDatabaseLookupMessageJob extends JobImpl { } } + private boolean weAreClosest(Set routerInfoSet) { + boolean weAreClosest = false; + for (Iterator iter = routerInfoSet.iterator(); iter.hasNext(); ) { + RouterInfo cur = (RouterInfo)iter.next(); + if (cur.getIdentity().calculateHash().equals(getContext().routerHash())) { + return true; + } + } + return false; + } + private void sendData(Hash key, DataStructure data, Hash toPeer, TunnelId replyTunnel) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Sending data matching key key " + key.toBase64() + " to peer " + toPeer.toBase64() @@ -129,27 +144,27 @@ public class HandleDatabaseLookupMessageJob extends JobImpl { _log.debug("Sending closest routers to key " + key.toBase64() + ": # peers = " + routerInfoSet.size() + " tunnel " + replyTunnel); DatabaseSearchReplyMessage msg = new DatabaseSearchReplyMessage(getContext()); - msg.setFromHash(getContext().router().getRouterInfo().getIdentity().getHash()); + msg.setFromHash(getContext().routerHash()); msg.setSearchKey(key); for (Iterator iter = routerInfoSet.iterator(); iter.hasNext(); ) { RouterInfo peer = (RouterInfo)iter.next(); msg.addReply(peer.getIdentity().getHash()); + if (msg.getNumReplies() >= MAX_ROUTERS_RETURNED) + break; } getContext().statManager().addRateData("netDb.lookupsHandled", 1, 0); sendMessage(msg, toPeer, replyTunnel); // should this go via garlic messages instead? } private void sendMessage(I2NPMessage message, Hash toPeer, TunnelId replyTunnel) { - Job send = null; if (replyTunnel != null) { sendThroughTunnel(message, toPeer, replyTunnel); } else { if (_log.shouldLog(Log.DEBUG)) _log.debug("Sending reply directly to " + toPeer); - send = new SendMessageDirectJob(getContext(), message, toPeer, REPLY_TIMEOUT, MESSAGE_PRIORITY); + Job send = new SendMessageDirectJob(getContext(), message, toPeer, REPLY_TIMEOUT, MESSAGE_PRIORITY); + getContext().netDb().lookupRouterInfo(toPeer, send, null, REPLY_TIMEOUT); } - - getContext().netDb().lookupRouterInfo(toPeer, send, null, REPLY_TIMEOUT); } private void sendThroughTunnel(I2NPMessage message, Hash toPeer, TunnelId replyTunnel) { @@ -171,28 +186,6 @@ public class HandleDatabaseLookupMessageJob extends JobImpl { } } - private void sendToGateway(I2NPMessage message, Hash toPeer, TunnelId replyTunnel, TunnelInfo info) { - if (_log.shouldLog(Log.INFO)) - _log.info("Want to reply to a db request via a tunnel, but we're a participant in the reply! so send it to the gateway"); - - if ( (toPeer == null) || (replyTunnel == null) ) { - if (_log.shouldLog(Log.ERROR)) - _log.error("Someone br0ke us. where is this message supposed to go again?", getAddedBy()); - return; - } - - long expiration = REPLY_TIMEOUT + getContext().clock().now(); - - TunnelGatewayMessage msg = new TunnelGatewayMessage(getContext()); - msg.setMessage(message); - msg.setTunnelId(replyTunnel); - msg.setMessageExpiration(expiration); - getContext().jobQueue().addJob(new SendMessageDirectJob(getContext(), msg, toPeer, null, null, null, null, REPLY_TIMEOUT, MESSAGE_PRIORITY)); - - String bodyType = message.getClass().getName(); - getContext().messageHistory().wrap(bodyType, message.getUniqueId(), TunnelGatewayMessage.class.getName(), msg.getUniqueId()); - } - public String getName() { return "Handle Database Lookup Message"; } public void dropped() { diff --git a/router/java/src/net/i2p/router/networkdb/HandleDatabaseStoreMessageJob.java b/router/java/src/net/i2p/router/networkdb/HandleDatabaseStoreMessageJob.java index 080df02424f9a72995c61e31ad8037a5565d48dc..2e4073fc4e668a31dda8032c79425a7608ae8273 100644 --- a/router/java/src/net/i2p/router/networkdb/HandleDatabaseStoreMessageJob.java +++ b/router/java/src/net/i2p/router/networkdb/HandleDatabaseStoreMessageJob.java @@ -10,6 +10,7 @@ package net.i2p.router.networkdb; import java.util.Date; import net.i2p.data.Hash; +import net.i2p.data.LeaseSet; import net.i2p.data.RouterIdentity; import net.i2p.data.i2np.DatabaseStoreMessage; import net.i2p.data.i2np.DeliveryStatusMessage; @@ -48,8 +49,18 @@ public class HandleDatabaseStoreMessageJob extends JobImpl { boolean wasNew = false; if (_message.getValueType() == DatabaseStoreMessage.KEY_TYPE_LEASESET) { try { - Object match = getContext().netDb().store(_message.getKey(), _message.getLeaseSet()); - wasNew = (null == match); + LeaseSet ls = _message.getLeaseSet(); + // mark it as something we received, so we'll answer queries + // for it. this flag does NOT get set on entries that we + // receive in response to our own lookups. + ls.setReceivedAsPublished(true); + LeaseSet match = getContext().netDb().store(_message.getKey(), _message.getLeaseSet()); + if (match == null) { + wasNew = true; + } else { + wasNew = false; + match.setReceivedAsPublished(true); + } } catch (IllegalArgumentException iae) { invalidMessage = iae.getMessage(); } diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java index 0f71b172aef46c21ddf64d593585b9d993c92b1f..a8359e09faf3a53fb5fa9c7319f2d46ee58b1568 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java @@ -36,8 +36,8 @@ class StoreJob extends JobImpl { private long _expiration; private PeerSelector _peerSelector; - private final static int PARALLELIZATION = 1; // how many sent at a time - private final static int REDUNDANCY = 2; // we want the data sent to 2 peers + private final static int PARALLELIZATION = 2; // how many sent at a time + private final static int REDUNDANCY = 6; // we want the data sent to 6 peers /** * additionally send to 1 outlier(s), in case all of the routers chosen in our * REDUNDANCY set are attacking us by accepting DbStore messages but dropping diff --git a/router/java/src/net/i2p/router/transport/tcp/TCPTransport.java b/router/java/src/net/i2p/router/transport/tcp/TCPTransport.java index f324919602f3b6074a49c3ebfc45a2771c46d067..70130f32c904faebe4aae7cd413e63dc487b3448 100644 --- a/router/java/src/net/i2p/router/transport/tcp/TCPTransport.java +++ b/router/java/src/net/i2p/router/transport/tcp/TCPTransport.java @@ -87,8 +87,7 @@ public class TCPTransport extends TransportImpl { public static final int DEFAULT_ESTABLISHERS = 3; /** Ordered list of supported I2NP protocols */ - public static final int[] SUPPORTED_PROTOCOLS = new int[] { 3 - , 4}; // forward compat, to drop <= 0.5.0.3 + public static final int[] SUPPORTED_PROTOCOLS = new int[] { 4 }; // drop <= 0.5.0.3 /** blah, people shouldnt use defaults... */ public static final int DEFAULT_LISTEN_PORT = 8887; diff --git a/router/java/src/net/i2p/router/tunnel/BatchedPreprocessor.java b/router/java/src/net/i2p/router/tunnel/BatchedPreprocessor.java index 4231197c53d70e20a97e315bf9b3e9c3df207f40..b213f5738450d9270374dae3707aaa0e6656d749 100644 --- a/router/java/src/net/i2p/router/tunnel/BatchedPreprocessor.java +++ b/router/java/src/net/i2p/router/tunnel/BatchedPreprocessor.java @@ -34,16 +34,18 @@ public class BatchedPreprocessor extends TrivialPreprocessor { - 1 // 0x00 ending the padding - 4; // 4 byte checksum + private static final boolean DISABLE_BATCHING = false; + /* not final or private so the test code can adjust */ static long DEFAULT_DELAY = 500; /** wait up to 2 seconds before sending a small message */ protected long getSendDelay() { return DEFAULT_DELAY; } public boolean preprocessQueue(List pending, TunnelGateway.Sender sender, TunnelGateway.Receiver rec) { - if (_log.shouldLog(Log.INFO)) - _log.info("Preprocess queue with " + pending.size() + " to send"); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Preprocess queue with " + pending.size() + " to send"); - if (getSendDelay() <= 0) { + if (DISABLE_BATCHING || getSendDelay() <= 0) { if (_log.shouldLog(Log.INFO)) _log.info("No batching, send all messages immediately"); while (pending.size() > 0) { @@ -131,8 +133,8 @@ public class BatchedPreprocessor extends TrivialPreprocessor { } } - if (_log.shouldLog(Log.INFO)) - _log.info("Sent everything on the list (pending=" + pending.size() + ")"); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Sent everything on the list (pending=" + pending.size() + ")"); // sent everything from the pending list, no need to delayed flush return false; @@ -150,9 +152,6 @@ public class BatchedPreprocessor extends TrivialPreprocessor { if (_log.shouldLog(Log.DEBUG)) _log.debug("Sending " + startAt + ":" + sendThrough + " out of " + pending.size()); byte preprocessed[] = _dataCache.acquire().getData(); - ByteArray ivBuf = _ivCache.acquire(); - byte iv[] = ivBuf.getData(); // new byte[IV_SIZE]; - _context.random().nextBytes(iv); int offset = 0; offset = writeFragments(pending, startAt, sendThrough, preprocessed, offset); @@ -160,6 +159,17 @@ public class BatchedPreprocessor extends TrivialPreprocessor { // so we need to format, pad, and rearrange according to the spec to // generate the final preprocessed data + if (offset <= 0) { + StringBuffer buf = new StringBuffer(128); + buf.append("wtf, written offset is ").append(offset); + buf.append(" for ").append(startAt).append(" through ").append(sendThrough); + for (int i = startAt; i <= sendThrough; i++) { + buf.append(" ").append(pending.get(i).toString()); + } + _log.log(Log.CRIT, buf.toString()); + return; + } + preprocess(preprocessed, offset); sender.sendPreprocessed(preprocessed, rec); diff --git a/router/java/src/net/i2p/router/tunnel/BatchedRouterPreprocessor.java b/router/java/src/net/i2p/router/tunnel/BatchedRouterPreprocessor.java index b8f1795161f455dbac5d6ea30945e6231e3b4a34..7c8552e9d94181746455718ad35117d01ba5c045 100644 --- a/router/java/src/net/i2p/router/tunnel/BatchedRouterPreprocessor.java +++ b/router/java/src/net/i2p/router/tunnel/BatchedRouterPreprocessor.java @@ -17,7 +17,7 @@ public class BatchedRouterPreprocessor extends BatchedPreprocessor { */ public static final String PROP_BATCH_FREQUENCY = "batchFrequency"; public static final String PROP_ROUTER_BATCH_FREQUENCY = "router.batchFrequency"; - public static final int DEFAULT_BATCH_FREQUENCY = 0; + public static final int DEFAULT_BATCH_FREQUENCY = 500; public BatchedRouterPreprocessor(RouterContext ctx) { this(ctx, null); diff --git a/router/java/src/net/i2p/router/tunnel/FragmentHandler.java b/router/java/src/net/i2p/router/tunnel/FragmentHandler.java index b6c71fe978d6f853620f5261dbe589b5a4cac4ab..ae95d0330d61f0a6e96b316a32f97eab0c084290 100644 --- a/router/java/src/net/i2p/router/tunnel/FragmentHandler.java +++ b/router/java/src/net/i2p/router/tunnel/FragmentHandler.java @@ -49,6 +49,8 @@ public class FragmentHandler { "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 3*60*60*1000l, 24*60*60*1000 }); _context.statManager().createRateStat("tunnel.fragmentedDropped", "How many fragments were in a partially received yet failed message?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 3*60*60*1000l, 24*60*60*1000 }); + _context.statManager().createRateStat("tunnel.corruptMessage", "How many corrupted messages arrived?", + "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 3*60*60*1000l, 24*60*60*1000 }); } /** @@ -61,9 +63,11 @@ public class FragmentHandler { public void receiveTunnelMessage(byte preprocessed[], int offset, int length) { boolean ok = verifyPreprocessed(preprocessed, offset, length); if (!ok) { - _log.error("Unable to verify preprocessed data (pre.length=" + preprocessed.length - + " off=" +offset + " len=" + length, new Exception("failed")); + if (_log.shouldLog(Log.WARN)) + _log.warn("Unable to verify preprocessed data (pre.length=" + + preprocessed.length + " off=" +offset + " len=" + length); _cache.release(new ByteArray(preprocessed)); + _context.statManager().addRateData("tunnel.corruptMessage", 1, 1); return; } offset += HopProcessor.IV_LENGTH; // skip the IV @@ -84,6 +88,7 @@ public class FragmentHandler { } catch (RuntimeException e) { if (_log.shouldLog(Log.ERROR)) _log.error("Corrupt fragment received: offset = " + offset, e); + _context.statManager().addRateData("tunnel.corruptMessage", 1, 1); throw e; } finally { // each of the FragmentedMessages populated make a copy out of the @@ -110,8 +115,19 @@ public class FragmentHandler { private boolean verifyPreprocessed(byte preprocessed[], int offset, int length) { // now we need to verify that the message was received correctly int paddingEnd = HopProcessor.IV_LENGTH + 4; - while (preprocessed[offset+paddingEnd] != (byte)0x00) + while (preprocessed[offset+paddingEnd] != (byte)0x00) { paddingEnd++; + if (offset+paddingEnd >= length) { + if (_log.shouldLog(Log.ERROR)) + _log.error("Corrupt tunnel message padding"); + if (_log.shouldLog(Log.WARN)) + _log.warn("cannot verify, going past the end [off=" + + offset + " len=" + length + " paddingEnd=" + + paddingEnd + " data:\n" + + Base64.encode(preprocessed, offset, length)); + return false; + } + } paddingEnd++; // skip the last ByteArray ba = _validateCache.acquire(); // larger than necessary, but always sufficient @@ -129,10 +145,11 @@ public class FragmentHandler { boolean eq = DataHelper.eq(v.getData(), 0, preprocessed, offset + HopProcessor.IV_LENGTH, 4); if (!eq) { if (_log.shouldLog(Log.ERROR)) - _log.error("Endpoint data doesn't match: # pad bytes: " + (paddingEnd-(HopProcessor.IV_LENGTH+4)-1)); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("nomatching endpoint: # pad bytes: " + (paddingEnd-(HopProcessor.IV_LENGTH+4)-1) + "\n" - + Base64.encode(preprocessed, offset + paddingEnd, preV.length-HopProcessor.IV_LENGTH)); + _log.error("Corrupt tunnel message - verification fails"); + if (_log.shouldLog(Log.WARN)) + _log.warn("nomatching endpoint: # pad bytes: " + (paddingEnd-(HopProcessor.IV_LENGTH+4)-1) + "\n" + + " offset=" + offset + " length=" + length + " paddingEnd=" + paddingEnd + + Base64.encode(preprocessed, offset, length)); } _context.sha().cache().release(cache); @@ -380,8 +397,8 @@ public class FragmentHandler { if (removed && !_msg.getReleased()) { _failed++; noteFailure(_msg.getMessageId()); - if (_log.shouldLog(Log.ERROR)) - _log.error("Dropped failed fragmented message: " + _msg); + if (_log.shouldLog(Log.WARN)) + _log.warn("Dropped failed fragmented message: " + _msg); _context.statManager().addRateData("tunnel.fragmentedDropped", _msg.getFragmentCount(), _msg.getLifetime()); _msg.failed(); } else { diff --git a/router/java/src/net/i2p/router/tunnel/TunnelGateway.java b/router/java/src/net/i2p/router/tunnel/TunnelGateway.java index 7ec95790bdc563596951f17df81f677b6f4f9864..bb3d5b6968b8c44a3e8937051b67de0bd9b9caf6 100644 --- a/router/java/src/net/i2p/router/tunnel/TunnelGateway.java +++ b/router/java/src/net/i2p/router/tunnel/TunnelGateway.java @@ -8,6 +8,7 @@ import net.i2p.data.Hash; import net.i2p.data.TunnelId; import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.TunnelGatewayMessage; +import net.i2p.router.Router; import net.i2p.util.Log; import net.i2p.util.SimpleTimer; @@ -87,7 +88,7 @@ public class TunnelGateway { _messagesSent++; boolean delayedFlush = false; - Pending cur = new Pending(msg, toRouter, toTunnel); + Pending cur = new PendingImpl(msg, toRouter, toTunnel); synchronized (_queue) { _queue.add(cur); delayedFlush = _preprocessor.preprocessQueue(_queue, _sender, _receiver); @@ -96,9 +97,9 @@ public class TunnelGateway { // expire any as necessary, even if its framented for (int i = 0; i < _queue.size(); i++) { Pending m = (Pending)_queue.get(i); - if (m.getExpiration() < _lastFlush) { + if (m.getExpiration() + Router.CLOCK_FUDGE_FACTOR < _lastFlush) { if (_log.shouldLog(Log.ERROR)) - _log.error("Expire on the queue: " + m); + _log.error("Expire on the queue (size=" + _queue.size() + "): " + m); _queue.remove(i); i--; } @@ -140,13 +141,13 @@ public class TunnelGateway { } public static class Pending { - private Hash _toRouter; - private TunnelId _toTunnel; + protected Hash _toRouter; + protected TunnelId _toTunnel; private long _messageId; - private long _expiration; - private byte _remaining[]; - private int _offset; - private int _fragmentNumber; + protected long _expiration; + protected byte _remaining[]; + protected int _offset; + protected int _fragmentNumber; public Pending(I2NPMessage message, Hash toRouter, TunnelId toTunnel) { _toRouter = toRouter; @@ -174,6 +175,35 @@ public class TunnelGateway { /** ok, fragment sent, increment what the next will be */ public void incrementFragmentNumber() { _fragmentNumber++; } } + private class PendingImpl extends Pending { + private long _created; + + public PendingImpl(I2NPMessage message, Hash toRouter, TunnelId toTunnel) { + super(message, toRouter, toTunnel); + _created = _context.clock().now(); + } + + public String toString() { + StringBuffer buf = new StringBuffer(64); + buf.append("Message on "); + buf.append(TunnelGateway.this.toString()); + if (_toRouter != null) { + buf.append(" targetting "); + buf.append(_toRouter.toBase64()).append(" "); + if (_toTunnel != null) + buf.append(_toTunnel.getTunnelId()); + } + long now = _context.clock().now(); + buf.append(" actual lifetime "); + buf.append(now - _created).append("ms"); + buf.append(" potential lifetime "); + buf.append(_expiration - _created).append("ms"); + buf.append(" size ").append(_remaining.length); + buf.append(" offset ").append(_offset); + buf.append(" frag ").append(_fragmentNumber); + return buf.toString(); + } + } private class DelayedFlush implements SimpleTimer.TimedEvent { public void timeReached() { diff --git a/router/java/src/net/i2p/router/tunnel/pool/RequestTunnelJob.java b/router/java/src/net/i2p/router/tunnel/pool/RequestTunnelJob.java index 5275dedd3e3e02ac74e06fcf0303c2a2cf2a0708..ab6dd977e55dfc29d8c485bb35b7b561c889d981 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/RequestTunnelJob.java +++ b/router/java/src/net/i2p/router/tunnel/pool/RequestTunnelJob.java @@ -45,7 +45,7 @@ public class RequestTunnelJob extends JobImpl { private boolean _isFake; private boolean _isExploratory; - static final int HOP_REQUEST_TIMEOUT = 30*1000; + static final int HOP_REQUEST_TIMEOUT = 20*1000; private static final int LOOKUP_TIMEOUT = 10*1000; public RequestTunnelJob(RouterContext ctx, TunnelCreatorConfig cfg, Job onCreated, Job onFailed, int hop, boolean isFake, boolean isExploratory) { diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java index afa92ac6e16d31b9413d913db471f5844b5fc2d0..8a3b53d26e673a9ba26f1eea3f9a299700919af0 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java @@ -37,11 +37,11 @@ public class TunnelPool { private long _lastSelectionPeriod; /** - * Only 3 builds per minute per pool, even if we have failing tunnels, + * Only 5 builds per minute per pool, even if we have failing tunnels, * etc. On overflow, the necessary additional tunnels are built by the * RefreshJob */ - private static final int MAX_BUILDS_PER_MINUTE = 3; + private static final int MAX_BUILDS_PER_MINUTE = 5; public TunnelPool(RouterContext ctx, TunnelPoolManager mgr, TunnelPoolSettings settings, TunnelPeerSelector sel, TunnelBuilder builder) { _context = ctx;