From f3143d8b3d65e7add8dcf9ac5182652182fde878 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 18 Feb 2009 20:54:55 +0000 Subject: [PATCH 001/688] case insensitive sort on stat groups --- core/java/src/net/i2p/stat/StatManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/java/src/net/i2p/stat/StatManager.java b/core/java/src/net/i2p/stat/StatManager.java index 4c5c69c79..56af55f71 100644 --- a/core/java/src/net/i2p/stat/StatManager.java +++ b/core/java/src/net/i2p/stat/StatManager.java @@ -1,5 +1,6 @@ package net.i2p.stat; +import java.text.Collator; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -178,7 +179,7 @@ public class StatManager { /** Group name (String) to a Set of stat names, ordered alphabetically */ public Map getStatsByGroup() { - Map groups = new TreeMap(); + Map groups = new TreeMap(Collator.getInstance()); for (Iterator iter = _frequencyStats.values().iterator(); iter.hasNext();) { FrequencyStat stat = (FrequencyStat) iter.next(); if (!groups.containsKey(stat.getGroupName())) groups.put(stat.getGroupName(), new TreeSet()); From fbe7e42f46b08fc1d4d8cf15676d0779c986e5d3 Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 20 Feb 2009 17:53:17 +0000 Subject: [PATCH 002/688] fixed a NPE --- core/java/src/net/i2p/data/PrivateKeyFile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/java/src/net/i2p/data/PrivateKeyFile.java b/core/java/src/net/i2p/data/PrivateKeyFile.java index d9e52aecc..b5d68ee41 100644 --- a/core/java/src/net/i2p/data/PrivateKeyFile.java +++ b/core/java/src/net/i2p/data/PrivateKeyFile.java @@ -261,7 +261,7 @@ public class PrivateKeyFile { public String toString() { StringBuffer s = new StringBuffer(128); s.append("Dest: "); - s.append(this.dest.toBase64()); + s.append(this.dest != null ? this.dest.toBase64() : "null"); s.append("\nContains: "); s.append(this.dest); s.append("\nPrivate Key: "); From 78075cb3aaf08ad6ef5c9e7ad0796944cc26e7dd Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 20 Feb 2009 18:51:39 +0000 Subject: [PATCH 003/688] Add upnp html output, fix up logging --- .../transport/FIFOBandwidthLimiter.java | 6 +- .../router/transport/TransportManager.java | 4 +- .../src/net/i2p/router/transport/UPnP.java | 172 ++++++++---------- .../net/i2p/router/transport/UPnPManager.java | 51 ++++-- 4 files changed, 121 insertions(+), 112 deletions(-) diff --git a/router/java/src/net/i2p/router/transport/FIFOBandwidthLimiter.java b/router/java/src/net/i2p/router/transport/FIFOBandwidthLimiter.java index 86ba394e4..63bf73653 100644 --- a/router/java/src/net/i2p/router/transport/FIFOBandwidthLimiter.java +++ b/router/java/src/net/i2p/router/transport/FIFOBandwidthLimiter.java @@ -619,8 +619,8 @@ public class FIFOBandwidthLimiter { public void renderStatusHTML(Writer out) throws IOException { long now = now(); StringBuffer buf = new StringBuffer(4096); - buf.append("
Limiter status: ").append(getStatus().toString()).append("
\n"); - buf.append("Pending bandwidth requests:
    "); + buf.append("

    Limiter Status:
    ").append(getStatus().toString()).append("

    \n"); + buf.append("

    Pending bandwidth requests:

      "); buf.append("
    • Inbound requests:
        "); synchronized (_pendingInboundRequests) { for (int i = 0; i < _pendingInboundRequests.size(); i++) { @@ -643,7 +643,7 @@ public class FIFOBandwidthLimiter { buf.append("ms ago\n"); } } - buf.append("
    \n"); + buf.append("

\n"); out.write(buf.toString()); out.flush(); } diff --git a/router/java/src/net/i2p/router/transport/TransportManager.java b/router/java/src/net/i2p/router/transport/TransportManager.java index 0b7d8d547..813f7aa26 100644 --- a/router/java/src/net/i2p/router/transport/TransportManager.java +++ b/router/java/src/net/i2p/router/transport/TransportManager.java @@ -369,7 +369,7 @@ public class TransportManager implements TransportEventListener { t.renderStatusHTML(out, urlBase, sortFlags); } StringBuffer buf = new StringBuffer(4*1024); - buf.append("Listening on:
\n");
+        buf.append("

Router Transport Addresses:

\n");
         for (int i = 0; i < _transports.size(); i++) {
             Transport t = (Transport)_transports.get(i);
             if (t.getCurrentAddress() != null)
@@ -379,6 +379,8 @@ public class TransportManager implements TransportEventListener {
         }
         buf.append("
\n"); out.write(buf.toString()); + out.write(_upnpManager.renderStatusHTML()); + buf.append("

\n"); out.flush(); } } diff --git a/router/java/src/net/i2p/router/transport/UPnP.java b/router/java/src/net/i2p/router/transport/UPnP.java index aef65adc5..61ce5183b 100644 --- a/router/java/src/net/i2p/router/transport/UPnP.java +++ b/router/java/src/net/i2p/router/transport/UPnP.java @@ -108,11 +108,17 @@ public class UPnP extends ControlPoint implements DeviceChangeListener { DetectedIP result = null; final String natAddress = getNATAddress(); + if (natAddress == null || natAddress.length() <= 0) { + _log.warn("No external address returned"); + return null; + } try { InetAddress detectedIP = InetAddress.getByName(natAddress); + short status = DetectedIP.NOT_SUPPORTED; thinksWeAreDoubleNatted = !TransportImpl.isPubliclyRoutable(detectedIP.getAddress()); // If we have forwarded a port AND we don't have a private address + _log.warn("NATAddress: \"" + natAddress + "\" detectedIP: " + detectedIP + " double? " + thinksWeAreDoubleNatted); if((portsForwarded.size() > 1) && (!thinksWeAreDoubleNatted)) status = DetectedIP.FULL_INTERNET; @@ -291,6 +297,7 @@ public class UPnP extends ControlPoint implements DeviceChangeListener { return Integer.valueOf(getIP.getOutputArgumentList().getArgument("NewDownstreamMaxBitRate").getValue()); } +/*** private void listStateTable(Service serv, StringBuilder sb) { ServiceStateTable table = serv.getServiceStateTable(); sb.append("
"); @@ -320,6 +327,7 @@ public class UPnP extends ControlPoint implements DeviceChangeListener { sb.append("
"); } } +***/ private String toString(String action, String Argument, Service serv) { Action getIP = serv.getAction(action); @@ -333,122 +341,103 @@ public class UPnP extends ControlPoint implements DeviceChangeListener { // TODO: extend it! RTFM private void listSubServices(Device dev, StringBuilder sb) { ServiceList sl = dev.getServiceList(); + if (sl.size() <= 0) + return; + sb.append("
    \n"); for(int i=0; iservice ("+i+") : "+serv.getServiceType()+"
    "); + sb.append("
  • Service: "); if("urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1".equals(serv.getServiceType())){ - sb.append("WANCommonInterfaceConfig"); - sb.append(" status: " + toString("GetCommonLinkProperties", "NewPhysicalLinkStatus", serv)); - sb.append(" type: " + toString("GetCommonLinkProperties", "NewWANAccessType", serv)); - sb.append(" upstream: " + toString("GetCommonLinkProperties", "NewLayer1UpstreamMaxBitRate", serv)); - sb.append(" downstream: " + toString("GetCommonLinkProperties", "NewLayer1DownstreamMaxBitRate", serv) + "
    "); + sb.append("WAN Common Interface Config
      "); + sb.append("
    • Status: " + toString("GetCommonLinkProperties", "NewPhysicalLinkStatus", serv)); + sb.append("
    • Type: " + toString("GetCommonLinkProperties", "NewWANAccessType", serv)); + sb.append("
    • Upstream: " + toString("GetCommonLinkProperties", "NewLayer1UpstreamMaxBitRate", serv)); + sb.append("
    • Downstream: " + toString("GetCommonLinkProperties", "NewLayer1DownstreamMaxBitRate", serv) + "
      "); }else if("urn:schemas-upnp-org:service:WANPPPConnection:1".equals(serv.getServiceType())){ - sb.append("WANPPPConnection"); - sb.append(" status: " + toString("GetStatusInfo", "NewConnectionStatus", serv)); - sb.append(" type: " + toString("GetConnectionTypeInfo", "NewConnectionType", serv)); - sb.append(" upstream: " + toString("GetLinkLayerMaxBitRates", "NewUpstreamMaxBitRate", serv)); - sb.append(" downstream: " + toString("GetLinkLayerMaxBitRates", "NewDownstreamMaxBitRate", serv) + "
      "); - sb.append(" external IP: " + toString("GetExternalIPAddress", "NewExternalIPAddress", serv) + "
      "); + sb.append("WAN PPP Connection
        "); + sb.append("
      • Status: " + toString("GetStatusInfo", "NewConnectionStatus", serv)); + sb.append("
      • Type: " + toString("GetConnectionTypeInfo", "NewConnectionType", serv)); + sb.append("
      • Upstream: " + toString("GetLinkLayerMaxBitRates", "NewUpstreamMaxBitRate", serv)); + sb.append("
      • Downstream: " + toString("GetLinkLayerMaxBitRates", "NewDownstreamMaxBitRate", serv) + "
        "); + sb.append("
      • External IP: " + toString("GetExternalIPAddress", "NewExternalIPAddress", serv) + "
        "); }else if("urn:schemas-upnp-org:service:Layer3Forwarding:1".equals(serv.getServiceType())){ - sb.append("Layer3Forwarding"); - sb.append("DefaultConnectionService: " + toString("GetDefaultConnectionService", "NewDefaultConnectionService", serv)); + sb.append("Layer 3 Forwarding
          "); + sb.append("
        • Default Connection Service: " + toString("GetDefaultConnectionService", "NewDefaultConnectionService", serv)); }else if(WAN_IP_CONNECTION.equals(serv.getServiceType())){ - sb.append("WANIPConnection"); - sb.append(" status: " + toString("GetStatusInfo", "NewConnectionStatus", serv)); - sb.append(" type: " + toString("GetConnectionTypeInfo", "NewConnectionType", serv)); - sb.append(" external IP: " + toString("GetExternalIPAddress", "NewExternalIPAddress", serv) + "
          "); + sb.append("WAN IP Connection
            "); + sb.append("
          • Status: " + toString("GetStatusInfo", "NewConnectionStatus", serv)); + sb.append("
          • Type: " + toString("GetConnectionTypeInfo", "NewConnectionType", serv)); + sb.append("
          • External IP: " + toString("GetExternalIPAddress", "NewExternalIPAddress", serv) + "
            "); }else if("urn:schemas-upnp-org:service:WANEthernetLinkConfig:1".equals(serv.getServiceType())){ - sb.append("WANEthernetLinkConfig"); - sb.append(" status: " + toString("GetEthernetLinkStatus", "NewEthernetLinkStatus", serv) + "
            "); + sb.append("WAN Ethernet Link Config
              "); + sb.append("
            1. Status: " + toString("GetEthernetLinkStatus", "NewEthernetLinkStatus", serv) + "
              "); }else - sb.append("~~~~~~~ "+serv.getServiceType()); - listActions(serv, sb); - listStateTable(serv, sb); - sb.append(""); + sb.append("~~~~~~~ "+serv.getServiceType() + "
                "); + //listActions(serv, sb); + //listStateTable(serv, sb); + sb.append("
              \n"); } + sb.append("
          \n"); } private void listSubDev(String prefix, Device dev, StringBuilder sb){ - sb.append("

          Device : "+dev.getFriendlyName()+" - "+ dev.getDeviceType()+"
          "); + if (prefix == null) + sb.append("Device: "); + else + sb.append("

        • Subdevice: "); + sb.append(dev.getFriendlyName()); listSubServices(dev, sb); DeviceList dl = dev.getDeviceList(); + if (dl.size() <= 0) + return; + sb.append("
            \n"); for(int j=0; j"); listSubDev(dev.getFriendlyName(), subDev, sb); - sb.append(""); } - sb.append("

            "); + sb.append("
          \n"); } -/***** - public String handleHTTPGet(HTTPRequest request) throws PluginHTTPException { - if(request.isParameterSet("getDeviceCapabilities")) { - final StringBuilder sb = new StringBuilder(); - sb.append("UPnP report"); - listSubDev("WANDevice", _router, sb); - sb.append(""); + public String renderStatusHTML() { + final StringBuilder sb = new StringBuilder(); + sb.append("UPnP Status:
          "); + + if(isDisabled) { + sb.append("The plugin has been disabled; Do you have more than one UPnP Internet Gateway Device on your LAN ?"); + return sb.toString(); + } else if(!isNATPresent()) { + sb.append("The plugin hasn't found any UPnP aware, compatible device on your LAN."); return sb.toString(); } - HTMLNode pageNode = pr.getPageMaker().getPageNode("UP&P plugin configuration page", false, null); - HTMLNode contentNode = pr.getPageMaker().getContentNode(pageNode); - - if(isDisabled) { - HTMLNode disabledInfobox = contentNode.addChild("div", "class", "infobox infobox-error"); - HTMLNode disabledInfoboxHeader = disabledInfobox.addChild("div", "class", "infobox-header"); - HTMLNode disabledInfoboxContent = disabledInfobox.addChild("div", "class", "infobox-content"); - - disabledInfoboxHeader.addChild("#", "UP&P plugin report"); - disabledInfoboxContent.addChild("#", "The plugin has been disabled; Do you have more than one UP&P IGD on your LAN ?"); - return pageNode.generate(); - } else if(!isNATPresent()) { - HTMLNode notFoundInfobox = contentNode.addChild("div", "class", "infobox infobox-warning"); - HTMLNode notFoundInfoboxHeader = notFoundInfobox.addChild("div", "class", "infobox-header"); - HTMLNode notFoundInfoboxContent = notFoundInfobox.addChild("div", "class", "infobox-content"); - - notFoundInfoboxHeader.addChild("#", "UP&P plugin report"); - notFoundInfoboxContent.addChild("#", "The plugin hasn't found any UP&P aware, compatible device on your LAN."); - return pageNode.generate(); - } - - HTMLNode foundInfobox = contentNode.addChild("div", "class", "infobox infobox-normal"); - HTMLNode foundInfoboxHeader = foundInfobox.addChild("div", "class", "infobox-header"); - HTMLNode foundInfoboxContent = foundInfobox.addChild("div", "class", "infobox-content"); - // FIXME L10n! - foundInfoboxHeader.addChild("#", "UP&P plugin report"); - foundInfoboxContent.addChild("p", "The following device has been found : ").addChild("a", "href", "?getDeviceCapabilities").addChild("#", _router.getFriendlyName()); - foundInfoboxContent.addChild("p", "Our current external ip address is : " + getNATAddress()); + sb.append("

          Found "); + listSubDev(null, _router, sb); + sb.append("
          The current external IP address reported by UPnP is " + getNATAddress()); int downstreamMaxBitRate = getDownstreamMaxBitRate(); int upstreamMaxBitRate = getUpstramMaxBitRate(); if(downstreamMaxBitRate > 0) - foundInfoboxContent.addChild("p", "Our reported max downstream bit rate is : " + getDownstreamMaxBitRate()+ " bits/sec"); + sb.append("
          UPnP reports the max downstream bit rate is : " + getDownstreamMaxBitRate()+ " bits/sec\n"); if(upstreamMaxBitRate > 0) - foundInfoboxContent.addChild("p", "Our reported max upstream bit rate is : " + getUpstramMaxBitRate()+ " bits/sec"); + sb.append("
          UPnP reports the max upstream bit rate is : " + getUpstramMaxBitRate()+ " bits/sec\n"); synchronized(lock) { if(portsToForward != null) { for(ForwardPort port : portsToForward) { - if(portsForwarded.contains(port)) { - foundInfoboxContent.addChild("p", "The "+port.name+" port "+port.portNumber+" / "+port.protocol+" has been forwarded successfully."); - } else { - foundInfoboxContent.addChild("p", "The "+port.name+" port "+port.portNumber+" / "+port.protocol+" has not been forwarded."); - } + sb.append("
          " + protoToString(port.protocol) + " port " + port.portNumber + " for " + port.name); + if(portsForwarded.contains(port)) + sb.append(" has been forwarded successfully by UPnP.\n"); + else + sb.append(" has not been forwarded UPnP.\n"); } } } - return pageNode.generate(); + sb.append("

          "); + return sb.toString(); } - - public String handleHTTPPost(HTTPRequest request) throws PluginHTTPException { - return null; - } -***/ private boolean addMapping(String protocol, int port, String description, ForwardPort fp) { if(isDisabled || !isNATPresent() || _router == null) { @@ -457,8 +446,8 @@ public class UPnP extends ControlPoint implements DeviceChangeListener { } // Just in case... - // this confuses my linksys - zzz - // removeMapping(protocol, port, fp, true); + // this confuses my linksys? - zzz + removeMapping(protocol, port, fp, true); Action add = _service.getAction("AddPortMapping"); if(add == null) { @@ -557,14 +546,18 @@ public class UPnP extends ControlPoint implements DeviceChangeListener { registerPorts(portsToForwardNow); } + private static String protoToString(int p) { + if(p == ForwardPort.PROTOCOL_UDP_IPV4) + return "UDP"; + if(p == ForwardPort.PROTOCOL_TCP_IPV4) + return "TCP"; + return "?"; + } + private void registerPorts(Set portsToForwardNow) { for(ForwardPort port : portsToForwardNow) { - String proto; - if(port.protocol == ForwardPort.PROTOCOL_UDP_IPV4) - proto = "UDP"; - else if(port.protocol == ForwardPort.PROTOCOL_TCP_IPV4) - proto = "TCP"; - else { + String proto = protoToString(port.protocol); + if (proto.length() <= 1) { HashMap map = new HashMap(); map.put(port, new ForwardPortStatus(ForwardPortStatus.DEFINITE_FAILURE, "Protocol not supported", port.portNumber)); forwardCallback.portForwardStatus(map); @@ -586,15 +579,10 @@ public class UPnP extends ControlPoint implements DeviceChangeListener { private void unregisterPorts(Set portsToForwardNow) { for(ForwardPort port : portsToForwardNow) { - String proto; - if(port.protocol == ForwardPort.PROTOCOL_UDP_IPV4) - proto = "UDP"; - else if(port.protocol == ForwardPort.PROTOCOL_TCP_IPV4) - proto = "TCP"; - else { + String proto = protoToString(port.protocol); + if (proto.length() <= 1) // Ignore, we've already complained about it continue; - } removeMapping(proto, port.portNumber, port, false); } } diff --git a/router/java/src/net/i2p/router/transport/UPnPManager.java b/router/java/src/net/i2p/router/transport/UPnPManager.java index 4deaebc4d..a508cabef 100644 --- a/router/java/src/net/i2p/router/transport/UPnPManager.java +++ b/router/java/src/net/i2p/router/transport/UPnPManager.java @@ -45,15 +45,18 @@ public class UPnPManager { } public synchronized void start() { - _log.error("UPnP Start"); - Debug.on(); // UPnP stuff -> wrapper log + if (_log.shouldLog(Log.DEBUG)) { + _log.debug("UPnP Start"); + Debug.on(); // UPnP stuff -> wrapper log + } if (!_isRunning) _upnp.runPlugin(); _isRunning = true; } public synchronized void stop() { - _log.error("UPnP Stop"); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("UPnP Stop"); if (_isRunning) _upnp.terminate(); _isRunning = false; @@ -61,7 +64,8 @@ public class UPnPManager { /** call when the ports might have changed */ public void update(Map addresses) { - _log.error("UPnP Update:"); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("UPnP Update:"); if (!_isRunning) return; Set forwards = new HashSet(addresses.size()); @@ -86,7 +90,8 @@ public class UPnPManager { protocol = ForwardPort.PROTOCOL_TCP_IPV4; else continue; - _log.error("Adding: " + style + " " + port); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Adding: " + style + " " + port); ForwardPort fp = new ForwardPort(style, false, protocol, port); forwards.add(fp); } @@ -98,18 +103,32 @@ public class UPnPManager { /** Called to indicate status on one or more forwarded ports. */ public void portForwardStatus(Map statuses) { - _log.error("UPnP Callback:"); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("UPnP Callback:"); - DetectedIP[] ips = _upnp.getAddress(); - for (DetectedIP ip : ips) { - _log.error("External address: " + ip.publicAddress + " type: " + ip.natType); - } + DetectedIP[] ips = _upnp.getAddress(); + if (ips != null) { + for (DetectedIP ip : ips) { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("External address: " + ip.publicAddress + " type: " + ip.natType); + } + } else { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("No external address returned"); + } - for (ForwardPort fp : statuses.keySet()) { - ForwardPortStatus fps = statuses.get(fp); - _log.error(fp.name + " " + fp.protocol + " " + fp.portNumber + - " status: " + fps.status + " reason: " + fps.reasonString + " ext port: " + fps.externalPort); - } - } + for (ForwardPort fp : statuses.keySet()) { + ForwardPortStatus fps = statuses.get(fp); + if (_log.shouldLog(Log.DEBUG)) + _log.debug(fp.name + " " + fp.protocol + " " + fp.portNumber + + " status: " + fps.status + " reason: " + fps.reasonString + " ext port: " + fps.externalPort); + } + } + } + + public String renderStatusHTML() { + if (!_isRunning) + return "UPnP is not enabled\n"; + return _upnp.renderStatusHTML(); } } From 06aeff9a3057a5642f4c06f963eac68e548071c8 Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 20 Feb 2009 19:00:01 +0000 Subject: [PATCH 004/688] drop file accidentally checked in --- router/java/src/org/imports | 57 ------------------------------------- 1 file changed, 57 deletions(-) delete mode 100644 router/java/src/org/imports diff --git a/router/java/src/org/imports b/router/java/src/org/imports deleted file mode 100644 index 6fd39288b..000000000 --- a/router/java/src/org/imports +++ /dev/null @@ -1,57 +0,0 @@ - * import java.io.IOException; - * import java.io.StringReader; - * import plugins.JabberLinker.org.xmlpull.v1.XmlPullParserException.html; - * import plugins.JabberLinker.org.xmlpull.v1.XmlPullParserFactory; - * import plugins.JabberLinker.org.xmlpull.v1.XmlPullParser; -//import plugins.UPnP.org.cybergarage.util.*; -import java.io.*; -import java.io.*; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.io.Reader; -import java.io.Writer; -import java.net.*; -import java.net.*; -import java.util.*; -import java.util.*; -import java.util.Calendar; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.TimeZone; -import java.util.Vector; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import org.w3c.dom.Document; -import org.w3c.dom.NamedNodeMap; -import org.xml.sax.InputSource; -import plugins.UPnP.org.cybergarage.http.*; -import plugins.UPnP.org.cybergarage.http.*; -import plugins.UPnP.org.cybergarage.net.*; -import plugins.UPnP.org.cybergarage.net.*; -import plugins.UPnP.org.cybergarage.soap.*; -import plugins.UPnP.org.cybergarage.soap.*; -import plugins.UPnP.org.cybergarage.upnp.*; -import plugins.UPnP.org.cybergarage.upnp.*; -import plugins.UPnP.org.cybergarage.upnp.Device; -import plugins.UPnP.org.cybergarage.upnp.control.*; -import plugins.UPnP.org.cybergarage.upnp.control.*; -import plugins.UPnP.org.cybergarage.upnp.device.*; -import plugins.UPnP.org.cybergarage.upnp.device.*; -import plugins.UPnP.org.cybergarage.upnp.event.*; -import plugins.UPnP.org.cybergarage.upnp.event.*; -import plugins.UPnP.org.cybergarage.upnp.ssdp.*; -import plugins.UPnP.org.cybergarage.upnp.ssdp.*; -import plugins.UPnP.org.cybergarage.upnp.xml.*; -import plugins.UPnP.org.cybergarage.upnp.xml.*; -import plugins.UPnP.org.cybergarage.util.*; -import plugins.UPnP.org.cybergarage.util.*; -import plugins.UPnP.org.cybergarage.xml.*; -import plugins.UPnP.org.cybergarage.xml.*; -import plugins.UPnP.org.cybergarage.xml.Node; -import plugins.UPnP.org.cybergarage.xml.Parser; -import plugins.UPnP.org.cybergarage.xml.ParserException; -import plugins.UPnP.org.cybergarage.xml.parser.*; From f4c3607c4d7725997fc5d9627d70a305d3a2d0e4 Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 22 Feb 2009 00:35:24 +0000 Subject: [PATCH 005/688] * I2PTunnel: - Add new IRCServer tunnel type - Catch OOMs in HTTPServer - Name the IRCClient filter threads --- .../java/src/net/i2p/i2ptunnel/I2PTunnel.java | 49 +++++ .../i2p/i2ptunnel/I2PTunnelHTTPServer.java | 9 + .../net/i2p/i2ptunnel/I2PTunnelIRCClient.java | 4 +- .../net/i2p/i2ptunnel/I2PTunnelIRCServer.java | 193 ++++++++++++++++++ .../net/i2p/i2ptunnel/TunnelController.java | 15 +- .../src/net/i2p/i2ptunnel/web/IndexBean.java | 1 + apps/i2ptunnel/jsp/edit.jsp | 4 +- apps/i2ptunnel/jsp/index.jsp | 1 + 8 files changed, 270 insertions(+), 6 deletions(-) create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java index b72ae18b3..18b517cd2 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java @@ -234,6 +234,8 @@ public class I2PTunnel implements Logging, EventDispatcher { runServer(args, l); } else if ("httpserver".equals(cmdname)) { runHttpServer(args, l); + } else if ("ircserver".equals(cmdname)) { + runIrcServer(args, l); } else if ("textserver".equals(cmdname)) { runTextServer(args, l); } else if ("client".equals(cmdname)) { @@ -383,6 +385,53 @@ public class I2PTunnel implements Logging, EventDispatcher { } } + /** + * Same args as runServer + * (we should stop duplicating all this code...) + */ + public void runIrcServer(String args[], Logging l) { + if (args.length == 3) { + InetAddress serverHost = null; + int portNum = -1; + File privKeyFile = null; + try { + serverHost = InetAddress.getByName(args[0]); + } catch (UnknownHostException uhe) { + l.log("unknown host"); + _log.error(getPrefix() + "Error resolving " + args[0], uhe); + notifyEvent("serverTaskId", Integer.valueOf(-1)); + return; + } + + try { + portNum = Integer.parseInt(args[1]); + } catch (NumberFormatException nfe) { + l.log("invalid port"); + _log.error(getPrefix() + "Port specified is not valid: " + args[1], nfe); + notifyEvent("serverTaskId", Integer.valueOf(-1)); + return; + } + + privKeyFile = new File(args[2]); + if (!privKeyFile.canRead()) { + l.log("private key file does not exist"); + _log.error(getPrefix() + "Private key file does not exist or is not readable: " + args[2]); + notifyEvent("serverTaskId", Integer.valueOf(-1)); + return; + } + I2PTunnelServer serv = new I2PTunnelIRCServer(serverHost, portNum, privKeyFile, args[2], l, (EventDispatcher) this, this); + serv.setReadTimeout(readTimeout); + serv.startRunning(); + addtask(serv); + notifyEvent("serverTaskId", Integer.valueOf(serv.getId())); + return; + } else { + l.log("server "); + l.log(" creates a server that sends all incoming data\n" + " of its destination to host:port."); + notifyEvent("serverTaskId", Integer.valueOf(-1)); + } + } + /** * Run the HTTP server pointing at the host and port specified using the private i2p * destination loaded from the specified file, replacing the HTTP headers diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java index 658dd5e32..536844af9 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java @@ -124,8 +124,17 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { _log.error("Error while closing the received i2p con", ex); } } catch (IOException ex) { + try { + socket.close(); + } catch (IOException ioe) {} if (_log.shouldLog(Log.WARN)) _log.warn("Error while receiving the new HTTP request", ex); + } catch (OutOfMemoryError oom) { + try { + socket.close(); + } catch (IOException ioe) {} + if (_log.shouldLog(Log.ERROR)) + _log.error("OOM in HTTP server", oom); } long afterHandle = getTunnel().getContext().clock().now(); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java index e6708aa21..5b223b1a4 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java @@ -83,9 +83,9 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable i2ps = createI2PSocket(dest); i2ps.setReadTimeout(readTimeout); StringBuffer expectedPong = new StringBuffer(); - Thread in = new I2PThread(new IrcInboundFilter(s,i2ps, expectedPong)); + Thread in = new I2PThread(new IrcInboundFilter(s,i2ps, expectedPong), "IRC Client " + __clientId + " in"); in.start(); - Thread out = new I2PThread(new IrcOutboundFilter(s,i2ps, expectedPong)); + Thread out = new I2PThread(new IrcOutboundFilter(s,i2ps, expectedPong), "IRC Client " + __clientId + " out"); out.start(); } catch (Exception ex) { if (_log.shouldLog(Log.ERROR)) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java new file mode 100644 index 000000000..970d90ff0 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java @@ -0,0 +1,193 @@ +package net.i2p.i2ptunnel; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketException; +import java.util.Properties; + +import net.i2p.I2PAppContext; +import net.i2p.client.streaming.I2PSocket; +import net.i2p.crypto.SHA256Generator; +import net.i2p.data.DataFormatException; +import net.i2p.data.DataHelper; +import net.i2p.data.Destination; +import net.i2p.data.Hash; +import net.i2p.util.EventDispatcher; +import net.i2p.util.I2PThread; +import net.i2p.util.Log; + +/** + * Simple extension to the I2PTunnelServer that filters the registration + * sequence to pass the destination hash of the client through as the hostname, + * so an IRC Server may track users across nick changes. + * + * Of course, this requires the ircd actually use the hostname sent by + * the client rather than the IP. It is common for ircds to ignore the + * hostname in the USER message (unless it's coming from another server) + * since it is easily spoofed. So you have to fix or, if you are lucky, + * configure your ircd first. At least in unrealircd and ngircd this is + * not configurable. + * + * There are three options for mangling the desthash. Put the option in the + * "custom options" section of i2ptunnel. + * - ircserver.cloakKey unset: Cloak with a random value that is persistent for + * the life of this tunnel. This is the default. + * - ircserver.cloakKey=none: Don't cloak. Users may be correlated with their + * (probably) shared clients destination. + * Of course if the ircd does cloaking than this is ok. + * - ircserver.cloakKey=somepassphrase: Cloak with the hash of the passphrase. Use this to + * have consistent mangling across restarts, or to + * have multiple IRC servers cloak consistently to + * be able to track users even when they switch servers. + * Note: don't quote or put spaces in the passphrase, + * the i2ptunnel gui can't handle it. + * + * There is no outbound filtering. + * + * @author zzz + */ +public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable { + + private static final Log _log = new Log(I2PTunnelIRCServer.class); + private static final String PROP_CLOAK="ircserver.cloakKey"; + private boolean _cloak; + private byte[] _cloakKey; // 32 bytes of stuff to scramble the dest with + + /** + * @throws IllegalArgumentException if the I2PTunnel does not contain + * valid config to contact the router + */ + public I2PTunnelIRCServer(InetAddress host, int port, String privData, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) { + super(host, port, privData, l, notifyThis, tunnel); + initCloak(tunnel); + } + + public I2PTunnelIRCServer(InetAddress host, int port, File privkey, String privkeyname, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) { + super(host, port, privkey, privkeyname, l, notifyThis, tunnel); + initCloak(tunnel); + } + + public I2PTunnelIRCServer(InetAddress host, int port, InputStream privData, String privkeyname, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) { + super(host, port, privData, privkeyname, l, notifyThis, tunnel); + initCloak(tunnel); + } + + /** generate a random 32 bytes, or the hash of the passphrase */ + private void initCloak(I2PTunnel tunnel) { + Properties opts = tunnel.getClientOptions(); + String passphrase = opts.getProperty(PROP_CLOAK); + _cloak = passphrase == null || !"none".equals(passphrase); + if (_cloak) { + if (passphrase == null) { + _cloakKey = new byte[Hash.HASH_LENGTH]; + tunnel.getContext().random().nextBytes(_cloakKey); + } else { + _cloakKey = SHA256Generator.getInstance().calculateHash(passphrase.trim().getBytes()).getData(); + } + } + } + + protected void blockingHandle(I2PSocket socket) { + try { + // give them 15 seconds to send in the request + socket.setReadTimeout(15*1000); + InputStream in = socket.getInputStream(); + String modifiedRegistration = filterRegistration(in, cloakDest(socket.getPeerDestination())); + socket.setReadTimeout(readTimeout); + Socket s = new Socket(remoteHost, remotePort); + new I2PTunnelRunner(s, socket, slock, null, modifiedRegistration.getBytes(), null); + } catch (SocketException ex) { + try { + socket.close(); + } catch (IOException ioe) { + if (_log.shouldLog(Log.ERROR)) + _log.error("Error while closing the received i2p con", ex); + } + } catch (IOException ex) { + try { + socket.close(); + } catch (IOException ioe) {} + if (_log.shouldLog(Log.WARN)) + _log.warn("Error while receiving the new IRC Connection", ex); + } catch (OutOfMemoryError oom) { + try { + socket.close(); + } catch (IOException ioe) {} + if (_log.shouldLog(Log.ERROR)) + _log.error("OOM in IRC server", oom); + } + } + + /** + * (Optionally) append 32 bytes of crap to the destination then return + * the first few characters of the hash of the whole thing, + ".i2p". + * Or do we want the full hash if the ircd is going to use this for + * nickserv auto-login? Or even Base32 if it will be used in a + * case-insensitive manner? + * + */ + String cloakDest(Destination d) { + Hash h; + if (_cloak) { + byte[] b = new byte[d.size() + _cloakKey.length]; + System.arraycopy(b, 0, d.toByteArray(), 0, d.size()); + System.arraycopy(b, d.size(), _cloakKey, 0, _cloakKey.length); + h = SHA256Generator.getInstance().calculateHash(b); + } else { + h = d.calculateHash(); + } + return h.toBase64().substring(0, 8) + ".i2p"; + } + + /** keep reading until we see USER or SERVER */ + private String filterRegistration(InputStream in, String newHostname) throws IOException { + StringBuffer buf = new StringBuffer(128); + int lineCount = 0; + + while (true) { + String s = DataHelper.readLine(in); + if (s == null) + throw new IOException("EOF reached before the end of the headers [" + buf.toString() + "]"); + if (++lineCount > 10) + throw new IOException("Too many lines before USER or SERVER, giving up"); + s = s.trim(); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Got line: " + s); + + String field[]=s.split(" ",5); + String command; + int idx=0; + + if(field[0].charAt(0)==':') + idx++; + + try { command = field[idx++]; } + catch (IndexOutOfBoundsException ioobe) // wtf, server sent borked command? + { + throw new IOException("Dropping defective message: index out of bounds while extracting command."); + } + + if ("USER".equalsIgnoreCase(command)) { + if (field.length < idx + 4) + throw new IOException("Too few parameters in USER message: " + s); + // USER zzz1 hostname localhost :zzz + // => + // USER zzz1 abcd1234.i2p localhost :zzz + // this whole class is for these two lines... + buf.append("USER ").append(field[idx]).append(' ').append(newHostname).append(".i2p "); + buf.append(field[idx+2]).append(' ').append(field[idx+3]).append("\r\n"); + break; + } + buf.append(s).append("\r\n"); + if ("SERVER".equalsIgnoreCase(command)) + break; + } + if (_log.shouldLog(Log.DEBUG)) + _log.debug("All done, sending: " + buf.toString()); + return buf.toString(); + } +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java index 3c9640ce5..82b253985 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java @@ -58,7 +58,7 @@ public class TunnelController implements Logging { setConfig(config, prefix); _messages = new ArrayList(4); _running = false; - if (createKey && ("server".equals(getType()) || "httpserver".equals(getType())) ) + if (createKey && getType().endsWith("server")) createPrivateKey(); _starting = getStartOnLoad(); } @@ -148,6 +148,8 @@ public class TunnelController implements Logging { startServer(); } else if ("httpserver".equals(type)) { startHttpServer(); + } else if ("ircserver".equals(type)) { + startIrcServer(); } else { if (_log.shouldLog(Log.ERROR)) _log.error("Cannot start tunnel - unknown type [" + type + "]"); @@ -274,6 +276,17 @@ public class TunnelController implements Logging { _running = true; } + private void startIrcServer() { + setI2CPOptions(); + setSessionOptions(); + String targetHost = getTargetHost(); + String targetPort = getTargetPort(); + String privKeyFile = getPrivKeyFile(); + _tunnel.runIrcServer(new String[] { targetHost, targetPort, privKeyFile }, this); + acquire(); + _running = true; + } + private void setListenOn() { String listenOn = getListenOnInterface(); if ( (listenOn != null) && (listenOn.length() > 0) ) { diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java index 46b555772..87d8e26f6 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java @@ -386,6 +386,7 @@ public class IndexBean { else if ("httpserver".equals(internalType)) return "HTTP server"; else if ("sockstunnel".equals(internalType)) return "SOCKS 5 proxy"; else if ("connectclient".equals(internalType)) return "CONNECT/SSL/HTTPS proxy"; + else if ("ircserver".equals(internalType)) return "IRC server"; else return internalType; } diff --git a/apps/i2ptunnel/jsp/edit.jsp b/apps/i2ptunnel/jsp/edit.jsp index 67fdf016c..b58798b20 100644 --- a/apps/i2ptunnel/jsp/edit.jsp +++ b/apps/i2ptunnel/jsp/edit.jsp @@ -16,10 +16,8 @@ String tun = request.getParameter("tunnel"); int curTunnel = -1; if (EditBean.isClient(type)) { %><% - } else if ("server".equals(type) || "httpserver".equals(type)) { - %><% } else { - %>Invalid tunnel type<% + %><% } } %> diff --git a/apps/i2ptunnel/jsp/index.jsp b/apps/i2ptunnel/jsp/index.jsp index b96236ae1..77303267c 100644 --- a/apps/i2ptunnel/jsp/index.jsp +++ b/apps/i2ptunnel/jsp/index.jsp @@ -260,6 +260,7 @@ From 3603cc23ee0725256a0321e6fba59c190c1e37fe Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 22 Feb 2009 02:58:00 +0000 Subject: [PATCH 006/688] add socks 4/4a support --- .../i2p/i2ptunnel/socks/SOCKS4aServer.java | 283 ++++++++++++++++++ .../i2ptunnel/socks/SOCKSServerFactory.java | 4 + .../src/net/i2p/i2ptunnel/web/IndexBean.java | 2 +- apps/i2ptunnel/jsp/index.jsp | 2 +- 4 files changed, 289 insertions(+), 2 deletions(-) create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS4aServer.java diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS4aServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS4aServer.java new file mode 100644 index 000000000..2745cb0fa --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS4aServer.java @@ -0,0 +1,283 @@ +/* I2PSOCKSTunnel is released under the terms of the GNU GPL, + * with an additional exception. For further details, see the + * licensing terms in I2PTunnel.java. + * + * Copyright (c) 2004 by human + */ +package net.i2p.i2ptunnel.socks; + +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketException; +import java.util.List; + +import net.i2p.I2PAppContext; +import net.i2p.I2PException; +import net.i2p.client.streaming.I2PSocket; +import net.i2p.data.DataFormatException; +import net.i2p.i2ptunnel.I2PTunnel; +import net.i2p.util.HexDump; +import net.i2p.util.Log; + +/* + * Class that manages SOCKS 4/4a connections, and forwards them to + * destination hosts or (eventually) some outproxy. + * + * @author zzz modded from SOCKS5Server + */ +public class SOCKS4aServer extends SOCKSServer { + private static final Log _log = new Log(SOCKS4aServer.class); + + private Socket clientSock = null; + private boolean setupCompleted = false; + + /** + * Create a SOCKS4a server that communicates with the client using + * the specified socket. This method should not be invoked + * directly: new SOCKS4aServer objects should be created by using + * SOCKSServerFactory.createSOCSKServer(). It is assumed that the + * SOCKS VER field has been stripped from the input stream of the + * client socket. + * + * @param clientSock client socket + */ + public SOCKS4aServer(Socket clientSock) { + this.clientSock = clientSock; + } + + public Socket getClientSocket() throws SOCKSException { + setupServer(); + + return clientSock; + } + + protected void setupServer() throws SOCKSException { + if (setupCompleted) { return; } + + DataInputStream in; + DataOutputStream out; + try { + in = new DataInputStream(clientSock.getInputStream()); + out = new DataOutputStream(clientSock.getOutputStream()); + + manageRequest(in, out); + } catch (IOException e) { + throw new SOCKSException("Connection error (" + e.getMessage() + ")"); + } + + setupCompleted = true; + } + + /** + * SOCKS4a request management. This method assumes that all the + * stuff preceding or enveloping the actual request + * has been stripped out of the input/output streams. + */ + private void manageRequest(DataInputStream in, DataOutputStream out) throws IOException, SOCKSException { + + int command = in.readByte() & 0xff; + switch (command) { + case Command.CONNECT: + break; + case Command.BIND: + _log.debug("BIND command is not supported!"); + sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out); + throw new SOCKSException("BIND command not supported"); + default: + _log.debug("unknown command in request (" + Integer.toHexString(command) + ")"); + sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out); + throw new SOCKSException("Invalid command in request"); + } + + connPort = in.readUnsignedShort(); + if (connPort == 0) { + _log.debug("trying to connect to TCP port 0? Dropping!"); + sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out); + throw new SOCKSException("Invalid port number in request"); + } + + connHostName = new String(""); + boolean alreadyWarned = false; + for (int i = 0; i < 4; ++i) { + int octet = in.readByte() & 0xff; + connHostName += Integer.toString(octet); + if (i != 3) { + connHostName += "."; + if (octet != 0 && !alreadyWarned) { + _log.warn("IPV4 address type in request: " + connHostName + ". Is your client secure?"); + alreadyWarned = true; + } + } + } + + // discard user name + readString(in); + + // SOCKS 4a + if (connHostName.startsWith("0.0.0.") && !connHostName.equals("0.0.0.0")) + connHostName = readString(in); + } + + private String readString(DataInputStream in) throws IOException { + StringBuffer sb = new StringBuffer(16); + char c; + while ((c = (char) (in.readByte() & 0xff)) != 0) + sb.append(c); + return sb.toString(); + } + + protected void confirmConnection() throws SOCKSException { + DataInputStream in; + DataOutputStream out; + try { + out = new DataOutputStream(clientSock.getOutputStream()); + + sendRequestReply(Reply.SUCCEEDED, InetAddress.getByName("127.0.0.1"), 1, out); + } catch (IOException e) { + throw new SOCKSException("Connection error (" + e.getMessage() + ")"); + } + } + + /** + * Send the specified reply to a request of the client. Either + * one of inetAddr or domainName can be null, depending on + * addressType. + */ + private void sendRequestReply(int replyCode, InetAddress inetAddr, + int bindPort, DataOutputStream out) throws IOException { + ByteArrayOutputStream reps = new ByteArrayOutputStream(); + DataOutputStream dreps = new DataOutputStream(reps); + + // Reserved byte, should be 0x00 + dreps.write(0x00); + dreps.write(replyCode); + dreps.writeShort(bindPort); + dreps.write(inetAddr.getAddress()); + + byte[] reply = reps.toByteArray(); + + if (_log.shouldLog(Log.DEBUG)) { + _log.debug("Sending request reply:\n" + HexDump.dump(reply)); + } + + out.write(reply); + } + + /** + * Get an I2PSocket that can be used to send/receive 8-bit clean data + * to/from the destination of the SOCKS connection. + * + * @return an I2PSocket connected with the destination + */ + public I2PSocket getDestinationI2PSocket(I2PSOCKSTunnel t) throws SOCKSException { + setupServer(); + + if (connHostName == null) { + _log.error("BUG: destination host name has not been initialized!"); + throw new SOCKSException("BUG! See the logs!"); + } + if (connPort == 0) { + _log.error("BUG: destination port has not been initialized!"); + throw new SOCKSException("BUG! See the logs!"); + } + + DataOutputStream out; // for errors + try { + out = new DataOutputStream(clientSock.getOutputStream()); + } catch (IOException e) { + throw new SOCKSException("Connection error (" + e.getMessage() + ")"); + } + + // FIXME: here we should read our config file, select an + // outproxy, and instantiate the proper socket class that + // handles the outproxy itself (SOCKS4a, SOCKS4a, HTTP CONNECT...). + I2PSocket destSock; + + try { + if (connHostName.toLowerCase().endsWith(".i2p")) { + _log.debug("connecting to " + connHostName + "..."); + // Let's not due a new Dest for every request, huh? + //I2PSocketManager sm = I2PSocketManagerFactory.createManager(); + //destSock = sm.connect(I2PTunnel.destFromName(connHostName), null); + destSock = t.createI2PSocket(I2PTunnel.destFromName(connHostName)); + } else if ("localhost".equals(connHostName) || "127.0.0.1".equals(connHostName)) { + String err = "No localhost accesses allowed through the Socks Proxy"; + _log.error(err); + try { + sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out); + } catch (IOException ioe) {} + throw new SOCKSException(err); + } else if (connPort == 80) { + // rewrite GET line to include hostname??? or add Host: line??? + // or forward to local eepProxy (but that's a Socket not an I2PSocket) + // use eepProxy configured outproxies? + String err = "No handler for HTTP outproxy implemented - to: " + connHostName; + _log.error(err); + try { + sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out); + } catch (IOException ioe) {} + throw new SOCKSException(err); + } else { + List proxies = t.getProxies(connPort); + if (proxies == null || proxies.size() <= 0) { + String err = "No outproxy configured for port " + connPort + " and no default configured either"; + _log.error(err); + try { + sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out); + } catch (IOException ioe) {} + throw new SOCKSException(err); + } + int p = I2PAppContext.getGlobalContext().random().nextInt(proxies.size()); + String proxy = proxies.get(p); + _log.debug("connecting to port " + connPort + " proxy " + proxy + " for " + connHostName + "..."); + // this isn't going to work, these need to be socks outproxies so we need + // to do a socks session to them? + destSock = t.createI2PSocket(I2PTunnel.destFromName(proxy)); + } + confirmConnection(); + _log.debug("connection confirmed - exchanging data..."); + } catch (DataFormatException e) { + try { + sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out); + } catch (IOException ioe) {} + throw new SOCKSException("Error in destination format"); + } catch (SocketException e) { + try { + sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out); + } catch (IOException ioe) {} + throw new SOCKSException("Error connecting (" + + e.getMessage() + ")"); + } catch (IOException e) { + try { + sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out); + } catch (IOException ioe) {} + throw new SOCKSException("Error connecting (" + + e.getMessage() + ")"); + } catch (I2PException e) { + try { + sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out); + } catch (IOException ioe) {} + throw new SOCKSException("Error connecting (" + + e.getMessage() + ")"); + } + + return destSock; + } + + /* + * Some namespaces to enclose SOCKS protocol codes + */ + private static class Command { + private static final int CONNECT = 0x01; + private static final int BIND = 0x02; + } + + private static class Reply { + private static final int SUCCEEDED = 0x5a; + private static final int CONNECTION_REFUSED = 0x5b; + } +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServerFactory.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServerFactory.java index 67a52d688..80dfacb6a 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServerFactory.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServerFactory.java @@ -44,6 +44,10 @@ public class SOCKSServerFactory { int socksVer = in.readByte(); switch (socksVer) { + case 0x04: + // SOCKS version 4/4a + serv = new SOCKS4aServer(s); + break; case 0x05: // SOCKS version 5 serv = new SOCKS5Server(s); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java index 87d8e26f6..045ea5e58 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java @@ -384,7 +384,7 @@ public class IndexBean { else if ("ircclient".equals(internalType)) return "IRC client"; else if ("server".equals(internalType)) return "Standard server"; else if ("httpserver".equals(internalType)) return "HTTP server"; - else if ("sockstunnel".equals(internalType)) return "SOCKS 5 proxy"; + else if ("sockstunnel".equals(internalType)) return "SOCKS 4/4a/5 proxy"; else if ("connectclient".equals(internalType)) return "CONNECT/SSL/HTTPS proxy"; else if ("ircserver".equals(internalType)) return "IRC server"; else return internalType; diff --git a/apps/i2ptunnel/jsp/index.jsp b/apps/i2ptunnel/jsp/index.jsp index 77303267c..7787eb1f5 100644 --- a/apps/i2ptunnel/jsp/index.jsp +++ b/apps/i2ptunnel/jsp/index.jsp @@ -148,7 +148,7 @@ - + From 8bce2fd7a2cf10d25114dcfc3752a7e4b8b8b29f Mon Sep 17 00:00:00 2001 From: sponge Date: Sun, 22 Feb 2009 07:04:31 +0000 Subject: [PATCH 007/688] Hopeful BOB fixes for orphaned tunnels. Additional comments in TCPio addressing performance. --- apps/BOB/src/net/i2p/BOB/I2Plistener.java | 2 +- apps/BOB/src/net/i2p/BOB/MUXlisten.java | 37 ++++++++++++++++++----- apps/BOB/src/net/i2p/BOB/TCPio.java | 19 ++++++++++++ apps/BOB/src/net/i2p/BOB/TCPlistener.java | 2 +- 4 files changed, 51 insertions(+), 9 deletions(-) diff --git a/apps/BOB/src/net/i2p/BOB/I2Plistener.java b/apps/BOB/src/net/i2p/BOB/I2Plistener.java index 1561b7a22..c59683270 100644 --- a/apps/BOB/src/net/i2p/BOB/I2Plistener.java +++ b/apps/BOB/src/net/i2p/BOB/I2Plistener.java @@ -70,7 +70,7 @@ public class I2Plistener implements Runnable { boolean g = false; I2PSocket sessSocket = null; - serverSocket.setSoTimeout(100); + serverSocket.setSoTimeout(50); database.getReadLock(); info.getReadLock(); if(info.exists("INPORT")) { diff --git a/apps/BOB/src/net/i2p/BOB/MUXlisten.java b/apps/BOB/src/net/i2p/BOB/MUXlisten.java index bd52e27fd..89ab53fe6 100644 --- a/apps/BOB/src/net/i2p/BOB/MUXlisten.java +++ b/apps/BOB/src/net/i2p/BOB/MUXlisten.java @@ -173,7 +173,7 @@ die: { boolean spin = true; while(spin) { try { - Thread.sleep(1000); //sleep for 1000 ms (One second) + Thread.sleep(200); //sleep for 200 ms (Two thenths second) } catch(InterruptedException e) { // nop } @@ -213,14 +213,21 @@ die: { } } // die + try { + Thread.sleep(500); //sleep for 500 ms (One half second) + } catch(InterruptedException ex) { + // nop + } // wait for child threads and thread groups to die // System.out.println("MUXlisten: waiting for children"); - while(tg.activeCount() + tg.activeGroupCount() != 0) { + if(tg.activeCount() + tg.activeGroupCount() != 0) { tg.interrupt(); // unwedge any blocking threads. - try { - Thread.sleep(100); //sleep for 100 ms (One tenth second) - } catch(InterruptedException ex) { - // nop + while(tg.activeCount() + tg.activeGroupCount() != 0) { + try { + Thread.sleep(100); //sleep for 100 ms (One tenth second) + } catch(InterruptedException ex) { + // nop + } } } tg.destroy(); @@ -260,17 +267,33 @@ die: { } // This is here to catch when something fucks up REALLY bad. if(tg != null) { - while(tg.activeCount() + tg.activeGroupCount() != 0) { + if(tg.activeCount() + tg.activeGroupCount() != 0) { tg.interrupt(); // unwedge any blocking threads. + while(tg.activeCount() + tg.activeGroupCount() != 0) { try { Thread.sleep(100); //sleep for 100 ms (One tenth second) } catch(InterruptedException ex) { // nop } + } } tg.destroy(); // Zap reference to the ThreadGroup so the JVM can GC it. tg = null; } + + // Lastly try to close things again. + if(this.come_in) { + try { + listener.close(); + } catch(IOException e) { + } + } + try { + socketManager.destroySocketManager(); + } catch(Exception e) { + // nop + } + } } diff --git a/apps/BOB/src/net/i2p/BOB/TCPio.java b/apps/BOB/src/net/i2p/BOB/TCPio.java index 25290bcdc..41bb7cbe4 100644 --- a/apps/BOB/src/net/i2p/BOB/TCPio.java +++ b/apps/BOB/src/net/i2p/BOB/TCPio.java @@ -56,9 +56,28 @@ public class TCPio implements Runnable { * Copy from source to destination... * and yes, we are totally OK to block here on writes, * The OS has buffers, and I intend to use them. + * We send an interrupt signal to the threadgroup to + * unwedge any pending writes. * */ public void run() { + /* + * NOTE: + * The write method of OutputStream calls the write method of + * one argument on each of the bytes to be written out. + * Subclasses are encouraged to override this method and provide + * a more efficient implementation. + * + * So, is this really a performance problem? + * Should we expand to several bytes? + * I don't believe there would be any gain, since read method + * has the same reccomendations. If anyone has a better way to + * do this, I'm interested in performance improvements. + * + * --Sponge + * + */ + int b; byte a[] = new byte[1]; boolean spin = true; diff --git a/apps/BOB/src/net/i2p/BOB/TCPlistener.java b/apps/BOB/src/net/i2p/BOB/TCPlistener.java index 99ae047d3..30380a55d 100644 --- a/apps/BOB/src/net/i2p/BOB/TCPlistener.java +++ b/apps/BOB/src/net/i2p/BOB/TCPlistener.java @@ -77,7 +77,7 @@ public class TCPlistener implements Runnable { } try { Socket server = new Socket(); - listener.setSoTimeout(1000); + listener.setSoTimeout(50); // Half of the expected time from MUXlisten info.releaseReadLock(); database.releaseReadLock(); while(spin) { From 532077a4c15b6bf4b2f634a7d695de0eb96e55d1 Mon Sep 17 00:00:00 2001 From: sponge Date: Sun, 22 Feb 2009 07:26:08 +0000 Subject: [PATCH 008/688] BOB version bump. Router Build bump. --- apps/BOB/src/net/i2p/BOB/DoCMDS.java | 2 +- history.txt | 4 ++++ router/java/src/net/i2p/router/RouterVersion.java | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/BOB/src/net/i2p/BOB/DoCMDS.java b/apps/BOB/src/net/i2p/BOB/DoCMDS.java index 6033e303b..099d69fec 100644 --- a/apps/BOB/src/net/i2p/BOB/DoCMDS.java +++ b/apps/BOB/src/net/i2p/BOB/DoCMDS.java @@ -46,7 +46,7 @@ public class DoCMDS implements Runnable { // FIX ME // I need a better way to do versioning, but this will do for now. - public static final String BMAJ = "00", BMIN = "00", BREV = "03", BEXT = ""; + public static final String BMAJ = "00", BMIN = "00", BREV = "04", BEXT = ""; public static final String BOBversion = BMAJ + "." + BMIN + "." + BREV + BEXT; private Socket server; private Properties props; diff --git a/history.txt b/history.txt index 62c4430e9..ec81d5a43 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,7 @@ +2009-02-22 sponge + * BOB: Orphan tunnel issue fix, bump BOB version + * bump to Build 6 + 2009-02-16 zzz * Streaming lib: Plug timer leak, don't send keepalives after close, don't disconnect hard after close diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 5203f2360..c08948969 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -17,7 +17,7 @@ import net.i2p.CoreVersion; public class RouterVersion { public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 5; + public final static long BUILD = 6; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID); From 720aa704c4c5b8750e123045131f5ce8a00cdb97 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 23 Feb 2009 05:09:44 +0000 Subject: [PATCH 009/688] port streamr to i2ptunnel --- LICENSE.txt | 4 + .../java/src/net/i2p/i2ptunnel/I2PTunnel.java | 82 +++++++ .../net/i2p/i2ptunnel/I2PTunnelIRCServer.java | 9 - .../net/i2p/i2ptunnel/TunnelController.java | 59 +++-- .../i2p/i2ptunnel/streamr/MultiSource.java | 60 +++++ .../src/net/i2p/i2ptunnel/streamr/Pinger.java | 59 +++++ .../i2ptunnel/streamr/StreamrConsumer.java | 64 +++++ .../i2ptunnel/streamr/StreamrProducer.java | 70 ++++++ .../net/i2p/i2ptunnel/streamr/Subscriber.java | 75 ++++++ .../src/net/i2p/i2ptunnel/udp/I2PSink.java | 70 ++++++ .../i2p/i2ptunnel/udp/I2PSinkAnywhere.java | 67 ++++++ .../src/net/i2p/i2ptunnel/udp/I2PSource.java | 123 ++++++++++ .../java/src/net/i2p/i2ptunnel/udp/Sink.java | 17 ++ .../src/net/i2p/i2ptunnel/udp/Source.java | 15 ++ .../src/net/i2p/i2ptunnel/udp/Stream.java | 15 ++ .../src/net/i2p/i2ptunnel/udp/UDPSink.java | 74 ++++++ .../src/net/i2p/i2ptunnel/udp/UDPSource.java | 83 +++++++ .../udpTunnel/I2PTunnelUDPClientBase.java | 218 ++++++++++++++++++ .../udpTunnel/I2PTunnelUDPServerBase.java | 212 +++++++++++++++++ .../src/net/i2p/i2ptunnel/web/IndexBean.java | 8 +- apps/i2ptunnel/jsp/editClient.jsp | 30 ++- apps/i2ptunnel/jsp/editServer.jsp | 8 + apps/i2ptunnel/jsp/index.jsp | 2 + 23 files changed, 1375 insertions(+), 49 deletions(-) create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/MultiSource.java create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/Pinger.java create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrConsumer.java create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrProducer.java create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/Subscriber.java create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSink.java create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSinkAnywhere.java create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSource.java create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/Sink.java create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/Source.java create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/Stream.java create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/UDPSink.java create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/UDPSource.java create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udpTunnel/I2PTunnelUDPClientBase.java create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udpTunnel/I2PTunnelUDPServerBase.java diff --git a/LICENSE.txt b/LICENSE.txt index 324f532c6..e93798548 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -113,6 +113,10 @@ Applications: See licenses/LICENSE-I2PTunnel.txt See licenses/LICENSE-GPLv2.txt + I2PTunnel UDP and Streamr: + By welterde. + See licenses/LICENSE-GPLv2.txt + Jetty 5.1.12: Copyright 2000-2004 Mort Bay Consulting Pty. Ltd. See licenses/LICENSE-Apache1.1.txt diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java index 18b517cd2..dc9dfd2fc 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java @@ -62,6 +62,8 @@ import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; import net.i2p.data.Destination; import net.i2p.i2ptunnel.socks.I2PSOCKSTunnel; +import net.i2p.i2ptunnel.streamr.StreamrConsumer; +import net.i2p.i2ptunnel.streamr.StreamrProducer; import net.i2p.util.EventDispatcher; import net.i2p.util.EventDispatcherImpl; import net.i2p.util.Log; @@ -248,6 +250,10 @@ public class I2PTunnel implements Logging, EventDispatcher { runSOCKSTunnel(args, l); } else if ("connectclient".equals(cmdname)) { runConnectClient(args, l); + } else if ("streamrclient".equals(cmdname)) { + runStreamrClient(args, l); + } else if ("streamrserver".equals(cmdname)) { + runStreamrServer(args, l); } else if ("config".equals(cmdname)) { runConfig(args, l); } else if ("listen_on".equals(cmdname)) { @@ -800,6 +806,82 @@ public class I2PTunnel implements Logging, EventDispatcher { } } + /** + * Streamr client + * + * @param args {targethost, targetport, destinationString} + * @param l logger to receive events and output + */ + public void runStreamrClient(String args[], Logging l) { + if (args.length == 3) { + InetAddress host; + try { + host = InetAddress.getByName(args[0]); + } catch (UnknownHostException uhe) { + l.log("unknown host"); + _log.error(getPrefix() + "Error resolving " + args[0], uhe); + notifyEvent("streamrtunnelTaskId", Integer.valueOf(-1)); + return; + } + + int port = -1; + try { + port = Integer.parseInt(args[1]); + } catch (NumberFormatException nfe) { + l.log("invalid port"); + _log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe); + notifyEvent("streamrtunnelTaskId", Integer.valueOf(-1)); + return; + } + + StreamrConsumer task = new StreamrConsumer(host, port, args[2], l, (EventDispatcher) this, this); + task.startRunning(); + addtask(task); + notifyEvent("streamrtunnelTaskId", Integer.valueOf(task.getId())); + } else { + l.log("streamrclient "); + l.log(" creates a tunnel that receives streaming data."); + notifyEvent("streamrtunnelTaskId", Integer.valueOf(-1)); + } + } + + /** + * Streamr server + * + * @param args {port, privkeyfile} + * @param l logger to receive events and output + */ + public void runStreamrServer(String args[], Logging l) { + if (args.length == 2) { + int port = -1; + try { + port = Integer.parseInt(args[0]); + } catch (NumberFormatException nfe) { + l.log("invalid port"); + _log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe); + notifyEvent("streamrtunnelTaskId", Integer.valueOf(-1)); + return; + } + + File privKeyFile = new File(args[1]); + if (!privKeyFile.canRead()) { + l.log("private key file does not exist"); + _log.error(getPrefix() + "Private key file does not exist or is not readable: " + args[3]); + notifyEvent("serverTaskId", Integer.valueOf(-1)); + return; + } + + StreamrProducer task = new StreamrProducer(port, privKeyFile, args[1], l, (EventDispatcher) this, this); + task.startRunning(); + addtask(task); + notifyEvent("streamrtunnelTaskId", Integer.valueOf(task.getId())); + } else { + l.log("streamrserver "); + l.log(" creates a tunnel that sends streaming data."); + notifyEvent("streamrtunnelTaskId", Integer.valueOf(-1)); + } + } + /** * Specify the i2cp host and port * diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java index 970d90ff0..aa95e526c 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java @@ -61,21 +61,12 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable { * @throws IllegalArgumentException if the I2PTunnel does not contain * valid config to contact the router */ - public I2PTunnelIRCServer(InetAddress host, int port, String privData, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) { - super(host, port, privData, l, notifyThis, tunnel); - initCloak(tunnel); - } public I2PTunnelIRCServer(InetAddress host, int port, File privkey, String privkeyname, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) { super(host, port, privkey, privkeyname, l, notifyThis, tunnel); initCloak(tunnel); } - public I2PTunnelIRCServer(InetAddress host, int port, InputStream privData, String privkeyname, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) { - super(host, port, privData, privkeyname, l, notifyThis, tunnel); - initCloak(tunnel); - } - /** generate a random 32 bytes, or the hash of the passphrase */ private void initCloak(I2PTunnel tunnel) { Properties opts = tunnel.getClientOptions(); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java index 82b253985..6c5fa4eb9 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java @@ -134,6 +134,8 @@ public class TunnelController implements Logging { _log.warn("Cannot start the tunnel - no type specified"); return; } + setI2CPOptions(); + setSessionOptions(); if ("httpclient".equals(type)) { startHttpClient(); } else if("ircclient".equals(type)) { @@ -144,21 +146,26 @@ public class TunnelController implements Logging { startConnectClient(); } else if ("client".equals(type)) { startClient(); + } else if ("streamrclient".equals(type)) { + startStreamrClient(); } else if ("server".equals(type)) { startServer(); } else if ("httpserver".equals(type)) { startHttpServer(); } else if ("ircserver".equals(type)) { startIrcServer(); + } else if ("streamrserver".equals(type)) { + startStreamrServer(); } else { if (_log.shouldLog(Log.ERROR)) _log.error("Cannot start tunnel - unknown type [" + type + "]"); + return; } + acquire(); + _running = true; } private void startHttpClient() { - setI2CPOptions(); - setSessionOptions(); setListenOn(); String listenPort = getListenPort(); String proxyList = getProxyList(); @@ -167,13 +174,9 @@ public class TunnelController implements Logging { _tunnel.runHttpClient(new String[] { listenPort, sharedClient }, this); else _tunnel.runHttpClient(new String[] { listenPort, sharedClient, proxyList }, this); - acquire(); - _running = true; } private void startConnectClient() { - setI2CPOptions(); - setSessionOptions(); setListenOn(); String listenPort = getListenPort(); String proxyList = getProxyList(); @@ -182,31 +185,39 @@ public class TunnelController implements Logging { _tunnel.runConnectClient(new String[] { listenPort, sharedClient }, this); else _tunnel.runConnectClient(new String[] { listenPort, sharedClient, proxyList }, this); - acquire(); - _running = true; } private void startIrcClient() { - setI2CPOptions(); - setSessionOptions(); setListenOn(); String listenPort = getListenPort(); String dest = getTargetDestination(); String sharedClient = getSharedClient(); _tunnel.runIrcClient(new String[] { listenPort, dest, sharedClient }, this); - acquire(); - _running = true; } private void startSocksClient() { - setI2CPOptions(); - setSessionOptions(); setListenOn(); String listenPort = getListenPort(); String sharedClient = getSharedClient(); _tunnel.runSOCKSTunnel(new String[] { listenPort, sharedClient }, this); - acquire(); - _running = true; + } + + /* + * Streamr client is a UDP server, use the listenPort field for targetPort + * and the listenOnInterface field for the targetHost + */ + private void startStreamrClient() { + String targetHost = getListenOnInterface(); + String targetPort = getListenPort(); + String dest = getTargetDestination(); + _tunnel.runStreamrClient(new String[] { targetHost, targetPort, dest }, this); + } + + /** Streamr server is a UDP client, use the targetPort field for listenPort */ + private void startStreamrServer() { + String listenPort = getTargetPort(); + String privKeyFile = getPrivKeyFile(); + _tunnel.runStreamrServer(new String[] { listenPort, privKeyFile }, this); } /** @@ -242,49 +253,33 @@ public class TunnelController implements Logging { } private void startClient() { - setI2CPOptions(); - setSessionOptions(); setListenOn(); String listenPort = getListenPort(); String dest = getTargetDestination(); String sharedClient = getSharedClient(); _tunnel.runClient(new String[] { listenPort, dest, sharedClient }, this); - acquire(); - _running = true; } private void startServer() { - setI2CPOptions(); - setSessionOptions(); String targetHost = getTargetHost(); String targetPort = getTargetPort(); String privKeyFile = getPrivKeyFile(); _tunnel.runServer(new String[] { targetHost, targetPort, privKeyFile }, this); - acquire(); - _running = true; } private void startHttpServer() { - setI2CPOptions(); - setSessionOptions(); String targetHost = getTargetHost(); String targetPort = getTargetPort(); String spoofedHost = getSpoofedHost(); String privKeyFile = getPrivKeyFile(); _tunnel.runHttpServer(new String[] { targetHost, targetPort, spoofedHost, privKeyFile }, this); - acquire(); - _running = true; } private void startIrcServer() { - setI2CPOptions(); - setSessionOptions(); String targetHost = getTargetHost(); String targetPort = getTargetPort(); String privKeyFile = getPrivKeyFile(); _tunnel.runIrcServer(new String[] { targetHost, targetPort, privKeyFile }, this); - acquire(); - _running = true; } private void setListenOn() { diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/MultiSource.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/MultiSource.java new file mode 100644 index 000000000..13d9b5520 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/MultiSource.java @@ -0,0 +1,60 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package net.i2p.i2ptunnel.streamr; + +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.List; + +import net.i2p.data.Destination; +import net.i2p.i2ptunnel.udp.*; + +/** + * Sends to many Sinks + * @author welterde + * @author zzz modded for I2PTunnel + */ +public class MultiSource implements Source, Sink { + public MultiSource() { + this.sinks = new CopyOnWriteArrayList(); + } + + public void setSink(Sink sink) { + this.sink = sink; + } + + public void start() {} + + public void send(Destination ignored_from, byte[] data) { + for(Destination dest : this.sinks) { + this.sink.send(dest, data); + } + } + + public void add(Destination sink) { + this.sinks.add(sink); + } + + public void remove(Destination sink) { + this.sinks.remove(sink); + } + + + + + + + + + + + + + + + + private Sink sink; + private List sinks; +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/Pinger.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/Pinger.java new file mode 100644 index 000000000..a3a797536 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/Pinger.java @@ -0,0 +1,59 @@ +package net.i2p.i2ptunnel.streamr; + +import net.i2p.i2ptunnel.udp.*; + +/** + * + * @author welterde/zzz + */ +public class Pinger implements Source, Runnable { + public Pinger() { + this.thread = new Thread(this); + } + public void setSink(Sink sink) { + this.sink = sink; + } + + public void start() { + this.running = true; + this.waitlock = new Object(); + this.thread.start(); + } + + public void stop() { + this.running = false; + synchronized(this.waitlock) { + this.waitlock.notifyAll(); + } + // send unsubscribe-message + byte[] data = new byte[1]; + data[0] = 1; + this.sink.send(null, data); + } + + public void run() { + // send subscribe-message + byte[] data = new byte[1]; + data[0] = 0; + int i = 0; + while(this.running) { + //System.out.print("p"); + this.sink.send(null, data); + synchronized(this.waitlock) { + int delay = 10000; + if (i < 5) { + i++; + delay = 2000; + } + try { + this.waitlock.wait(delay); + } catch(InterruptedException ie) {} + } + } + } + + protected Sink sink; + protected Thread thread; + protected Object waitlock; + protected boolean running; +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrConsumer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrConsumer.java new file mode 100644 index 000000000..3fc1d881b --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrConsumer.java @@ -0,0 +1,64 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package net.i2p.i2ptunnel.streamr; + +import java.net.InetAddress; + +import net.i2p.data.Destination; +import net.i2p.i2ptunnel.I2PTunnel; +import net.i2p.i2ptunnel.Logging; +import net.i2p.i2ptunnel.udp.*; +import net.i2p.i2ptunnel.udpTunnel.I2PTunnelUDPClientBase; +import net.i2p.util.EventDispatcher; + +/** + * Compared to a standard I2PTunnel, + * this acts like a client on the I2P side (no privkey file) + * but a server on the UDP side (sends to a configured host/port) + * + * @author welterde + * @author zzz modded for I2PTunnel + */ +public class StreamrConsumer extends I2PTunnelUDPClientBase { + + public StreamrConsumer(InetAddress host, int port, String destination, + Logging l, EventDispatcher notifyThis, + I2PTunnel tunnel) { + super(destination, l, notifyThis, tunnel); + + // create udp-destination + this.sink = new UDPSink(host, port); + setSink(this.sink); + + // create pinger + this.pinger = new Pinger(); + this.pinger.setSink(this); + } + + public final void startRunning() { + super.startRunning(); + // send subscribe-message + this.pinger.start(); + } + + public boolean close(boolean forced) { + // send unsubscribe-message + this.pinger.stop(); + return super.close(forced); + } + + + + + + + + + + + private Sink sink; + private Pinger pinger; +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrProducer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrProducer.java new file mode 100644 index 000000000..d722c5f95 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrProducer.java @@ -0,0 +1,70 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package net.i2p.i2ptunnel.streamr; + +// system +import java.io.File; + +// i2p +import net.i2p.client.I2PSession; +import net.i2p.i2ptunnel.I2PTunnel; +import net.i2p.i2ptunnel.Logging; +import net.i2p.i2ptunnel.udp.*; +import net.i2p.i2ptunnel.udpTunnel.I2PTunnelUDPServerBase; +import net.i2p.util.EventDispatcher; + +/** + * Compared to a standard I2PTunnel, + * this acts like a server on the I2P side (persistent privkey file) + * but a client on the UDP side (receives on a configured port) + * + * @author welterde + * @author zzz modded for I2PTunnel + */ +public class StreamrProducer extends I2PTunnelUDPServerBase { + + public StreamrProducer(int port, + File privkey, String privkeyname, Logging l, + EventDispatcher notifyThis, I2PTunnel tunnel) { + // verify subscription requests + super(true, privkey, privkeyname, l, notifyThis, tunnel); + + // The broadcaster + this.multi = new MultiSource(); + this.multi.setSink(this); + + // The listener + this.subscriber = new Subscriber(this.multi); + setSink(this.subscriber); + + // now start udp-server + this.server = new UDPSource(port); + this.server.setSink(this.multi); + } + + public final void startRunning() { + super.startRunning(); + this.server.start(); + } + + public boolean close(boolean forced) { + // need some stop() methods in UDPSource and MultiSource + return super.close(forced); + } + + + + + + + + + + + private MultiSource multi; + private Source server; + private Sink subscriber; +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/Subscriber.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/Subscriber.java new file mode 100644 index 000000000..97abdb889 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/Subscriber.java @@ -0,0 +1,75 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package net.i2p.i2ptunnel.streamr; + +// system +import java.io.File; +import java.util.Set; + +// i2p +import net.i2p.client.I2PSession; +import net.i2p.data.Destination; +import net.i2p.i2ptunnel.I2PTunnel; +import net.i2p.i2ptunnel.Logging; +import net.i2p.i2ptunnel.udp.*; +import net.i2p.i2ptunnel.udpTunnel.I2PTunnelUDPServerBase; +import net.i2p.util.EventDispatcher; +import net.i2p.util.ConcurrentHashSet; + +/** + * server-mode + * @author welterde + * @author zzz modded from Producer for I2PTunnel + */ +public class Subscriber implements Sink { + + public Subscriber(MultiSource multi) { + this.multi = multi; + // subscriptions + this.subscriptions = new ConcurrentHashSet(); + } + + public void send(Destination dest, byte[] data) { + if(dest == null || data.length < 1) { + // invalid packet + // TODO: write to log + } else { + byte ctrl = data[0]; + if(ctrl == 0) { + if (!this.subscriptions.contains(dest)) { + // subscribe + System.out.println("Add subscription: " + dest.toBase64().substring(0,4)); + this.subscriptions.add(dest); + this.multi.add(dest); + } // else already subscribed + } else if(ctrl == 1) { + // unsubscribe + System.out.println("Remove subscription: " + dest.toBase64().substring(0,4)); + boolean removed = this.subscriptions.remove(dest); + if(removed) + multi.remove(dest); + } else { + // invalid packet + // TODO: write to log + } + } + } + + + + + + + + + + + private I2PSession sess; + private Source listener; + private Set subscriptions; + private MultiSource multi; + private Source server; +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSink.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSink.java new file mode 100644 index 000000000..3cbccf139 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSink.java @@ -0,0 +1,70 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package net.i2p.i2ptunnel.udp; + +// i2p +import net.i2p.client.I2PSession; +import net.i2p.client.I2PSessionException; +import net.i2p.data.Destination; +import net.i2p.client.datagram.I2PDatagramMaker; + +/** + * Producer + * + * This sends to a fixed destination specified in the constructor + * + * @author welterde + */ +public class I2PSink implements Sink { + public I2PSink(I2PSession sess, Destination dest) { + this(sess, dest, false); + } + public I2PSink(I2PSession sess, Destination dest, boolean raw) { + this.sess = sess; + this.dest = dest; + this.raw = raw; + + // create maker + if (!raw) + this.maker = new I2PDatagramMaker(this.sess); + } + + /** @param src ignored */ + public synchronized void send(Destination src, byte[] data) { + //System.out.print("w"); + // create payload + byte[] payload; + if(!this.raw) + payload = this.maker.makeI2PDatagram(data); + else + payload = data; + + // send message + try { + this.sess.sendMessage(this.dest, payload); + } catch(I2PSessionException exc) { + // TODO: handle better + exc.printStackTrace(); + } + } + + + + + + + + + + + + + + protected boolean raw; + protected I2PSession sess; + protected Destination dest; + protected I2PDatagramMaker maker; +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSinkAnywhere.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSinkAnywhere.java new file mode 100644 index 000000000..09385d46f --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSinkAnywhere.java @@ -0,0 +1,67 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package net.i2p.i2ptunnel.udp; + +// i2p +import net.i2p.client.I2PSession; +import net.i2p.client.I2PSessionException; +import net.i2p.data.Destination; +import net.i2p.client.datagram.I2PDatagramMaker; + +/** + * Producer + * + * This sends to any destination specified in send() + * + * @author zzz modded from I2PSink by welterde + */ +public class I2PSinkAnywhere implements Sink { + public I2PSinkAnywhere(I2PSession sess) { + this(sess, false); + } + public I2PSinkAnywhere(I2PSession sess, boolean raw) { + this.sess = sess; + this.raw = raw; + + // create maker + this.maker = new I2PDatagramMaker(this.sess); + } + + /** @param to - where it's going */ + public synchronized void send(Destination to, byte[] data) { + // create payload + byte[] payload; + if(!this.raw) + payload = this.maker.makeI2PDatagram(data); + else + payload = data; + + // send message + try { + this.sess.sendMessage(to, payload); + } catch(I2PSessionException exc) { + // TODO: handle better + exc.printStackTrace(); + } + } + + + + + + + + + + + + + + protected boolean raw; + protected I2PSession sess; + protected Destination dest; + protected I2PDatagramMaker maker; +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSource.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSource.java new file mode 100644 index 000000000..0b5474777 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSource.java @@ -0,0 +1,123 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package net.i2p.i2ptunnel.udp; + +// system +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; + +// i2p +import net.i2p.client.I2PSession; +import net.i2p.client.I2PSessionListener; +import net.i2p.client.datagram.I2PDatagramDissector; + +/** + * + * @author welterde + */ +public class I2PSource implements Source, Runnable { + public I2PSource(I2PSession sess) { + this(sess, true, false); + } + public I2PSource(I2PSession sess, boolean verify) { + this(sess, verify, false); + } + public I2PSource(I2PSession sess, boolean verify, boolean raw) { + this.sess = sess; + this.sink = null; + this.verify = verify; + this.raw = raw; + + // create queue + this.queue = new ArrayBlockingQueue(256); + + // create listener + this.sess.setSessionListener(new Listener()); + + // create thread + this.thread = new Thread(this); + } + + public void setSink(Sink sink) { + this.sink = sink; + } + + public void start() { + this.thread.start(); + } + + public void run() { + // create dissector + I2PDatagramDissector diss = new I2PDatagramDissector(); + while(true) { + try { + // get id + int id = this.queue.take(); + + // receive message + byte[] msg = this.sess.receiveMessage(id); + + if(!this.raw) { + // load datagram into it + diss.loadI2PDatagram(msg); + + // now call sink + if(this.verify) + this.sink.send(diss.getSender(), diss.getPayload()); + else + this.sink.send(diss.extractSender(), diss.extractPayload()); + } else { + // verify is ignored + this.sink.send(null, msg); + } + //System.out.print("r"); + } catch(Exception e) { + e.printStackTrace(); + } + } + } + + + + + + + protected class Listener implements I2PSessionListener { + + public void messageAvailable(I2PSession sess, int id, long size) { + try { + queue.put(id); + } catch(Exception e) { + // ignore + } + } + + public void reportAbuse(I2PSession arg0, int arg1) { + // ignore + } + + public void disconnected(I2PSession arg0) { + // ignore + } + + public void errorOccurred(I2PSession arg0, String arg1, Throwable arg2) { + // ignore + } + + } + + + + + + + protected I2PSession sess; + protected BlockingQueue queue; + protected Sink sink; + protected Thread thread; + protected boolean verify; + protected boolean raw; +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/Sink.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/Sink.java new file mode 100644 index 000000000..49e3e47a3 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/Sink.java @@ -0,0 +1,17 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package net.i2p.i2ptunnel.udp; + +// i2p +import net.i2p.data.Destination; + +/** + * + * @author welterde + */ +public interface Sink { + public void send(Destination src, byte[] data); +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/Source.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/Source.java new file mode 100644 index 000000000..f65d03b19 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/Source.java @@ -0,0 +1,15 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package net.i2p.i2ptunnel.udp; + +/** + * + * @author welterde + */ +public interface Source { + public void setSink(Sink sink); + public void start(); +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/Stream.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/Stream.java new file mode 100644 index 000000000..b8b57e696 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/Stream.java @@ -0,0 +1,15 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package net.i2p.i2ptunnel.udp; + +/** + * + * @author welterde + */ +public interface Stream { + public void start(); + public void stop(); +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/UDPSink.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/UDPSink.java new file mode 100644 index 000000000..15feba615 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/UDPSink.java @@ -0,0 +1,74 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package net.i2p.i2ptunnel.udp; + +// system +import java.net.DatagramSocket; +import java.net.DatagramPacket; +import java.net.InetAddress; + +// i2p +import net.i2p.data.Destination; + +/** + * + * @author welterde + */ +public class UDPSink implements Sink { + public UDPSink(InetAddress host, int port) { + // create socket + try { + this.sock = new DatagramSocket(); + } catch(Exception e) { + // TODO: fail better + throw new RuntimeException("failed to open udp-socket", e); + } + + this.remoteHost = host; + + // remote port + this.remotePort = port; + } + + public void send(Destination src, byte[] data) { + // create packet + DatagramPacket packet = new DatagramPacket(data, data.length, this.remoteHost, this.remotePort); + + // send packet + try { + this.sock.send(packet); + } catch(Exception e) { + // TODO: fail a bit better + e.printStackTrace(); + } + } + + + + + + + + + + + + + + + + + + + + + + + protected DatagramSocket sock; + protected InetAddress remoteHost; + protected int remotePort; + +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/UDPSource.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/UDPSource.java new file mode 100644 index 000000000..c54a984b0 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/UDPSource.java @@ -0,0 +1,83 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package net.i2p.i2ptunnel.udp; + +// system +import java.net.DatagramSocket; +import java.net.DatagramPacket; + +/** + * + * @author welterde + */ +public class UDPSource implements Source, Runnable { + public static final int MAX_SIZE = 15360; + public UDPSource(int port) { + this.sink = null; + + // create udp-socket + try { + this.sock = new DatagramSocket(port); + } catch(Exception e) { + throw new RuntimeException("failed to listen...", e); + } + + // create thread + this.thread = new Thread(this); + } + + public void setSink(Sink sink) { + this.sink = sink; + } + + public void start() { + this.thread.start(); + } + + public void run() { + // create packet + byte[] buf = new byte[MAX_SIZE]; + DatagramPacket pack = new DatagramPacket(buf, buf.length); + while(true) { + try { + // receive... + this.sock.receive(pack); + + // create new data array + byte[] nbuf = new byte[pack.getLength()]; + + // copy over + System.arraycopy(pack.getData(), 0, nbuf, 0, nbuf.length); + + // transfer to sink + this.sink.send(null, nbuf); + //System.out.print("i"); + } catch(Exception e) { + e.printStackTrace(); + } + } + } + + + + + + + + + + + + + + + + + + protected DatagramSocket sock; + protected Sink sink; + protected Thread thread; +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udpTunnel/I2PTunnelUDPClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udpTunnel/I2PTunnelUDPClientBase.java new file mode 100644 index 000000000..0123be6ea --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udpTunnel/I2PTunnelUDPClientBase.java @@ -0,0 +1,218 @@ +/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java) + * (c) 2003 - 2004 mihi + */ +package net.i2p.i2ptunnel.udpTunnel; + +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.ConnectException; +import java.net.InetAddress; +import java.net.NoRouteToHostException; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; + +import net.i2p.I2PAppContext; +import net.i2p.I2PException; +import net.i2p.client.I2PClient; +import net.i2p.client.I2PClientFactory; +import net.i2p.client.I2PSession; +import net.i2p.client.I2PSessionException; +import net.i2p.data.DataFormatException; +import net.i2p.data.Destination; +import net.i2p.i2ptunnel.I2PTunnel; +import net.i2p.i2ptunnel.I2PTunnelTask; +import net.i2p.i2ptunnel.Logging; +import net.i2p.i2ptunnel.udp.*; +import net.i2p.util.EventDispatcher; +import net.i2p.util.I2PThread; +import net.i2p.util.Log; + +public abstract class I2PTunnelUDPClientBase extends I2PTunnelTask implements Source, Sink { + + private static final Log _log = new Log(I2PTunnelUDPClientBase.class); + protected I2PAppContext _context; + protected Logging l; + + static final long DEFAULT_CONNECT_TIMEOUT = 60 * 1000; + + private static volatile long __clientId = 0; + protected long _clientId; + + protected Destination dest = null; + + private boolean listenerReady = false; + + private ServerSocket ss; + + private Object startLock = new Object(); + private boolean startRunning = false; + + private byte[] pubkey; + + private String handlerName; + + private Object conLock = new Object(); + + /** How many connections will we allow to be in the process of being built at once? */ + private int _numConnectionBuilders; + /** How long will we allow sockets to sit in the _waitingSockets map before killing them? */ + private int _maxWaitTime; + + private I2PSession _session; + private Source _i2pSource; + private Sink _i2pSink; + private Destination _otherDest; + + /** + * Base client class that sets up an I2P Datagram client destination. + * The UDP side is not implemented here, as there are at least + * two possibilities: + * + * 1) UDP side is a "server" + * Example: Streamr Consumer + * - Configure a destination host and port + * - External application sends no data + * - Extending class must have a constructor with host and port arguments + * + * 2) UDP side is a client/server + * Example: SOCKS UDP (DNS requests?) + * - configure an inbound port and a destination host and port + * - External application sends and receives data + * - Extending class must have a constructor with host and 2 port arguments + * + * So the implementing class must create a UDPSource and/or UDPSink, + * and must call setSink(). + * + * @throws IllegalArgumentException if the I2CP configuration is b0rked so + * badly that we cant create a socketManager + * + * @author zzz with portions from welterde's streamr + */ + public I2PTunnelUDPClientBase(String destination, Logging l, EventDispatcher notifyThis, + I2PTunnel tunnel) throws IllegalArgumentException { + super("UDPServer", notifyThis, tunnel); + _clientId = ++__clientId; + this.l = l; + + _context = tunnel.getContext(); + + tunnel.getClientOptions().setProperty("i2cp.dontPublishLeaseSet", "true"); + + // create i2pclient and destination + I2PClient client = I2PClientFactory.createClient(); + Destination dest; + byte[] key; + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(512); + dest = client.createDestination(out); + key = out.toByteArray(); + } catch(Exception exc) { + throw new RuntimeException("failed to create i2p-destination", exc); + } + + // create a session + try { + ByteArrayInputStream in = new ByteArrayInputStream(key); + _session = client.createSession(in, tunnel.getClientOptions()); + } catch(Exception exc) { + throw new RuntimeException("failed to create session", exc); + } + + // Setup the source. Always expect raw unverified datagrams. + _i2pSource = new I2PSource(_session, false, true); + + // Setup the sink. Always send repliable datagrams. + if (destination != null && destination.length() > 0) { + try { + _otherDest = I2PTunnel.destFromName(destination); + } catch (DataFormatException dfe) {} + if (_otherDest == null) { + l.log("Could not resolve " + destination); + throw new RuntimeException("failed to create session - could not resolve " + destination); + } + _i2pSink = new I2PSink(_session, _otherDest, false); + } else { + _i2pSink = new I2PSinkAnywhere(_session, false); + } + + //configurePool(tunnel); + + } + + /** + * Actually start working on outgoing connections. + * Classes should override to start UDP side as well. + * + * Not specified in I2PTunnelTask but used in both + * I2PTunnelClientBase and I2PTunnelServer so let's + * implement it here too. + */ + public void startRunning() { + synchronized (startLock) { + try { + _session.connect(); + } catch(I2PSessionException exc) { + throw new RuntimeException("failed to connect session", exc); + } + start(); + startRunning = true; + startLock.notify(); + } + + if (open && listenerReady) { + notifyEvent("openBaseClientResult", "ok"); + } else { + l.log("Error listening - please see the logs!"); + notifyEvent("openBaseClientResult", "error"); + } + } + + /** + * I2PTunnelTask Methods + * + * Classes should override to close UDP side as well + */ + public boolean close(boolean forced) { + if (!open) return true; + if (_session != null) { + try { + _session.destroySession(); + } catch (I2PSessionException ise) {} + } + l.log("Closing client " + toString()); + return true; + } + + /** + * Source Methods + * + * Sets the receiver of the UDP datagrams from I2P + * Subclass must call this after constructor + * and before start() + */ + public void setSink(Sink s) { + _i2pSource.setSink(s); + } + + /** start the source */ + public void start() { + _i2pSource.start(); + } + + /** + * Sink Methods + * + * @param to - ignored if configured for a single destination + * (we use the dest specified in the constructor) + */ + public void send(Destination to, byte[] data) { + _i2pSink.send(to, data); + } +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udpTunnel/I2PTunnelUDPServerBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udpTunnel/I2PTunnelUDPServerBase.java new file mode 100644 index 000000000..fe129fb13 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udpTunnel/I2PTunnelUDPServerBase.java @@ -0,0 +1,212 @@ +/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java) + * (c) 2003 - 2004 mihi + */ +package net.i2p.i2ptunnel.udpTunnel; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.ConnectException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.util.Iterator; +import java.util.Properties; + +import net.i2p.I2PAppContext; +import net.i2p.I2PException; +import net.i2p.client.I2PClient; +import net.i2p.client.I2PClientFactory; +import net.i2p.client.I2PSession; +import net.i2p.client.I2PSessionException; +import net.i2p.data.Base64; +import net.i2p.data.Destination; +import net.i2p.i2ptunnel.I2PTunnel; +import net.i2p.i2ptunnel.I2PTunnelTask; +import net.i2p.i2ptunnel.Logging; +import net.i2p.i2ptunnel.udp.*; +import net.i2p.util.EventDispatcher; +import net.i2p.util.I2PThread; +import net.i2p.util.Log; + +public class I2PTunnelUDPServerBase extends I2PTunnelTask implements Source, Sink { + + private final static Log _log = new Log(I2PTunnelUDPServerBase.class); + + private Object lock = new Object(); + protected Object slock = new Object(); + + private static volatile long __serverId = 0; + + private Logging l; + + private static final long DEFAULT_READ_TIMEOUT = -1; // 3*60*1000; + /** default timeout to 3 minutes - override if desired */ + protected long readTimeout = DEFAULT_READ_TIMEOUT; + + private I2PSession _session; + private Source _i2pSource; + private Sink _i2pSink; + + /** + * Base client class that sets up an I2P Datagram server destination. + * The UDP side is not implemented here, as there are at least + * two possibilities: + * + * 1) UDP side is a "client" + * Example: Streamr Producer + * - configure an inbound port + * - External application receives no data + * - Extending class must have a constructor with a port argument + * + * 2) UDP side is a client/server + * Example: DNS + * - configure an inbound port and a destination host and port + * - External application sends and receives data + * - Extending class must have a constructor with host and 2 port arguments + * + * So the implementing class must create a UDPSource and/or UDPSink, + * and must call setSink(). + * + * @throws IllegalArgumentException if the I2CP configuration is b0rked so + * badly that we cant create a socketManager + * + * @author zzz with portions from welterde's streamr + */ + + public I2PTunnelUDPServerBase(boolean verify, File privkey, String privkeyname, Logging l, + EventDispatcher notifyThis, I2PTunnel tunnel) { + super("UDPServer <- " + privkeyname, notifyThis, tunnel); + FileInputStream fis = null; + try { + fis = new FileInputStream(privkey); + init(verify, fis, privkeyname, l); + } catch (IOException ioe) { + _log.error("Error starting server", ioe); + notifyEvent("openServerResult", "error"); + } finally { + if (fis != null) + try { fis.close(); } catch (IOException ioe) {} + } + } + + private void init(boolean verify, InputStream privData, String privkeyname, Logging l) { + this.l = l; + int portNum = 7654; + if (getTunnel().port != null) { + try { + portNum = Integer.parseInt(getTunnel().port); + } catch (NumberFormatException nfe) { + _log.log(Log.CRIT, "Invalid port specified [" + getTunnel().port + "], reverting to " + portNum); + } + } + + // create i2pclient + I2PClient client = I2PClientFactory.createClient(); + + try { + _session = client.createSession(privData, getTunnel().getClientOptions()); + } catch(I2PSessionException exc) { + throw new RuntimeException("failed to create session", exc); + } + + // Setup the source. Always expect repliable datagrams, optionally verify + _i2pSource = new I2PSource(_session, verify, false); + + // Setup the sink. Always send raw datagrams. + _i2pSink = new I2PSinkAnywhere(_session, true); + } + + /** + * Classes should override to start UDP side as well. + * + * Not specified in I2PTunnelTask but used in both + * I2PTunnelClientBase and I2PTunnelServer so let's + * implement it here too. + */ + public void startRunning() { + //synchronized (startLock) { + try { + _session.connect(); + } catch(I2PSessionException exc) { + throw new RuntimeException("failed to connect session", exc); + } + start(); + //} + + l.log("Ready!"); + notifyEvent("openServerResult", "ok"); + open = true; + } + + /** + * Set the read idle timeout for newly-created connections (in + * milliseconds). After this time expires without data being reached from + * the I2P network, the connection itself will be closed. + */ + public void setReadTimeout(long ms) { + readTimeout = ms; + } + + /** + * Get the read idle timeout for newly-created connections (in + * milliseconds). + * + * @return The read timeout used for connections + */ + public long getReadTimeout() { + return readTimeout; + } + + /** + * I2PTunnelTask Methods + * + * Classes should override to close UDP side as well + */ + public boolean close(boolean forced) { + if (!open) return true; + synchronized (lock) { + l.log("Shutting down server " + toString()); + try { + if (_session != null) { + _session.destroySession(); + } + } catch (I2PException ex) { + _log.error("Error destroying the session", ex); + } + l.log("Server shut down."); + open = false; + return true; + } + } + + /** + * Source Methods + * + * Sets the receiver of the UDP datagrams from I2P + * Subclass must call this after constructor + * and before start() + */ + public void setSink(Sink s) { + _i2pSource.setSink(s); + } + + /** start the source */ + public void start() { + _i2pSource.start(); + } + + /** + * Sink Methods + * + * @param to + * + */ + public void send(Destination to, byte[] data) { + _i2pSink.send(to, data); + } +} + diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java index 045ea5e58..6fcd9f2fe 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java @@ -351,6 +351,7 @@ public class IndexBean { ("httpclient".equals(type)) || ("sockstunnel".equals(type)) || ("connectclient".equals(type)) || + ("streamrclient".equals(type)) || ("ircclient".equals(type))); } @@ -387,6 +388,8 @@ public class IndexBean { else if ("sockstunnel".equals(internalType)) return "SOCKS 4/4a/5 proxy"; else if ("connectclient".equals(internalType)) return "CONNECT/SSL/HTTPS proxy"; else if ("ircserver".equals(internalType)) return "IRC server"; + else if ("streamrclient".equals(internalType)) return "Streamr client"; + else if ("streamrserver".equals(internalType)) return "Streamr server"; else return internalType; } @@ -434,7 +437,8 @@ public class IndexBean { TunnelController tun = getController(tunnel); if (tun == null) return ""; String rv; - if ("client".equals(tun.getType())||"ircclient".equals(tun.getType())) + if ("client".equals(tun.getType()) || "ircclient".equals(tun.getType()) || + "streamrclient".equals(tun.getType())) rv = tun.getTargetDestination(); else rv = tun.getProxyList(); @@ -798,7 +802,7 @@ public class IndexBean { if ("httpclient".equals(_type) || "connectclient".equals(_type)) { if (_proxyList != null) config.setProperty("proxyList", _proxyList); - } else if ("ircclient".equals(_type) || "client".equals(_type)) { + } else if ("ircclient".equals(_type) || "client".equals(_type) || "streamrclient".equals(_type)) { if (_targetDestination != null) config.setProperty("targetDestination", _targetDestination); } else if ("httpserver".equals(_type)) { diff --git a/apps/i2ptunnel/jsp/editClient.jsp b/apps/i2ptunnel/jsp/editClient.jsp index 3e4c3ecd8..6a796eb7d 100644 --- a/apps/i2ptunnel/jsp/editClient.jsp +++ b/apps/i2ptunnel/jsp/editClient.jsp @@ -75,7 +75,11 @@
          + <% if ("streamrclient".equals(tunnelType)) { %> + + <% } else { %> + <% } %>
          + <% String otherInterface = ""; + String clientInterface = editBean.getClientInterface(curTunnel); + if ("streamrclient".equals(tunnelType)) { + otherInterface = clientInterface; + } else { %>
          + <% } // streamrclient %>
          @@ -123,7 +139,7 @@ - <% } else if ("client".equals(tunnelType) || "ircclient".equals(tunnelType)) { + <% } else if ("client".equals(tunnelType) || "ircclient".equals(tunnelType) || "streamrclient".equals(tunnelType)) { %>
          - <% } - %>
          + <% } %> + <% if (!"streamrclient".equals(tunnelType)) { %> +
          @@ -160,6 +177,7 @@ class="tickbox" /> (Share tunnels with other clients and irc/httpclients? Change requires restart of client proxy)
          + <% } // !streamrclient %>
          + <% if ("streamrserver".equals(tunnelType)) { %> + + <% } else { %> + <% } %>
          + <% if (!"streamrserver".equals(tunnelType)) { %>
          + <% } // !streamrserver %>
          + <% if (!"streamrserver".equals(tunnelType)) { %>
          + <% } // !streamrserver %>
          @@ -261,6 +262,7 @@ +
          From 7e21afe6a6c2ce3d84a8973393d5ac60154d7f90 Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 24 Feb 2009 22:59:59 +0000 Subject: [PATCH 010/688] sort the summary bar destinations --- .../src/net/i2p/router/web/SummaryHelper.java | 47 ++++++++++++++----- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java index 2e56e858b..47a07c374 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java @@ -1,11 +1,15 @@ package net.i2p.router.web; +import java.text.Collator; import java.text.DateFormat; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.Date; import java.util.Iterator; +import java.util.List; import java.util.Locale; -import java.util.Set; import net.i2p.data.DataHelper; import net.i2p.data.Destination; @@ -346,20 +350,16 @@ public class SummaryHelper extends HelperBase { * @return html section summary */ public String getDestinations() { - Set clients = _context.clientManager().listClients(); + // covert the set to a list so we can sort by name and not lose duplicates + List clients = new ArrayList(_context.clientManager().listClients()); + Collections.sort(clients, new AlphaComparator()); StringBuffer buf = new StringBuffer(512); buf.append("Local destinations
          "); for (Iterator iter = clients.iterator(); iter.hasNext(); ) { Destination client = (Destination)iter.next(); - TunnelPoolSettings in = _context.tunnelManager().getInboundSettings(client.calculateHash()); - TunnelPoolSettings out = _context.tunnelManager().getOutboundSettings(client.calculateHash()); - String name = (in != null ? in.getDestinationNickname() : null); - if (name == null) - name = (out != null ? out.getDestinationNickname() : null); - if (name == null) - name = client.calculateHash().toBase64().substring(0,6); + String name = getName(client); buf.append("* ").append(name).append("
          \n"); LeaseSet ls = _context.netDb().lookupLeaseSetLocally(client.calculateHash()); @@ -373,14 +373,38 @@ public class SummaryHelper extends HelperBase { buf.append("No leases
          \n"); } buf.append("Details "); + buf.append("\" target=\"_top\">Details "); buf.append("Config
          \n"); + buf.append("\" target=\"_top\">Config
          \n"); } buf.append("
          \n"); return buf.toString(); } + private class AlphaComparator implements Comparator { + public int compare(Object lhs, Object rhs) { + String lname = getName((Destination)lhs); + String rname = getName((Destination)rhs); + if (lname.equals("shared clients")) + return -1; + if (rname.equals("shared clients")) + return 1; + return Collator.getInstance().compare(lname, rname); + } + } + + private String getName(Destination d) { + TunnelPoolSettings in = _context.tunnelManager().getInboundSettings(d.calculateHash()); + String name = (in != null ? in.getDestinationNickname() : null); + if (name == null) { + TunnelPoolSettings out = _context.tunnelManager().getOutboundSettings(d.calculateHash()); + name = (out != null ? out.getDestinationNickname() : null); + if (name == null) + name = d.calculateHash().toBase64().substring(0,6); + } + return name; + } + /** * How many free inbound tunnels we have. * @@ -511,4 +535,5 @@ public class SummaryHelper extends HelperBase { public boolean updateAvailable() { return NewsFetcher.getInstance(_context).updateAvailable(); } + } From 7a684c160b50988cb9103d6a0033af64eb18bd08 Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 24 Feb 2009 23:15:26 +0000 Subject: [PATCH 011/688] * Routerconsole: - Thread hard shutdown and restart requests from the routerconsole, and add a delay even if no tunnels, to allow time for a UI response --- .../net/i2p/router/web/ConfigRestartBean.java | 19 +++++++++--- router/java/src/net/i2p/router/Router.java | 30 +++++++++++++++---- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigRestartBean.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigRestartBean.java index 3c9fe1bbf..75a8108c5 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigRestartBean.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigRestartBean.java @@ -26,12 +26,14 @@ public class ConfigRestartBean { if ( (nonce != null) && (systemNonce.equals(nonce)) && (action != null) ) { if ("shutdownImmediate".equals(action)) { ctx.router().addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_HARD)); - ctx.router().shutdown(Router.EXIT_HARD); // never returns + //ctx.router().shutdown(Router.EXIT_HARD); // never returns + ctx.router().shutdownGracefully(Router.EXIT_HARD); // give the UI time to respond } else if ("cancelShutdown".equals(action)) { ctx.router().cancelGracefulShutdown(); } else if ("restartImmediate".equals(action)) { ctx.router().addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_HARD_RESTART)); - ctx.router().shutdown(Router.EXIT_HARD_RESTART); // never returns + //ctx.router().shutdown(Router.EXIT_HARD_RESTART); // never returns + ctx.router().shutdownGracefully(Router.EXIT_HARD_RESTART); // give the UI time to respond } else if ("restart".equals(action)) { ctx.router().addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_GRACEFUL_RESTART)); ctx.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART); @@ -79,9 +81,18 @@ public class ConfigRestartBean { } private static boolean isShuttingDown(RouterContext ctx) { - return Router.EXIT_GRACEFUL == ctx.router().scheduledGracefulExitCode(); + return Router.EXIT_GRACEFUL == ctx.router().scheduledGracefulExitCode() || + Router.EXIT_HARD == ctx.router().scheduledGracefulExitCode(); } private static boolean isRestarting(RouterContext ctx) { - return Router.EXIT_GRACEFUL_RESTART == ctx.router().scheduledGracefulExitCode(); + return Router.EXIT_GRACEFUL_RESTART == ctx.router().scheduledGracefulExitCode() || + Router.EXIT_HARD_RESTART == ctx.router().scheduledGracefulExitCode(); + } + /** this is for summaryframe.jsp */ + public static long getRestartTimeRemaining() { + RouterContext ctx = ContextHelper.getContext(null); + if (ctx.router().gracefulShutdownInProgress()) + return ctx.router().getShutdownTimeRemaining(); + return Long.MAX_VALUE/2; // summaryframe.jsp adds a safety factor so we don't want to overflow... } } diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java index 033678924..77e4b1968 100644 --- a/router/java/src/net/i2p/router/Router.java +++ b/router/java/src/net/i2p/router/Router.java @@ -446,13 +446,14 @@ public class Router { */ private static final String _rebuildFiles[] = new String[] { "router.info", "router.keys", - "netDb/my.info", - "connectionTag.keys", + "netDb/my.info", // no longer used + "connectionTag.keys", // never used? "keyBackup/privateEncryption.key", "keyBackup/privateSigning.key", "keyBackup/publicEncryption.key", "keyBackup/publicSigning.key", - "sessionKeys.dat" }; + "sessionKeys.dat" // no longer used + }; static final String IDENTLOG = "identlog.txt"; public static void killKeys() { @@ -859,6 +860,10 @@ public class Router { public void shutdownGracefully() { shutdownGracefully(EXIT_GRACEFUL); } + /** + * Call this with EXIT_HARD or EXIT_HARD_RESTART for a non-blocking, + * hard, non-graceful shutdown with a brief delay to allow a UI response + */ public void shutdownGracefully(int exitCode) { _gracefulExitCode = exitCode; _config.setProperty(PROP_SHUTDOWN_IN_PROGRESS, "true"); @@ -887,7 +892,9 @@ public class Router { } /** How long until the graceful shutdown will kill us? */ public long getShutdownTimeRemaining() { - if (_gracefulExitCode <= 0) return -1; + if (_gracefulExitCode <= 0) return -1; // maybe Long.MAX_VALUE would be better? + if (_gracefulExitCode == EXIT_HARD || _gracefulExitCode == EXIT_HARD_RESTART) + return 0; long exp = _context.tunnelManager().getLastParticipatingExpiration(); if (exp < 0) return -1; @@ -906,9 +913,20 @@ public class Router { while (true) { boolean shutdown = (null != _config.getProperty(PROP_SHUTDOWN_IN_PROGRESS)); if (shutdown) { - if (_context.tunnelManager().getParticipatingCount() <= 0) { - if (_log.shouldLog(Log.CRIT)) + if (_gracefulExitCode == EXIT_HARD || _gracefulExitCode == EXIT_HARD_RESTART || + _context.tunnelManager().getParticipatingCount() <= 0) { + if (_gracefulExitCode == EXIT_HARD) + _log.log(Log.CRIT, "Shutting down after a brief delay"); + else if (_gracefulExitCode == EXIT_HARD_RESTART) + _log.log(Log.CRIT, "Restarting after a brief delay"); + else _log.log(Log.CRIT, "Graceful shutdown progress - no more tunnels, safe to die"); + // Allow time for a UI reponse + try { + synchronized (Thread.currentThread()) { + Thread.currentThread().wait(2*1000); + } + } catch (InterruptedException ie) {} shutdown(_gracefulExitCode); return; } else { From 559653f0ab532352503b527f38b326745f176940 Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 24 Feb 2009 23:18:12 +0000 Subject: [PATCH 012/688] clean up OCMOSJ cache cleaner --- .../OutboundClientMessageOneShotJob.java | 87 +++++++++++-------- 1 file changed, 51 insertions(+), 36 deletions(-) diff --git a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java index 0515d5c34..0e858ef77 100644 --- a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java +++ b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java @@ -34,6 +34,8 @@ import net.i2p.router.Router; import net.i2p.router.RouterContext; import net.i2p.router.TunnelInfo; import net.i2p.util.Log; +import net.i2p.util.SimpleScheduler; +import net.i2p.util.SimpleTimer; /** * Send a client message out a random outbound tunnel and into a random inbound @@ -98,6 +100,10 @@ public class OutboundClientMessageOneShotJob extends JobImpl { */ private static final int BUNDLE_PROBABILITY_DEFAULT = 100; + private static final Object _initializeLock = new Object(); + private static boolean _initialized = false; + private static final int CLEAN_INTERVAL = 5*60*1000; + /** * Send the sucker */ @@ -105,20 +111,26 @@ public class OutboundClientMessageOneShotJob extends JobImpl { super(ctx); _log = ctx.logManager().getLog(OutboundClientMessageOneShotJob.class); - ctx.statManager().createFrequencyStat("client.sendMessageFailFrequency", "How often does a client fail to send a message?", "ClientMessages", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.sendMessageSize", "How large are messages sent by the client?", "ClientMessages", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.sendAckTime", "Message round trip time", "ClientMessages", new long[] { 60*1000l, 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.timeoutCongestionTunnel", "How lagged our tunnels are when a send times out?", "ClientMessages", new long[] { 60*1000l, 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.timeoutCongestionMessage", "How fast we process messages locally when a send times out?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.timeoutCongestionInbound", "How much faster we are receiving data than our average bps when a send times out?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.leaseSetFoundLocally", "How often we tried to look for a leaseSet and found it locally?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.leaseSetFoundRemoteTime", "How long we tried to look for a remote leaseSet (when we succeeded)?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.leaseSetFailedRemoteTime", "How long we tried to look for a remote leaseSet (when we failed)?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.dispatchPrepareTime", "How long until we've queued up the dispatch job (since we started)?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.dispatchTime", "How long until we've dispatched the message (since we started)?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.dispatchSendTime", "How long the actual dispatching takes?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.dispatchNoTunnels", "How long after start do we run out of tunnels to send/receive with?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.dispatchNoACK", "Repeated message sends to a peer (no ack required)", "ClientMessages", new long[] { 60*1000l, 5*60*1000l, 60*60*1000l }); + synchronized (_initializeLock) { + if (!_initialized) { + SimpleScheduler.getInstance().addPeriodicEvent(new OCMOSJCacheCleaner(ctx), CLEAN_INTERVAL, CLEAN_INTERVAL); + ctx.statManager().createFrequencyStat("client.sendMessageFailFrequency", "How often does a client fail to send a message?", "ClientMessages", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.sendMessageSize", "How large are messages sent by the client?", "ClientMessages", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.sendAckTime", "Message round trip time", "ClientMessages", new long[] { 60*1000l, 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.timeoutCongestionTunnel", "How lagged our tunnels are when a send times out?", "ClientMessages", new long[] { 60*1000l, 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.timeoutCongestionMessage", "How fast we process messages locally when a send times out?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.timeoutCongestionInbound", "How much faster we are receiving data than our average bps when a send times out?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.leaseSetFoundLocally", "How often we tried to look for a leaseSet and found it locally?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.leaseSetFoundRemoteTime", "How long we tried to look for a remote leaseSet (when we succeeded)?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.leaseSetFailedRemoteTime", "How long we tried to look for a remote leaseSet (when we failed)?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.dispatchPrepareTime", "How long until we've queued up the dispatch job (since we started)?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.dispatchTime", "How long until we've dispatched the message (since we started)?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.dispatchSendTime", "How long the actual dispatching takes?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.dispatchNoTunnels", "How long after start do we run out of tunnels to send/receive with?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.dispatchNoACK", "Repeated message sends to a peer (no ack required)", "ClientMessages", new long[] { 60*1000l, 5*60*1000l, 60*60*1000l }); + _initialized = true; + } + } long timeoutMs = OVERALL_TIMEOUT_MS_DEFAULT; _clientMessage = msg; _clientMessageId = msg.getMessageId(); @@ -201,7 +213,6 @@ public class OutboundClientMessageOneShotJob extends JobImpl { * Key the cache on the source+dest pair. */ private static HashMap _leaseSetCache = new HashMap(); - private static long _lscleanTime = 0; private LeaseSet getReplyLeaseSet(boolean force) { LeaseSet newLS = getContext().netDb().lookupLeaseSetLocally(_from.calculateHash()); if (newLS == null) @@ -235,10 +246,6 @@ public class OutboundClientMessageOneShotJob extends JobImpl { // If the last leaseSet we sent him is still good, don't bother sending again long now = getContext().clock().now(); synchronized (_leaseSetCache) { - if (now - _lscleanTime > 5*60*1000) { // clean out periodically - cleanLeaseSetCache(_leaseSetCache); - _lscleanTime = now; - } if (!force) { LeaseSet ls = (LeaseSet) _leaseSetCache.get(hashPair()); if (ls != null) { @@ -306,7 +313,6 @@ public class OutboundClientMessageOneShotJob extends JobImpl { * */ private static HashMap _leaseCache = new HashMap(); - private static long _lcleanTime = 0; private boolean getNextLease() { _leaseSet = getContext().netDb().lookupLeaseSetLocally(_to.calculateHash()); if (_leaseSet == null) { @@ -319,10 +325,6 @@ public class OutboundClientMessageOneShotJob extends JobImpl { // Use the same lease if it's still good // Even if _leaseSet changed, _leaseSet.getEncryptionKey() didn't... synchronized (_leaseCache) { - if (now - _lcleanTime > 5*60*1000) { // clean out periodically - cleanLeaseCache(_leaseCache); - _lcleanTime = now; - } _lease = (Lease) _leaseCache.get(hashPair()); if (_lease != null) { // if outbound tunnel length == 0 && lease.firsthop.isBacklogged() don't use it ?? @@ -607,7 +609,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { * (needed for cleanTunnelCache) * 44 = 32 * 4 / 3 */ - private Hash sourceFromHashPair(String s) { + private static Hash sourceFromHashPair(String s) { return new Hash(Base64.decode(s.substring(44, 88))); } @@ -648,8 +650,8 @@ public class OutboundClientMessageOneShotJob extends JobImpl { * Clean out old leaseSets from a set. * Caller must synchronize on tc. */ - private void cleanLeaseSetCache(HashMap tc) { - long now = getContext().clock().now(); + private static void cleanLeaseSetCache(RouterContext ctx, HashMap tc) { + long now = ctx.clock().now(); List deleteList = new ArrayList(); for (Iterator iter = tc.entrySet().iterator(); iter.hasNext(); ) { Map.Entry entry = (Map.Entry)iter.next(); @@ -668,7 +670,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { * Clean out old leases from a set. * Caller must synchronize on tc. */ - private void cleanLeaseCache(HashMap tc) { + private static void cleanLeaseCache(HashMap tc) { List deleteList = new ArrayList(); for (Iterator iter = tc.entrySet().iterator(); iter.hasNext(); ) { Map.Entry entry = (Map.Entry)iter.next(); @@ -687,13 +689,13 @@ public class OutboundClientMessageOneShotJob extends JobImpl { * Clean out old tunnels from a set. * Caller must synchronize on tc. */ - private void cleanTunnelCache(HashMap tc) { + private static void cleanTunnelCache(RouterContext ctx, HashMap tc) { List deleteList = new ArrayList(); for (Iterator iter = tc.entrySet().iterator(); iter.hasNext(); ) { Map.Entry entry = (Map.Entry)iter.next(); String k = (String) entry.getKey(); TunnelInfo tunnel = (TunnelInfo) entry.getValue(); - if (!getContext().tunnelManager().isValidTunnel(sourceFromHashPair(k), tunnel)) + if (!ctx.tunnelManager().isValidTunnel(sourceFromHashPair(k), tunnel)) deleteList.add(k); } for (Iterator iter = deleteList.iterator(); iter.hasNext(); ) { @@ -702,6 +704,25 @@ public class OutboundClientMessageOneShotJob extends JobImpl { } } + private static class OCMOSJCacheCleaner implements SimpleTimer.TimedEvent { + private RouterContext _ctx; + private OCMOSJCacheCleaner(RouterContext ctx) { + _ctx = ctx; + } + public void timeReached() { + synchronized(_leaseSetCache) { + cleanLeaseSetCache(_ctx, _leaseSetCache); + } + synchronized(_leaseCache) { + cleanLeaseCache(_leaseCache); + } + synchronized(_tunnelCache) { + cleanTunnelCache(_ctx, _tunnelCache); + cleanTunnelCache(_ctx, _backloggedTunnelCache); + } + } + } + /** * Use the same outbound tunnel as we did for the same destination previously, * if possible, to keep the streaming lib happy @@ -712,16 +733,10 @@ public class OutboundClientMessageOneShotJob extends JobImpl { */ private static HashMap _tunnelCache = new HashMap(); private static HashMap _backloggedTunnelCache = new HashMap(); - private static long _cleanTime = 0; private TunnelInfo selectOutboundTunnel(Destination to) { TunnelInfo tunnel; long now = getContext().clock().now(); synchronized (_tunnelCache) { - if (now - _cleanTime > 5*60*1000) { // clean out periodically - cleanTunnelCache(_tunnelCache); - cleanTunnelCache(_backloggedTunnelCache); - _cleanTime = now; - } /** * If old tunnel is valid and no longer backlogged, use it. * This prevents an active anonymity attack, where a peer could tell From 6484005569a98cfa2bbb5ec4d3e869b5e51f2e06 Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 24 Feb 2009 23:28:53 +0000 Subject: [PATCH 013/688] I2PTunnel: First cut at SOCKS UDP (untested); also some streamr and UDP tweaks --- .../net/i2p/i2ptunnel/socks/MultiSink.java | 35 +++++++ .../net/i2p/i2ptunnel/socks/ReplyTracker.java | 36 +++++++ .../net/i2p/i2ptunnel/socks/SOCKS5Server.java | 84 +++++++++++++++-- .../net/i2p/i2ptunnel/socks/SOCKSHeader.java | 89 ++++++++++++++++++ .../net/i2p/i2ptunnel/socks/SOCKSUDPPort.java | 77 +++++++++++++++ .../i2p/i2ptunnel/socks/SOCKSUDPTunnel.java | 94 +++++++++++++++++++ .../i2ptunnel/socks/SOCKSUDPUnwrapper.java | 59 ++++++++++++ .../i2p/i2ptunnel/socks/SOCKSUDPWrapper.java | 49 ++++++++++ .../i2p/i2ptunnel/streamr/MultiSource.java | 4 + .../i2ptunnel/streamr/StreamrConsumer.java | 3 +- .../i2ptunnel/streamr/StreamrProducer.java | 5 +- .../src/net/i2p/i2ptunnel/udp/UDPSink.java | 21 +++-- .../src/net/i2p/i2ptunnel/udp/UDPSource.java | 14 ++- 13 files changed, 549 insertions(+), 21 deletions(-) create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/MultiSink.java create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/ReplyTracker.java create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSHeader.java create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSUDPPort.java create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSUDPTunnel.java create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSUDPUnwrapper.java create mode 100644 apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSUDPWrapper.java diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/MultiSink.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/MultiSink.java new file mode 100644 index 000000000..3c63758c1 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/MultiSink.java @@ -0,0 +1,35 @@ +package net.i2p.i2ptunnel.socks; + +import java.util.Map; + +import net.i2p.data.Destination; +import net.i2p.i2ptunnel.udp.*; +import net.i2p.util.Log; + +/** + * Sends to one of many Sinks + * @author zzz modded from streamr/MultiSource + */ +public class MultiSink implements Source, Sink { + private static final Log _log = new Log(MultiSink.class); + + public MultiSink(Map cache) { + this.cache = cache; + } + + /** Don't use this - put sinks in the cache */ + public void setSink(Sink sink) {} + + public void start() {} + + public void send(Destination from, byte[] data) { + Sink s = this.cache.get(from); + if (s == null) { + _log.error("No where to go for " + from.calculateHash().toBase64().substring(0, 6)); + return; + } + s.send(from, data); + } + + private Map cache; +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/ReplyTracker.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/ReplyTracker.java new file mode 100644 index 000000000..f6a124c95 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/ReplyTracker.java @@ -0,0 +1,36 @@ +package net.i2p.i2ptunnel.socks; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.Map; + +import net.i2p.data.Destination; +import net.i2p.i2ptunnel.udp.*; +import net.i2p.util.Log; + +/** + * Track who the reply goes to + * @author zzz + */ +public class ReplyTracker implements Source, Sink { + private static final Log _log = new Log(MultiSink.class); + + public ReplyTracker(Sink reply, Map cache) { + this.reply = reply; + this.cache = cache; + } + + public void setSink(Sink sink) { + this.sink = sink; + } + + public void start() {} + + public void send(Destination to, byte[] data) { + this.cache.put(to, this.reply); + this.sink.send(to, data); + } + + private Sink reply; + private Map cache; + private Sink sink; +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS5Server.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS5Server.java index 38c50f266..5e5292607 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS5Server.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS5Server.java @@ -13,12 +13,15 @@ import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.ArrayList; import java.util.List; import net.i2p.I2PAppContext; import net.i2p.I2PException; import net.i2p.client.streaming.I2PSocket; import net.i2p.data.DataFormatException; +import net.i2p.data.Destination; import net.i2p.i2ptunnel.I2PTunnel; import net.i2p.util.HexDump; import net.i2p.util.Log; @@ -67,7 +70,8 @@ public class SOCKS5Server extends SOCKSServer { out = new DataOutputStream(clientSock.getOutputStream()); init(in, out); - manageRequest(in, out); + if (manageRequest(in, out) == Command.UDP_ASSOCIATE) + handleUDP(in, out); } catch (IOException e) { throw new SOCKSException("Connection error (" + e.getMessage() + ")"); } @@ -111,7 +115,7 @@ public class SOCKS5Server extends SOCKSServer { * initialization, integrity/confidentiality encapsulations, etc) * has been stripped out of the input/output streams. */ - private void manageRequest(DataInputStream in, DataOutputStream out) throws IOException, SOCKSException { + private int manageRequest(DataInputStream in, DataOutputStream out) throws IOException, SOCKSException { int socksVer = in.readByte() & 0xff; if (socksVer != SOCKS_VERSION_5) { _log.debug("error in SOCKS5 request (protocol != 5? wtf?)"); @@ -127,9 +131,12 @@ public class SOCKS5Server extends SOCKSServer { sendRequestReply(Reply.COMMAND_NOT_SUPPORTED, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out); throw new SOCKSException("BIND command not supported"); case Command.UDP_ASSOCIATE: + /*** if(!Boolean.valueOf(tunnel.getOptions().getProperty("i2ptunnel.socks.allowUDP")).booleanValue()) { _log.debug("UDP ASSOCIATE command is not supported!"); sendRequestReply(Reply.COMMAND_NOT_SUPPORTED, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out); throw new SOCKSException("UDP ASSOCIATE command not supported"); + ***/ + break; default: _log.debug("unknown command in request (" + Integer.toHexString(command) + ")"); sendRequestReply(Reply.COMMAND_NOT_SUPPORTED, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out); @@ -152,7 +159,8 @@ public class SOCKS5Server extends SOCKSServer { connHostName += "."; } } - _log.warn("IPV4 address type in request: " + connHostName + ". Is your client secure?"); + if (command != Command.UDP_ASSOCIATE) + _log.warn("IPV4 address type in request: " + connHostName + ". Is your client secure?"); break; case AddressType.DOMAINNAME: { @@ -168,9 +176,12 @@ public class SOCKS5Server extends SOCKSServer { _log.debug("DOMAINNAME address type in request: " + connHostName); break; case AddressType.IPV6: - _log.warn("IP V6 address type in request! Is your client secure?" + " (IPv6 is not supported, anyway :-)"); - sendRequestReply(Reply.ADDRESS_TYPE_NOT_SUPPORTED, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out); - throw new SOCKSException("IPV6 addresses not supported"); + if (command != Command.UDP_ASSOCIATE) { + _log.warn("IP V6 address type in request! Is your client secure?" + " (IPv6 is not supported, anyway :-)"); + sendRequestReply(Reply.ADDRESS_TYPE_NOT_SUPPORTED, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out); + throw new SOCKSException("IPV6 addresses not supported"); + } + break; default: _log.debug("unknown address type in request (" + Integer.toHexString(command) + ")"); sendRequestReply(Reply.ADDRESS_TYPE_NOT_SUPPORTED, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out); @@ -183,6 +194,7 @@ public class SOCKS5Server extends SOCKSServer { sendRequestReply(Reply.CONNECTION_NOT_ALLOWED_BY_RULESET, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out); throw new SOCKSException("Invalid port number in request"); } + return command; } protected void confirmConnection() throws SOCKSException { @@ -293,6 +305,13 @@ public class SOCKS5Server extends SOCKSServer { // Let's not due a new Dest for every request, huh? //I2PSocketManager sm = I2PSocketManagerFactory.createManager(); //destSock = sm.connect(I2PTunnel.destFromName(connHostName), null); + Destination dest = I2PTunnel.destFromName(connHostName); + if (dest == null) { + try { + sendRequestReply(Reply.HOST_UNREACHABLE, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out); + } catch (IOException ioe) {} + throw new SOCKSException("Host not found"); + } destSock = t.createI2PSocket(I2PTunnel.destFromName(connHostName)); } else if ("localhost".equals(connHostName) || "127.0.0.1".equals(connHostName)) { String err = "No localhost accesses allowed through the Socks Proxy"; @@ -358,6 +377,59 @@ public class SOCKS5Server extends SOCKSServer { return destSock; } + // This isn't really the right place for this, we can't stop the tunnel once it starts. + static SOCKSUDPTunnel _tunnel; + static Object _startLock = new Object(); + static byte[] dummyIP = new byte[4]; + /** + * We got a UDP associate command. + * Loop here looking for more, never return normally, + * or else I2PSocksTunnel will create a streaming lib connection. + * + * Do UDP Socks clients actually send more than one Associate request? + * RFC 1928 isn't clear... maybe not. + */ + private void handleUDP(DataInputStream in, DataOutputStream out) throws SOCKSException { + List ports = new ArrayList(1); + synchronized (_startLock) { + if (_tunnel == null) { + // tunnel options? + _tunnel = new SOCKSUDPTunnel(new I2PTunnel()); + _tunnel.startRunning(); + } + } + while (true) { + // Set it up. connHostName and connPort are the client's info. + InetAddress ia = null; + try { + ia = InetAddress.getByAddress(connHostName, dummyIP); + } catch (UnknownHostException uhe) {} // won't happen, no resolving done here + int myPort = _tunnel.add(ia, connPort); + ports.add(Integer.valueOf(myPort)); + try { + sendRequestReply(Reply.SUCCEEDED, AddressType.IPV4, InetAddress.getByName("127.0.0.1"), null, myPort, out); + } catch (IOException ioe) { break; } + + // wait for more ??? + try { + int command = manageRequest(in, out); + // don't do this... + if (command != Command.UDP_ASSOCIATE) + break; + } catch (IOException ioe) { break; } + catch (SOCKSException ioe) { break; } + } + + for (Integer i : ports) + _tunnel.remove(i); + + // Prevent I2PSocksTunnel from calling getDestinationI2PSocket() above + // to create a streaming lib connection... + // This isn't very elegant... + // + throw new SOCKSException("End of UDP Processing"); + } + /* * Some namespaces to enclose SOCKS protocol codes */ diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSHeader.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSHeader.java new file mode 100644 index 000000000..763b9aa10 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSHeader.java @@ -0,0 +1,89 @@ +package net.i2p.i2ptunnel.socks; + +import net.i2p.data.Base32; +import net.i2p.data.DataFormatException; +import net.i2p.data.Destination; +import net.i2p.i2ptunnel.I2PTunnel; + +/** + * Save the SOCKS header from a datagram + * Ref: RFC 1928 + * + * @author zzz + */ +public class SOCKSHeader { + + /** + * @param data the whole packet + */ + public SOCKSHeader(byte[] data) { + if (data.length <= 8) + throw new IllegalArgumentException("Header too short: " + data.length); + if (data[0] != 0 || data[1] != 0) + throw new IllegalArgumentException("Not a SOCKS datagram?"); + if (data[2] != 0) + throw new IllegalArgumentException("We can't handle fragments!"); + int headerlen = 0; + int addressType = data[3]; + if (addressType == 1) { + // this will fail in getDestination() + headerlen = 6 + 4; + } else if (addressType == 3) { + headerlen = 6 + 1 + (data[4] & 0xff); + } else if (addressType == 4) { + // this will fail in getDestination() + // but future garlicat partial hash lookup possible? + headerlen = 6 + 16; + } else { + throw new IllegalArgumentException("Unknown address type: " + addressType); + } + if (data.length < headerlen) + throw new IllegalArgumentException("Header too short: " + data.length); + + this.header = new byte[headerlen]; + System.arraycopy(this.header, 0, data, 0, headerlen); + } + + private static final byte[] beg = {0,0,0,3,60}; + private static final byte[] end = {'.','b','3','2','.','i','2','p',0,0}; + + /** + * Make a dummy header from a dest, + * for those cases where we want to receive unsolicited datagrams. + * Unused for now. + */ + public SOCKSHeader(Destination dest) { + this.header = new byte[beg.length + 52 + end.length]; + System.arraycopy(this.header, 0, beg, 0, beg.length); + String b32 = Base32.encode(dest.calculateHash().getData()); + System.arraycopy(this.header, beg.length, b32.getBytes(), 0, 52); + System.arraycopy(this.header, beg.length + 52, end, 0, end.length); + } + + public String getHost() { + int addressType = this.header[3]; + if (addressType != 3) + return null; + int namelen = (this.header[4] & 0xff); + byte[] nameBytes = new byte[namelen]; + System.arraycopy(nameBytes, 0, this.header, 5, namelen); + return new String(nameBytes); + } + + public Destination getDestination() { + String name = getHost(); + if (name == null) + return null; + try { + // the naming service does caching (thankfully) + return I2PTunnel.destFromName(name); + } catch (DataFormatException dfe) {} + return null; + } + + public byte[] getBytes() { + return header; + } + + private byte[] header; +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSUDPPort.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSUDPPort.java new file mode 100644 index 000000000..b56c9082f --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSUDPPort.java @@ -0,0 +1,77 @@ +package net.i2p.i2ptunnel.socks; + +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.util.concurrent.ConcurrentHashMap; +import java.util.Map; + +import net.i2p.data.Destination; +import net.i2p.i2ptunnel.udp.*; + +/** + * Implements a UDP port and Socks encapsulation / decapsulation. + * This is for a single port. If there is demuxing for multiple + * ports, it happens outside of here. + * + * TX: + * UDPSource -> SOCKSUDPUnwrapper -> ReplyTracker ( -> I2PSink in SOCKSUDPTunnel) + * + * RX: + * UDPSink <- SOCKSUDPWrapper ( <- MultiSink <- I2PSource in SOCKSUDPTunnel) + * + * The Unwrapper passes headers to the Wrapper through a cache. + * The ReplyTracker passes sinks to MultiSink through a cache. + * + * @author zzz + */ +public class SOCKSUDPPort implements Source, Sink { + + public SOCKSUDPPort(InetAddress host, int port, Map replyMap) { + + // this passes the host and port from UDPUnwrapper to UDPWrapper + Map cache = new ConcurrentHashMap(4); + + // rcv from I2P and send to a port + this.wrapper = new SOCKSUDPWrapper(cache); + this.udpsink = new UDPSink(host, port); + this.wrapper.setSink(this.udpsink); + + // rcv from the same port and send to I2P + DatagramSocket sock = this.udpsink.getSocket(); + this.udpsource = new UDPSource(sock); + this.unwrapper = new SOCKSUDPUnwrapper(cache); + this.udpsource.setSink(this.unwrapper); + this.udptracker = new ReplyTracker(this, replyMap); + this.unwrapper.setSink(this.udptracker); + } + + /** Socks passes this back to the client on the TCP connection */ + public int getPort() { + return this.udpsink.getPort(); + } + + public void setSink(Sink sink) { + this.udptracker.setSink(sink); + } + + public void start() { + // the other Sources don't use start + this.udpsource.start(); + } + + public void stop() { + this.udpsink.stop(); + this.udpsource.stop(); + } + + public void send(Destination from, byte[] data) { + this.wrapper.send(from, data); + } + + + private UDPSink udpsink; + private UDPSource udpsource; + private SOCKSUDPWrapper wrapper; + private SOCKSUDPUnwrapper unwrapper; + private ReplyTracker udptracker; +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSUDPTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSUDPTunnel.java new file mode 100644 index 000000000..0adaa1950 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSUDPTunnel.java @@ -0,0 +1,94 @@ +package net.i2p.i2ptunnel.socks; + +import java.net.InetAddress; +import java.util.concurrent.ConcurrentHashMap; +import java.util.Iterator; +import java.util.Map; + +import net.i2p.data.Destination; +import net.i2p.i2ptunnel.I2PTunnel; +import net.i2p.i2ptunnel.Logging; +import net.i2p.i2ptunnel.udp.*; +import net.i2p.i2ptunnel.udpTunnel.I2PTunnelUDPClientBase; +import net.i2p.util.EventDispatcher; + +/** + * A Datagram Tunnel that can have multiple bidirectional ports on the UDP side. + * + * TX: + * (ReplyTracker in multiple SOCKSUDPPorts -> ) I2PSink + * + * RX: + * (SOCKSUDPWrapper in multiple SOCKSUDPPorts <- ) MultiSink <- I2PSource + * + * The reply from a dest goes to the last SOCKSUDPPort that sent to that dest. + * If multiple ports are talking to a dest at the same time, this isn't + * going to work very well. + * + * @author zzz modded from streamr/StreamrConsumer + */ +public class SOCKSUDPTunnel extends I2PTunnelUDPClientBase { + + /** + * Set up a tunnel with no UDP side yet. + * Use add() for each port. + */ + public SOCKSUDPTunnel(I2PTunnel tunnel) { + super(null, tunnel, tunnel, tunnel); + + this.ports = new ConcurrentHashMap(1); + this.cache = new ConcurrentHashMap(1); + this.demuxer = new MultiSink(this.cache); + setSink(this.demuxer); + } + + + /** @return the UDP port number */ + public int add(InetAddress host, int port) { + SOCKSUDPPort sup = new SOCKSUDPPort(host, port, this.cache); + this.ports.put(Integer.valueOf(sup.getPort()), sup); + sup.setSink(this); + sup.start(); + return sup.getPort(); + } + + public void remove(Integer port) { + SOCKSUDPPort sup = this.ports.remove(port); + if (sup != null) + sup.stop(); + for (Iterator iter = cache.entrySet().iterator(); iter.hasNext();) { + Map.Entry e = (Map.Entry) iter.next(); + if (e.getValue() == sup) + iter.remove(); + } + } + + public final void startRunning() { + super.startRunning(); + // demuxer start() doesn't do anything + startall(); + } + + public boolean close(boolean forced) { + stopall(); + return super.close(forced); + } + + /** you should really add() after startRunning() */ + private void startall() { + } + + private void stopall() { + for (SOCKSUDPPort sup : this.ports.values()) { + sup.stop(); + } + this.ports.clear(); + this.cache.clear(); + } + + + + private Map ports; + private Map cache; + private MultiSink demuxer; +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSUDPUnwrapper.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSUDPUnwrapper.java new file mode 100644 index 000000000..2720b6fd4 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSUDPUnwrapper.java @@ -0,0 +1,59 @@ +package net.i2p.i2ptunnel.socks; + +import java.util.Map; + +import net.i2p.data.Destination; +import net.i2p.i2ptunnel.udp.*; +import net.i2p.util.Log; + +/** + * Strip a SOCKS header off a datagram, convert it to a Destination + * Ref: RFC 1928 + * + * @author zzz + */ +public class SOCKSUDPUnwrapper implements Source, Sink { + private static final Log _log = new Log(SOCKSUDPUnwrapper.class); + + /** + * @param cache put headers here to pass to SOCKSUDPWrapper + */ + public SOCKSUDPUnwrapper(Map cache) { + this.cache = cache; + } + + public void setSink(Sink sink) { + this.sink = sink; + } + + public void start() {} + + /** + * + */ + public void send(Destination ignored_from, byte[] data) { + SOCKSHeader h; + try { + h = new SOCKSHeader(data); + } catch (IllegalArgumentException iae) { + _log.error(iae.toString()); + return; + } + Destination dest = h.getDestination(); + if (dest == null) { + // no, we aren't going to send non-i2p traffic to a UDP outproxy :) + _log.error("Destination not found: " + h.getHost()); + return; + } + + cache.put(dest, h); + + int headerlen = h.getBytes().length; + byte unwrapped[] = new byte[data.length - headerlen]; + System.arraycopy(unwrapped, 0, data, headerlen, unwrapped.length); + this.sink.send(dest, unwrapped); + } + + private Sink sink; + private Map cache; +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSUDPWrapper.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSUDPWrapper.java new file mode 100644 index 000000000..4ec836157 --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSUDPWrapper.java @@ -0,0 +1,49 @@ +package net.i2p.i2ptunnel.socks; + +import java.util.Map; + +import net.i2p.data.Destination; +import net.i2p.i2ptunnel.udp.*; + +/** + * Put a SOCKS header on a datagram + * Ref: RFC 1928 + * + * @author zzz + */ +public class SOCKSUDPWrapper implements Source, Sink { + public SOCKSUDPWrapper(Map cache) { + this.cache = cache; + } + + public void setSink(Sink sink) { + this.sink = sink; + } + + public void start() {} + + /** + * Use the cached header, which should have the host string and port + * + */ + public void send(Destination from, byte[] data) { + if (this.sink == null) + return; + + SOCKSHeader h = cache.get(from); + if (h == null) { + // RFC 1928 says drop + // h = new SOCKSHeader(from); + return; + } + + byte[] header = h.getBytes(); + byte wrapped[] = new byte[header.length + data.length]; + System.arraycopy(wrapped, 0, header, 0, header.length); + System.arraycopy(wrapped, header.length, data, 0, data.length); + this.sink.send(from, wrapped); + } + + private Sink sink; + private Map cache; +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/MultiSource.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/MultiSource.java index 13d9b5520..5c5a08027 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/MultiSource.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/MultiSource.java @@ -27,6 +27,10 @@ public class MultiSource implements Source, Sink { public void start() {} + public void stop() { + this.sinks.clear(); + } + public void send(Destination ignored_from, byte[] data) { for(Destination dest : this.sinks) { this.sink.send(dest, data); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrConsumer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrConsumer.java index 3fc1d881b..02b443443 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrConsumer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrConsumer.java @@ -47,6 +47,7 @@ public class StreamrConsumer extends I2PTunnelUDPClientBase { public boolean close(boolean forced) { // send unsubscribe-message this.pinger.stop(); + this.sink.stop(); return super.close(forced); } @@ -59,6 +60,6 @@ public class StreamrConsumer extends I2PTunnelUDPClientBase { - private Sink sink; + private UDPSink sink; private Pinger pinger; } diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrProducer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrProducer.java index d722c5f95..c3963b6a6 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrProducer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrProducer.java @@ -51,7 +51,8 @@ public class StreamrProducer extends I2PTunnelUDPServerBase { } public boolean close(boolean forced) { - // need some stop() methods in UDPSource and MultiSource + this.server.stop(); + this.multi.stop(); return super.close(forced); } @@ -65,6 +66,6 @@ public class StreamrProducer extends I2PTunnelUDPServerBase { private MultiSource multi; - private Source server; + private UDPSource server; private Sink subscriber; } diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/UDPSink.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/UDPSink.java index 15feba615..d2e8e8924 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/UDPSink.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/UDPSink.java @@ -34,6 +34,8 @@ public class UDPSink implements Sink { } public void send(Destination src, byte[] data) { + // if data.length > this.sock.getSendBufferSize() ... + // create packet DatagramPacket packet = new DatagramPacket(data, data.length, this.remoteHost, this.remotePort); @@ -46,17 +48,18 @@ public class UDPSink implements Sink { } } + public int getPort() { + return this.sock.getLocalPort(); + } + /** to pass to UDPSource constructor */ + public DatagramSocket getSocket() { + return this.sock; + } - - - - - - - - - + public void stop() { + this.sock.close(); + } diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/UDPSource.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/UDPSource.java index c54a984b0..fc1dd5bf2 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/UDPSource.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/UDPSource.java @@ -28,6 +28,13 @@ public class UDPSource implements Source, Runnable { // create thread this.thread = new Thread(this); } + + /** use socket from UDPSink */ + public UDPSource(DatagramSocket sock) { + this.sink = null; + this.sock = sock; + this.thread = new Thread(this); + } public void setSink(Sink sink) { this.sink = sink; @@ -57,13 +64,14 @@ public class UDPSource implements Source, Runnable { //System.out.print("i"); } catch(Exception e) { e.printStackTrace(); + break; } } } - - - + public void stop() { + this.sock.close(); + } From 0d2812db5063bc3c88044dbd480e05599e5f29b1 Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 24 Feb 2009 23:32:38 +0000 Subject: [PATCH 014/688] add standard logging to NativeBigInteger --- .../src/net/i2p/util/NativeBigInteger.java | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/core/java/src/net/i2p/util/NativeBigInteger.java b/core/java/src/net/i2p/util/NativeBigInteger.java index 7a64e24e4..970de52c8 100644 --- a/core/java/src/net/i2p/util/NativeBigInteger.java +++ b/core/java/src/net/i2p/util/NativeBigInteger.java @@ -23,6 +23,9 @@ import freenet.support.CPUInformation.CPUInfo; import freenet.support.CPUInformation.IntelCPUInfo; import freenet.support.CPUInformation.UnknownCPUException; +import net.i2p.I2PAppContext; +import net.i2p.util.Log; + /** *

          BigInteger that takes advantage of the jbigi library for the modPow operation, * which accounts for a massive segment of the processing cost of asymmetric @@ -89,6 +92,9 @@ public class NativeBigInteger extends BigInteger { * do we want to dump some basic success/failure info to stderr during * initialization? this would otherwise use the Log component, but this makes * it easier for other systems to reuse this class + * + * Well, we really want to use Log so if you are one of those "other systems" + * then comment out the I2PAppContext usage below. */ private static final boolean _doLog = System.getProperty("jbigi.dontLog") == null; @@ -401,38 +407,32 @@ public class NativeBigInteger extends BigInteger { boolean loaded = loadGeneric("jbigi"); if (loaded) { _nativeOk = true; - if (_doLog) - System.err.println("INFO: Locally optimized native BigInteger loaded from the library path"); + info("Locally optimized native BigInteger library loaded from the library path"); } else { loaded = loadFromResource("jbigi"); if (loaded) { _nativeOk = true; - if (_doLog) - System.err.println("INFO: Locally optimized native BigInteger loaded from resource"); + info("Locally optimized native BigInteger library loaded from resource"); } else { loaded = loadFromResource(true); if (loaded) { _nativeOk = true; - if (_doLog) - System.err.println("INFO: Optimized native BigInteger library '"+getResourceName(true)+"' loaded from resource"); + info("Optimized native BigInteger library '"+getResourceName(true)+"' loaded from resource"); } else { loaded = loadGeneric(true); if (loaded) { _nativeOk = true; - if (_doLog) - System.err.println("INFO: Optimized native BigInteger library '"+getMiddleName(true)+"' loaded from somewhere in the path"); + info("Optimized native BigInteger library '"+getMiddleName(true)+"' loaded from somewhere in the path"); } else { loaded = loadFromResource(false); if (loaded) { _nativeOk = true; - if (_doLog) - System.err.println("INFO: Non-optimized native BigInteger library '"+getResourceName(false)+"' loaded from resource"); + info("Non-optimized native BigInteger library '"+getResourceName(false)+"' loaded from resource"); } else { loaded = loadGeneric(false); if (loaded) { _nativeOk = true; - if (_doLog) - System.err.println("INFO: Non-optimized native BigInteger library '"+getMiddleName(false)+"' loaded from somewhere in the path"); + info("Non-optimized native BigInteger library '"+getMiddleName(false)+"' loaded from somewhere in the path"); } else { _nativeOk = false; } @@ -442,16 +442,27 @@ public class NativeBigInteger extends BigInteger { } } } - if (_doLog && !_nativeOk) - System.err.println("INFO: Native BigInteger library jbigi not loaded - using pure java"); + if (!_nativeOk) { + warn("Native BigInteger library jbigi not loaded - using pure Java - " + + "poor performance may result - see http://www.i2p2.i2p/jbigi.html for help"); + } }catch(Exception e){ - if (_doLog) { - System.err.println("INFO: Native BigInteger library jbigi not loaded, reason: '"+e.getMessage()+"' - using pure java"); - e.printStackTrace(); - } + warn("Native BigInteger library jbigi not loaded, reason: '"+e.getMessage()+"' - using pure java"); } } + private static void info(String s) { + if(_doLog) + System.err.println("INFO: " + s); + I2PAppContext.getGlobalContext().logManager().getLog(NativeBigInteger.class).info(s); + } + + private static void warn(String s) { + if(_doLog) + System.err.println("WARNING: " + s); + I2PAppContext.getGlobalContext().logManager().getLog(NativeBigInteger.class).warn(s); + } + /** *

          Try loading it from an explictly build jbigi.dll / libjbigi.so first, before * looking into a jbigi.jar for any other libraries.

          From 84bd8274ad627e376c6e2320849d95b64f4d491e Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 25 Feb 2009 00:05:30 +0000 Subject: [PATCH 015/688] * Router: Move addShutdownTask from Router to I2PAppContext so that apps can register more easily --- .../src/org/klomp/snark/SnarkManager.java | 5 ++-- .../net/i2p/router/web/ConfigNetHandler.java | 2 +- .../net/i2p/router/web/ConfigRestartBean.java | 8 +++--- .../i2p/router/web/ConfigServiceHandler.java | 12 ++++----- .../src/net/i2p/router/web/UpdateHandler.java | 2 +- core/java/src/net/i2p/I2PAppContext.java | 12 +++++++++ router/java/src/net/i2p/router/Router.java | 26 ++++++------------- 7 files changed, 34 insertions(+), 33 deletions(-) diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index 7b62ace84..54367af1a 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -81,8 +81,7 @@ public class SnarkManager implements Snark.CompleteListener { I2PAppThread monitor = new I2PAppThread(new DirMonitor(), "Snark DirMonitor"); monitor.setDaemon(true); monitor.start(); - if (_context instanceof RouterContext) - ((RouterContext)_context).router().addShutdownTask(new SnarkManagerShutdown()); + _context.addShutdownTask(new SnarkManagerShutdown()); } /** hook to I2PSnarkUtil for the servlet */ @@ -539,7 +538,7 @@ public class SnarkManager implements Snark.CompleteListener { String announce = info.getAnnounce(); // basic validation of url if ((!announce.startsWith("http://")) || - (announce.indexOf(".i2p/") < 0)) + (announce.indexOf(".i2p/") < 0)) // need to do better than this return "Non-i2p tracker in " + info.getName() + ", deleting it"; List files = info.getFiles(); if ( (files != null) && (files.size() > MAX_FILES_PER_TORRENT) ) { diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java index 0ddcd58a9..a4fe7483e 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java @@ -237,7 +237,7 @@ public class ConfigNetHandler extends FormHandler { private void hiddenSwitch() { // Full restart required to generate new keys - _context.router().addShutdownTask(new UpdateWrapperManagerAndRekeyTask(Router.EXIT_GRACEFUL_RESTART)); + _context.addShutdownTask(new UpdateWrapperManagerAndRekeyTask(Router.EXIT_GRACEFUL_RESTART)); _context.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART); } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigRestartBean.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigRestartBean.java index 75a8108c5..e8eb6b26d 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigRestartBean.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigRestartBean.java @@ -25,20 +25,20 @@ public class ConfigRestartBean { String systemNonce = getNonce(); if ( (nonce != null) && (systemNonce.equals(nonce)) && (action != null) ) { if ("shutdownImmediate".equals(action)) { - ctx.router().addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_HARD)); + ctx.addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_HARD)); //ctx.router().shutdown(Router.EXIT_HARD); // never returns ctx.router().shutdownGracefully(Router.EXIT_HARD); // give the UI time to respond } else if ("cancelShutdown".equals(action)) { ctx.router().cancelGracefulShutdown(); } else if ("restartImmediate".equals(action)) { - ctx.router().addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_HARD_RESTART)); + ctx.addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_HARD_RESTART)); //ctx.router().shutdown(Router.EXIT_HARD_RESTART); // never returns ctx.router().shutdownGracefully(Router.EXIT_HARD_RESTART); // give the UI time to respond } else if ("restart".equals(action)) { - ctx.router().addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_GRACEFUL_RESTART)); + ctx.addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_GRACEFUL_RESTART)); ctx.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART); } else if ("shutdown".equals(action)) { - ctx.router().addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_GRACEFUL)); + ctx.addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_GRACEFUL)); ctx.router().shutdownGracefully(); } } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java index bd3bf7a5e..8d3e5725c 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java @@ -53,31 +53,31 @@ public class ConfigServiceHandler extends FormHandler { if (_action == null) return; if ("Shutdown gracefully".equals(_action)) { - _context.router().addShutdownTask(new UpdateWrapperManagerTask(Router.EXIT_GRACEFUL)); + _context.addShutdownTask(new UpdateWrapperManagerTask(Router.EXIT_GRACEFUL)); _context.router().shutdownGracefully(); addFormNotice("Graceful shutdown initiated"); } else if ("Shutdown immediately".equals(_action)) { - _context.router().addShutdownTask(new UpdateWrapperManagerTask(Router.EXIT_HARD)); + _context.addShutdownTask(new UpdateWrapperManagerTask(Router.EXIT_HARD)); _context.router().shutdown(Router.EXIT_HARD); addFormNotice("Shutdown immediately! boom bye bye bad bwoy"); } else if ("Cancel graceful shutdown".equals(_action)) { _context.router().cancelGracefulShutdown(); addFormNotice("Graceful shutdown cancelled"); } else if ("Graceful restart".equals(_action)) { - _context.router().addShutdownTask(new UpdateWrapperManagerTask(Router.EXIT_GRACEFUL_RESTART)); + _context.addShutdownTask(new UpdateWrapperManagerTask(Router.EXIT_GRACEFUL_RESTART)); _context.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART); addFormNotice("Graceful restart requested"); } else if ("Hard restart".equals(_action)) { - _context.router().addShutdownTask(new UpdateWrapperManagerTask(Router.EXIT_HARD_RESTART)); + _context.addShutdownTask(new UpdateWrapperManagerTask(Router.EXIT_HARD_RESTART)); _context.router().shutdown(Router.EXIT_HARD_RESTART); addFormNotice("Hard restart requested"); } else if ("Rekey and Restart".equals(_action)) { addFormNotice("Rekeying after graceful restart"); - _context.router().addShutdownTask(new UpdateWrapperManagerAndRekeyTask(Router.EXIT_GRACEFUL_RESTART)); + _context.addShutdownTask(new UpdateWrapperManagerAndRekeyTask(Router.EXIT_GRACEFUL_RESTART)); _context.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART); } else if ("Rekey and Shutdown".equals(_action)) { addFormNotice("Rekeying after graceful shutdown"); - _context.router().addShutdownTask(new UpdateWrapperManagerAndRekeyTask(Router.EXIT_GRACEFUL)); + _context.addShutdownTask(new UpdateWrapperManagerAndRekeyTask(Router.EXIT_GRACEFUL)); _context.router().shutdownGracefully(Router.EXIT_GRACEFUL); } else if ("Run I2P on startup".equals(_action)) { installService(); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java index be39da2fd..83495f33e 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java @@ -185,7 +185,7 @@ public class UpdateHandler { } private void restart() { - _context.router().addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_GRACEFUL_RESTART)); + _context.addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_GRACEFUL_RESTART)); _context.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART); } diff --git a/core/java/src/net/i2p/I2PAppContext.java b/core/java/src/net/i2p/I2PAppContext.java index 6b3b0fd5b..f26f74ab7 100644 --- a/core/java/src/net/i2p/I2PAppContext.java +++ b/core/java/src/net/i2p/I2PAppContext.java @@ -23,6 +23,7 @@ import net.i2p.crypto.SessionKeyManager; import net.i2p.data.RoutingKeyGenerator; import net.i2p.stat.StatManager; import net.i2p.util.Clock; +import net.i2p.util.ConcurrentHashSet; import net.i2p.util.FortunaRandomSource; import net.i2p.util.KeyRing; import net.i2p.util.LogManager; @@ -94,6 +95,7 @@ public class I2PAppContext { private volatile boolean _randomInitialized; private volatile boolean _keyGeneratorInitialized; protected volatile boolean _keyRingInitialized; // used in RouterContext + private Set _shutdownTasks; /** @@ -152,6 +154,7 @@ public class I2PAppContext { _elGamalAESEngineInitialized = false; _logManagerInitialized = false; _keyRingInitialized = false; + _shutdownTasks = new ConcurrentHashSet(0); } /** @@ -557,4 +560,13 @@ public class I2PAppContext { _randomInitialized = true; } } + + public void addShutdownTask(Runnable task) { + _shutdownTasks.add(task); + } + + public Set getShutdownTasks() { + return new HashSet(_shutdownTasks); + } + } diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java index 77e4b1968..13e801458 100644 --- a/router/java/src/net/i2p/router/Router.java +++ b/router/java/src/net/i2p/router/Router.java @@ -65,7 +65,6 @@ public class Router { private I2PThread.OOMEventListener _oomListener; private ShutdownHook _shutdownHook; private I2PThread _gracefulShutdownDetector; - private Set _shutdownTasks; public final static String PROP_CONFIG_FILE = "router.configLocation"; @@ -171,7 +170,6 @@ public class Router { watchdog.setDaemon(true); watchdog.start(); - _shutdownTasks = new HashSet(0); } /** @@ -491,13 +489,12 @@ public class Router { */ public void rebuildNewIdentity() { killKeys(); - try { - for (Iterator iter = _shutdownTasks.iterator(); iter.hasNext(); ) { - Runnable task = (Runnable)iter.next(); + for (Runnable task : _context.getShutdownTasks()) { + try { task.run(); + } catch (Throwable t) { + _log.log(Log.CRIT, "Error running shutdown task", t); } - } catch (Throwable t) { - _log.log(Log.CRIT, "Error running shutdown task", t); } // hard and ugly finalShutdown(EXIT_HARD_RESTART); @@ -782,12 +779,6 @@ public class Router { buf.setLength(0); } - public void addShutdownTask(Runnable task) { - synchronized (_shutdownTasks) { - _shutdownTasks.add(task); - } - } - public static final int EXIT_GRACEFUL = 2; public static final int EXIT_HARD = 3; public static final int EXIT_OOM = 10; @@ -800,13 +791,12 @@ public class Router { I2PThread.removeOOMEventListener(_oomListener); // Run the shutdown hooks first in case they want to send some goodbye messages // Maybe we need a delay after this too? - try { - for (Iterator iter = _shutdownTasks.iterator(); iter.hasNext(); ) { - Runnable task = (Runnable)iter.next(); + for (Runnable task : _context.getShutdownTasks()) { + try { task.run(); + } catch (Throwable t) { + _log.log(Log.CRIT, "Error running shutdown task", t); } - } catch (Throwable t) { - _log.log(Log.CRIT, "Error running shutdown task", t); } try { _context.clientManager().shutdown(); } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting down the client manager", t); } try { _context.jobQueue().shutdown(); } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting down the job queue", t); } From d222c7a9986dc7bfdfb9dc1159ae46df9388ba01 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 25 Feb 2009 01:18:38 +0000 Subject: [PATCH 016/688] move dest-to-hash conversion to new helper class --- .../i2p/router/web/ConfigKeyringHandler.java | 21 +---- core/java/src/net/i2p/util/ConvertToHash.java | 76 +++++++++++++++++++ 2 files changed, 79 insertions(+), 18 deletions(-) create mode 100644 core/java/src/net/i2p/util/ConvertToHash.java diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigKeyringHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigKeyringHandler.java index 09f0905bf..b43bc4d1f 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigKeyringHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigKeyringHandler.java @@ -2,9 +2,9 @@ package net.i2p.router.web; import net.i2p.I2PAppContext; import net.i2p.data.DataFormatException; -import net.i2p.data.Destination; import net.i2p.data.Hash; import net.i2p.data.SessionKey; +import net.i2p.util.ConvertToHash; /** * Support additions via B64 Destkey, B64 Desthash, or blahblah.i2p @@ -19,27 +19,12 @@ public class ConfigKeyringHandler extends FormHandler { addFormError("You must enter a destination and a key"); return; } - Hash h = new Hash(); - try { - h.fromBase64(_peer); - } catch (DataFormatException dfe) {} - if (h.getData() == null) { - try { - Destination d = new Destination(); - d.fromBase64(_peer); - h = d.calculateHash(); - } catch (DataFormatException dfe) {} - } - if (h.getData() == null) { - Destination d = _context.namingService().lookup(_peer); - if (d != null) - h = d.calculateHash(); - } + Hash h = ConvertToHash.getHash(_peer); SessionKey sk = new SessionKey(); try { sk.fromBase64(_key); } catch (DataFormatException dfe) {} - if (h.getData() != null && sk.getData() != null) { + if (h != null && h.getData() != null && sk.getData() != null) { _context.keyRing().put(h, sk); addFormNotice("Key for " + h.toBase64() + " added to keyring"); } else { diff --git a/core/java/src/net/i2p/util/ConvertToHash.java b/core/java/src/net/i2p/util/ConvertToHash.java new file mode 100644 index 000000000..087855640 --- /dev/null +++ b/core/java/src/net/i2p/util/ConvertToHash.java @@ -0,0 +1,76 @@ +package net.i2p.util; + +import net.i2p.I2PAppContext; +import net.i2p.data.Base32; +import net.i2p.data.DataFormatException; +import net.i2p.data.Destination; +import net.i2p.data.Hash; + +/** + * Convert any kind of destination String to a hash + * Supported: + * Base64 dest + * Base64 dest.i2p + * Base64 Hash + * Base32 Hash + * Base32 desthash.b32.i2p + * example.i2p + * + * @return null on failure + * + * @author zzz + */ +public class ConvertToHash { + + public static Hash getHash(String peer) { + if (peer == null) + return null; + Hash h = new Hash(); + String peerLC = peer.toLowerCase(); + // b64 hash + if (peer.length() == 44 && !peerLC.endsWith(".i2p")) { + try { + h.fromBase64(peer); + } catch (DataFormatException dfe) {} + } + // b64 dest.i2p + if (h.getData() == null && peer.length() >= 520 && peerLC.endsWith(".i2p")) { + try { + Destination d = new Destination(); + d.fromBase64(peer.substring(0, peer.length() - 4)); + h = d.calculateHash(); + } catch (DataFormatException dfe) {} + } + // b64 dest + if (h.getData() == null && peer.length() >= 516 && !peerLC.endsWith(".i2p")) { + try { + Destination d = new Destination(); + d.fromBase64(peer); + h = d.calculateHash(); + } catch (DataFormatException dfe) {} + } + // b32 hash.b32.i2p + // do this here rather than in naming service so it will work + // even if the leaseset is not found + if (h.getData() == null && peer.length() == 60 && peerLC.endsWith(".b32.i2p")) { + byte[] b = Base32.decode(peer.substring(0, 52)); + if (b != null && b.length == Hash.HASH_LENGTH) + h.setData(b); + } + // b32 hash + if (h.getData() == null && peer.length() == 52 && !peerLC.endsWith(".i2p")) { + byte[] b = Base32.decode(peer); + if (b != null && b.length == Hash.HASH_LENGTH) + h.setData(b); + } + // example.i2p + if (h.getData() == null) { + Destination d = I2PAppContext.getGlobalContext().namingService().lookup(peer); + if (d != null) + h = d.calculateHash(); + } + if (h.getData() == null) + return null; + return h; + } +} From 56473c6b65bccf9b8d410141861ab20a1011c299 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 25 Feb 2009 02:00:13 +0000 Subject: [PATCH 017/688] add reverse lookup by hash --- .../client/naming/HostsTxtNamingService.java | 32 +++++++++++++++++++ .../net/i2p/client/naming/NamingService.java | 2 ++ 2 files changed, 34 insertions(+) diff --git a/core/java/src/net/i2p/client/naming/HostsTxtNamingService.java b/core/java/src/net/i2p/client/naming/HostsTxtNamingService.java index 9fa227f81..054bd9d8f 100644 --- a/core/java/src/net/i2p/client/naming/HostsTxtNamingService.java +++ b/core/java/src/net/i2p/client/naming/HostsTxtNamingService.java @@ -16,8 +16,10 @@ import java.util.Set; import java.util.StringTokenizer; import net.i2p.I2PAppContext; +import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; import net.i2p.data.Destination; +import net.i2p.data.Hash; import net.i2p.util.Log; /** @@ -135,4 +137,34 @@ public class HostsTxtNamingService extends NamingService { } return null; } + + @Override + public String reverseLookup(Hash h) { + List filenames = getFilenames(); + for (int i = 0; i < filenames.size(); i++) { + String hostsfile = (String)filenames.get(i); + Properties hosts = new Properties(); + try { + File f = new File(hostsfile); + if ( (f.exists()) && (f.canRead()) ) { + DataHelper.loadProps(hosts, f, true); + Set keyset = hosts.keySet(); + Iterator iter = keyset.iterator(); + while (iter.hasNext()) { + String host = (String)iter.next(); + String key = hosts.getProperty(host); + try { + Destination destkey = new Destination(); + destkey.fromBase64(key); + if (h.equals(destkey.calculateHash())) + return host; + } catch (DataFormatException dfe) {} + } + } + } catch (Exception ioe) { + _log.error("Error loading hosts file " + hostsfile, ioe); + } + } + return null; + } } diff --git a/core/java/src/net/i2p/client/naming/NamingService.java b/core/java/src/net/i2p/client/naming/NamingService.java index 5b61b1bcf..ee02ec911 100644 --- a/core/java/src/net/i2p/client/naming/NamingService.java +++ b/core/java/src/net/i2p/client/naming/NamingService.java @@ -16,6 +16,7 @@ import java.util.Map; import net.i2p.I2PAppContext; import net.i2p.data.DataFormatException; import net.i2p.data.Destination; +import net.i2p.data.Hash; import net.i2p.util.Log; /** @@ -61,6 +62,7 @@ public abstract class NamingService { * null if no reverse lookup is possible. */ public abstract String reverseLookup(Destination dest); + public String reverseLookup(Hash h) { return null; }; /** * Check if host name is valid Base64 encoded dest and return this From 6648e182aef3909b8927e968640c4a43a663e915 Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 26 Feb 2009 14:45:45 +0000 Subject: [PATCH 018/688] * I2CP Client: Add support for muxing --- .../net/i2p/client/streaming/PacketQueue.java | 12 +- .../src/net/i2p/client/I2PClientImpl.java | 2 +- core/java/src/net/i2p/client/I2PSession.java | 22 ++ .../i2p/client/I2PSessionDemultiplexer.java | 135 ++++++++ .../src/net/i2p/client/I2PSessionImpl.java | 8 +- .../src/net/i2p/client/I2PSessionImpl2.java | 29 +- .../net/i2p/client/I2PSessionListener.java | 4 +- .../net/i2p/client/I2PSessionMuxedImpl.java | 319 ++++++++++++++++++ .../i2p/client/I2PSessionMuxedListener.java | 62 ++++ 9 files changed, 581 insertions(+), 12 deletions(-) create mode 100644 core/java/src/net/i2p/client/I2PSessionDemultiplexer.java create mode 100644 core/java/src/net/i2p/client/I2PSessionMuxedImpl.java create mode 100644 core/java/src/net/i2p/client/I2PSessionMuxedListener.java diff --git a/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java b/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java index a56e7753d..e91cbdb7d 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java @@ -89,9 +89,17 @@ class PacketQueue { // so if we retransmit it will use a new tunnel/lease combo expires = rpe.getNextSendTime() - 500; if (expires > 0) - sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent, expires); + // I2PSessionImpl2 + //sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent, expires); + // I2PSessionMuxedImpl + sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent, expires, + I2PSession.PROTO_STREAMING, I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED); else - sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent); + // I2PSessionImpl2 + //sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent, 0); + // I2PSessionMuxedImpl + sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent, + I2PSession.PROTO_STREAMING, I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED); end = _context.clock().now(); if ( (end-begin > 1000) && (_log.shouldLog(Log.WARN)) ) diff --git a/core/java/src/net/i2p/client/I2PClientImpl.java b/core/java/src/net/i2p/client/I2PClientImpl.java index 4783458a3..5b1b44867 100644 --- a/core/java/src/net/i2p/client/I2PClientImpl.java +++ b/core/java/src/net/i2p/client/I2PClientImpl.java @@ -77,6 +77,6 @@ class I2PClientImpl implements I2PClient { * */ public I2PSession createSession(I2PAppContext context, InputStream destKeyStream, Properties options) throws I2PSessionException { - return new I2PSessionImpl2(context, destKeyStream, options); // thread safe + return new I2PSessionMuxedImpl(context, destKeyStream, options); // thread safe and muxed } } diff --git a/core/java/src/net/i2p/client/I2PSession.java b/core/java/src/net/i2p/client/I2PSession.java index d8c64f222..1776af5c0 100644 --- a/core/java/src/net/i2p/client/I2PSession.java +++ b/core/java/src/net/i2p/client/I2PSession.java @@ -40,6 +40,8 @@ public interface I2PSession { */ public boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException; public boolean sendMessage(Destination dest, byte[] payload, int offset, int size) throws I2PSessionException; + /** See I2PSessionMuxedImpl for details */ + public boolean sendMessage(Destination dest, byte[] payload, int proto, int fromport, int toport) throws I2PSessionException; /** * Like sendMessage above, except the key used and the tags sent are exposed to the @@ -71,6 +73,12 @@ public interface I2PSession { public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException; public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent) throws I2PSessionException; public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire) throws I2PSessionException; + /** See I2PSessionMuxedImpl for details */ + public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, + int proto, int fromport, int toport) throws I2PSessionException; + /** See I2PSessionMuxedImpl for details */ + public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire, + int proto, int fromport, int toport) throws I2PSessionException; /** Receive a message that the router has notified the client about, returning * the payload. @@ -134,4 +142,18 @@ public interface I2PSession { * */ public Destination lookupDest(Hash h) throws I2PSessionException; + + /** See I2PSessionMuxedImpl for details */ + public void addSessionListener(I2PSessionListener lsnr, int proto, int port); + /** See I2PSessionMuxedImpl for details */ + public void addMuxedSessionListener(I2PSessionMuxedListener l, int proto, int port); + /** See I2PSessionMuxedImpl for details */ + public void removeListener(int proto, int port); + + public static final int PORT_ANY = 0; + public static final int PORT_UNSPECIFIED = 0; + public static final int PROTO_ANY = 0; + public static final int PROTO_UNSPECIFIED = 0; + public static final int PROTO_STREAMING = 6; + public static final int PROTO_DATAGRAM = 17; } diff --git a/core/java/src/net/i2p/client/I2PSessionDemultiplexer.java b/core/java/src/net/i2p/client/I2PSessionDemultiplexer.java new file mode 100644 index 000000000..9a1ff42e3 --- /dev/null +++ b/core/java/src/net/i2p/client/I2PSessionDemultiplexer.java @@ -0,0 +1,135 @@ +package net.i2p.client; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.Map; + +import net.i2p.I2PAppContext; +import net.i2p.util.Log; + +/* + * public domain + */ + +/** + * Implement multiplexing with a 1-byte 'protocol' and a two-byte 'port'. + * Listeners register with either addListener() or addMuxedListener(), + * depending on whether they want to hear about the + * protocol, from port, and to port for every received message. + * + * This only calls one listener, not all that apply. + * + * @author zzz + */ +public class I2PSessionDemultiplexer implements I2PSessionMuxedListener { + private Log _log; + private Map _listeners; + + public I2PSessionDemultiplexer(I2PAppContext ctx) { + _log = ctx.logManager().getLog(I2PSessionDemultiplexer.class); + _listeners = new ConcurrentHashMap(); + } + + /** unused */ + public void messageAvailable(I2PSession session, int msgId, long size) {} + + public void messageAvailable(I2PSession session, int msgId, long size, int proto, int fromport, int toport ) { + I2PSessionMuxedListener l = findListener(proto, toport); + if (l != null) + l.messageAvailable(session, msgId, size, proto, fromport, toport); + else { + // no listener, throw it out + _log.error("No listener found for proto: " + proto + " port: " + toport + "msg id: " + msgId + + " from pool of " + _listeners.size() + " listeners"); + try { + session.receiveMessage(msgId); + } catch (I2PSessionException ise) {} + } + } + + public void reportAbuse(I2PSession session, int severity) { + for (I2PSessionMuxedListener l : _listeners.values()) + l.reportAbuse(session, severity); + } + + public void disconnected(I2PSession session) { + for (I2PSessionMuxedListener l : _listeners.values()) + l.disconnected(session); + } + + public void errorOccurred(I2PSession session, String message, Throwable error) { + for (I2PSessionMuxedListener l : _listeners.values()) + l.errorOccurred(session, message, error); + } + + /** + * For those that don't need to hear about the protocol and ports + * in messageAvailable() + * (Streaming lib) + */ + public void addListener(I2PSessionListener l, int proto, int port) { + _listeners.put(key(proto, port), new NoPortsListener(l)); + } + + /** + * For those that do care + * UDP perhaps + */ + public void addMuxedListener(I2PSessionMuxedListener l, int proto, int port) { + _listeners.put(key(proto, port), l); + } + + public void removeListener(int proto, int port) { + _listeners.remove(key(proto, port)); + } + + /** find the one listener that most specifically matches the request */ + private I2PSessionMuxedListener findListener(int proto, int port) { + I2PSessionMuxedListener rv = getListener(proto, port); + if (rv != null) return rv; + if (port != I2PSession.PORT_ANY) { // try any port + rv = getListener(proto, I2PSession.PORT_ANY); + if (rv != null) return rv; + } + if (proto != I2PSession.PROTO_ANY) { // try any protocol + rv = getListener(I2PSession.PROTO_ANY, port); + if (rv != null) return rv; + } + if (proto != I2PSession.PROTO_ANY && port != I2PSession.PORT_ANY) { // try default + rv = getListener(I2PSession.PROTO_ANY, I2PSession.PORT_ANY); + } + return rv; + } + + private I2PSessionMuxedListener getListener(int proto, int port) { + return _listeners.get(key(proto, port)); + } + + private Integer key(int proto, int port) { + return Integer.valueOf(((port << 8) & 0xffff00) | proto); + } + + /** for those that don't care about proto and ports */ + private static class NoPortsListener implements I2PSessionMuxedListener { + private I2PSessionListener _l; + + public NoPortsListener(I2PSessionListener l) { + _l = l; + } + + public void messageAvailable(I2PSession session, int msgId, long size) { + throw new IllegalArgumentException("no"); + } + public void messageAvailable(I2PSession session, int msgId, long size, int proto, int fromport, int toport) { + _l.messageAvailable(session, msgId, size); + } + public void reportAbuse(I2PSession session, int severity) { + _l.reportAbuse(session, severity); + } + public void disconnected(I2PSession session) { + _l.disconnected(session); + } + public void errorOccurred(I2PSession session, String message, Throwable error) { + _l.errorOccurred(session, message, error); + } + } +} diff --git a/core/java/src/net/i2p/client/I2PSessionImpl.java b/core/java/src/net/i2p/client/I2PSessionImpl.java index 00da88aa2..0e13f2c56 100644 --- a/core/java/src/net/i2p/client/I2PSessionImpl.java +++ b/core/java/src/net/i2p/client/I2PSessionImpl.java @@ -77,12 +77,12 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa protected OutputStream _out; /** who we send events to */ - private I2PSessionListener _sessionListener; + protected I2PSessionListener _sessionListener; /** class that generates new messages */ protected I2CPMessageProducer _producer; /** map of Long --> MessagePayloadMessage */ - private Map _availableMessages; + protected Map _availableMessages; protected I2PClientMessageHandlerMap _handlerMap; @@ -366,14 +366,14 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa } SimpleScheduler.getInstance().addEvent(new VerifyUsage(mid), 30*1000); } - private class VerifyUsage implements SimpleTimer.TimedEvent { + protected class VerifyUsage implements SimpleTimer.TimedEvent { private Long _msgId; public VerifyUsage(Long id) { _msgId = id; } public void timeReached() { MessagePayloadMessage removed = _availableMessages.remove(_msgId); if (removed != null && !isClosed()) - _log.log(Log.CRIT, "Message NOT removed! id=" + _msgId + ": " + removed); + _log.error("Message NOT removed! id=" + _msgId + ": " + removed); } } diff --git a/core/java/src/net/i2p/client/I2PSessionImpl2.java b/core/java/src/net/i2p/client/I2PSessionImpl2.java index 56ef88974..9abce4b72 100644 --- a/core/java/src/net/i2p/client/I2PSessionImpl2.java +++ b/core/java/src/net/i2p/client/I2PSessionImpl2.java @@ -93,7 +93,7 @@ class I2PSessionImpl2 extends I2PSessionImpl { * set to false. */ private static final int DONT_COMPRESS_SIZE = 66; - private boolean shouldCompress(int size) { + protected boolean shouldCompress(int size) { if (size <= DONT_COMPRESS_SIZE) return false; String p = getOptions().getProperty("i2cp.gzip"); @@ -102,12 +102,35 @@ class I2PSessionImpl2 extends I2PSessionImpl { return SHOULD_COMPRESS; } + public void addSessionListener(I2PSessionListener lsnr, int proto, int port) { + throw new IllegalArgumentException("Use MuxedImpl"); + } + public void addMuxedSessionListener(I2PSessionMuxedListener l, int proto, int port) { + throw new IllegalArgumentException("Use MuxedImpl"); + } + public void removeListener(int proto, int port) { + throw new IllegalArgumentException("Use MuxedImpl"); + } + public boolean sendMessage(Destination dest, byte[] payload, int proto, int fromport, int toport) throws I2PSessionException { + throw new IllegalArgumentException("Use MuxedImpl"); + } + public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, + int proto, int fromport, int toport) throws I2PSessionException { + throw new IllegalArgumentException("Use MuxedImpl"); + } + public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire, + int proto, int fromport, int toport) throws I2PSessionException { + throw new IllegalArgumentException("Use MuxedImpl"); + } + @Override public boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException { return sendMessage(dest, payload, 0, payload.length); } public boolean sendMessage(Destination dest, byte[] payload, int offset, int size) throws I2PSessionException { - return sendMessage(dest, payload, offset, size, new SessionKey(), new HashSet(64), 0); + // we don't do end-to-end crypto any more + //return sendMessage(dest, payload, offset, size, new SessionKey(), new HashSet(64), 0); + return sendMessage(dest, payload, offset, size, null, null, 0); } @Override @@ -173,7 +196,7 @@ class I2PSessionImpl2 extends I2PSessionImpl { private static final int NUM_TAGS = 50; - private boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent, long expires) + protected boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent, long expires) throws I2PSessionException { SessionKey key = null; SessionKey newKey = null; diff --git a/core/java/src/net/i2p/client/I2PSessionListener.java b/core/java/src/net/i2p/client/I2PSessionListener.java index 4c78c6527..740ebeeab 100644 --- a/core/java/src/net/i2p/client/I2PSessionListener.java +++ b/core/java/src/net/i2p/client/I2PSessionListener.java @@ -20,7 +20,7 @@ public interface I2PSessionListener { * size # of bytes. * @param session session to notify * @param msgId message number available - * @param size size of the message + * @param size size of the message - why it's a long and not an int is a mystery */ void messageAvailable(I2PSession session, int msgId, long size); @@ -42,4 +42,4 @@ public interface I2PSessionListener { * */ void errorOccurred(I2PSession session, String message, Throwable error); -} \ No newline at end of file +} diff --git a/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java b/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java new file mode 100644 index 000000000..1cd7b072a --- /dev/null +++ b/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java @@ -0,0 +1,319 @@ +package net.i2p.client; + +/* + * public domain + */ + +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; + +import net.i2p.I2PAppContext; +import net.i2p.data.DataHelper; +import net.i2p.data.Destination; +import net.i2p.data.SessionKey; +import net.i2p.data.SessionTag; +import net.i2p.data.i2cp.MessagePayloadMessage; +import net.i2p.util.Log; +import net.i2p.util.SimpleScheduler; + +/** + * I2PSession with protocol and ports + * + * Streaming lib has been modified to send I2PSession.PROTO_STREAMING but + * still receives all. It sends with fromPort and toPort = 0, and receives on all ports. + * + * No datagram apps have been modified yet. + + * Therefore the compatibility situation is as follows: + * + * Compatibility: + * old streaming -> new streaming: sends proto anything, rcvs proto anything + * new streaming -> old streaming: sends PROTO_STREAMING, ignores rcvd proto + * old datagram -> new datagram: sends proto anything, rcvs proto anything + * new datagram -> old datagram: sends PROTO_DATAGRAM, ignores rcvd proto + * In all the above cases, streaming and datagram receive traffic for the other + * protocol, same as before. + * + * old datagram -> new muxed: doesn't work because the old sends proto 0 but the udp side + * of the mux registers with PROTO_DATAGRAM, so the datagrams + * go to the streaming side, same as before. + * old streaming -> new muxed: works + * + * Typical Usage: + * Streaming + datagrams: + * I2PSocketManager sockMgr = getSocketManager(); + * I2PSession session = sockMgr.getSession(); + * session.addMuxedSessionListener(myI2PSessionMuxedListener, I2PSession.PROTO_DATAGRAM, I2PSession.PORT_ANY); + * * or * + * session.addSessionListener(myI2PSessionListener, I2PSession.PROTO_DATAGRAM, I2PSession.PORT_ANY); + * session.sendMessage(dest, payload, I2PSession.PROTO_DATAGRAM, fromPort, toPort); + * + * Datagrams only, with multiple ports: + * I2PClient client = I2PClientFactory.createClient(); + * ... + * I2PSession session = client.createSession(...); + * session.addMuxedSessionListener(myI2PSessionMuxedListener, I2PSession.PROTO_DATAGRAM, I2PSession.PORT_ANY); + * * or * + * session.addSessionListener(myI2PSessionListener, I2PSession.PROTO_DATAGRAM, I2PSession.PORT_ANY); + * session.sendMessage(dest, payload, I2PSession.PROTO_DATAGRAM, fromPort, toPort); + * + * Multiple streaming ports: + * Needs some streaming lib hacking + * + * @author zzz + */ +class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession { + private I2PSessionDemultiplexer _demultiplexer; + + public I2PSessionMuxedImpl(I2PAppContext ctx, InputStream destKeyStream, Properties options) throws I2PSessionException { + super(ctx, destKeyStream, options); + // also stored in _sessionListener but we keep it in _demultipexer + // as well so we don't have to keep casting + _demultiplexer = new I2PSessionDemultiplexer(ctx); + super.setSessionListener(_demultiplexer); + // discards the one in super(), sorry about that... (no it wasn't started yet) + _availabilityNotifier = new MuxedAvailabilityNotifier(); + } + + /** listen on all protocols and ports */ + @Override + public void setSessionListener(I2PSessionListener lsnr) { + _demultiplexer.addListener(lsnr, PROTO_ANY, PORT_ANY); + } + + /** + * Listen on specified protocol and port. + * + * An existing listener with the same proto and port is replaced. + * Only the listener with the best match is called back for each message. + * + * @param proto 1-254 or PROTO_ANY for all; recommended: + * I2PSession.PROTO_STREAMING + * I2PSession.PROTO_DATAGRAM + * 255 disallowed + * @param port 1-65535 or PORT_ANY for all + */ + public void addSessionListener(I2PSessionListener lsnr, int proto, int port) { + _demultiplexer.addListener(lsnr, proto, port); + } + + /** + * Listen on specified protocol and port, and receive notification + * of proto, fromPort, and toPort for every message. + * @param proto 1-254 or 0 for all; 255 disallowed + * @param port 1-65535 or 0 for all + */ + public void addMuxedSessionListener(I2PSessionMuxedListener l, int proto, int port) { + _demultiplexer.addMuxedListener(l, proto, port); + } + + /** removes the specified listener (only) */ + public void removeListener(int proto, int port) { + _demultiplexer.removeListener(proto, port); + } + + @Override + public boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException { + return sendMessage(dest, payload, 0, 0, null, null, 0, PROTO_UNSPECIFIED, PORT_UNSPECIFIED, PORT_UNSPECIFIED); + } + + @Override + public boolean sendMessage(Destination dest, byte[] payload, int proto, int fromport, int toport) throws I2PSessionException { + return sendMessage(dest, payload, 0, 0, null, null, 0, proto, fromport, toport); + } + + @Override + public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, + SessionKey keyUsed, Set tagsSent, long expires) + throws I2PSessionException { + return sendMessage(dest, payload, offset, size, keyUsed, tagsSent, 0, PROTO_UNSPECIFIED, PORT_UNSPECIFIED, PORT_UNSPECIFIED); + } + + @Override + public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, + int proto, int fromport, int toport) throws I2PSessionException { + return sendMessage(dest, payload, offset, size, keyUsed, tagsSent, 0, proto, fromport, toport); + } + + /** + * @param proto 1-254 or 0 for unset; recommended: + * I2PSession.PROTO_UNSPECIFIED + * I2PSession.PROTO_STREAMING + * I2PSession.PROTO_DATAGRAM + * 255 disallowed + * @param fromport 1-65535 or 0 for unset + * @param toport 1-65535 or 0 for unset + */ + public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, + SessionKey keyUsed, Set tagsSent, long expires, + int proto, int fromPort, int toPort) + throws I2PSessionException { + if (isClosed()) throw new I2PSessionException("Already closed"); + updateActivity(); + + boolean sc = shouldCompress(size); + if (sc) + payload = DataHelper.compress(payload, offset, size); + else + payload = DataHelper.compress(payload, offset, size, DataHelper.NO_COMPRESSION); + + setProto(payload, proto); + setFromPort(payload, fromPort); + setToPort(payload, toPort); + + _context.statManager().addRateData("i2cp.tx.msgCompressed", payload.length, 0); + _context.statManager().addRateData("i2cp.tx.msgExpanded", size, 0); + return sendBestEffort(dest, payload, keyUsed, tagsSent, expires); + } + + /** + * Receive a payload message and let the app know its available + */ + @Override + public void addNewMessage(MessagePayloadMessage msg) { + Long mid = new Long(msg.getMessageId()); + _availableMessages.put(mid, msg); + long id = msg.getMessageId(); + byte data[] = msg.getPayload().getUnencryptedData(); + if ((data == null) || (data.length <= 0)) { + if (_log.shouldLog(Log.CRIT)) + _log.log(Log.CRIT, getPrefix() + "addNewMessage of a message with no unencrypted data", + new Exception("Empty message")); + return; + } + int size = data.length; + if (size < 10) { + _log.error(getPrefix() + "length too short for gzip header: " + size); + return; + } + ((MuxedAvailabilityNotifier)_availabilityNotifier).available(id, size, getProto(msg), + getFromPort(msg), getToPort(msg)); + SimpleScheduler.getInstance().addEvent(new VerifyUsage(mid), 30*1000); + } + + protected class MuxedAvailabilityNotifier extends AvailabilityNotifier { + private LinkedBlockingQueue _msgs; + private boolean _alive; + private static final int POISON_SIZE = -99999; + + public MuxedAvailabilityNotifier() { + _msgs = new LinkedBlockingQueue(); + } + + public void stopNotifying() { + _msgs.clear(); + if (_alive) { + _alive = false; + try { + _msgs.put(new MsgData(0, POISON_SIZE, 0, 0, 0)); + } catch (InterruptedException ie) {} + } + } + + /** unused */ + public void available(long msgId, int size) { throw new IllegalArgumentException("no"); } + + public void available(long msgId, int size, int proto, int fromPort, int toPort) { + try { + _msgs.put(new MsgData((int)(msgId & 0xffffffff), size, proto, fromPort, toPort)); + } catch (InterruptedException ie) {} + } + + public void run() { + _alive = true; + while (true) { + MsgData msg; + try { + msg = _msgs.take(); + } catch (InterruptedException ie) { + continue; + } + if (msg.size == POISON_SIZE) + break; + try { + _demultiplexer.messageAvailable(I2PSessionMuxedImpl.this, msg.id, + msg.size, msg.proto, msg.fromPort, msg.toPort); + } catch (Exception e) { + _log.error("Error notifying app of message availability"); + } + } + } + } + + /** let's keep this simple */ + private static class MsgData { + public int id, size, proto, fromPort, toPort; + public MsgData(int i, int s, int p, int f, int t) { + id = i; + size = s; + proto = p; + fromPort = f; + toPort = t; + } + } + + /** + * No, we couldn't put any protocol byte in front of everything and + * keep backward compatibility. But there are several bytes that + * are unused AND unchecked in the gzip header in releases <= 0.7. + * So let's use 5 of them for a protocol and two 2-byte ports. + * + * Following are all the methods to hide the + * protocol, fromPort, and toPort in the gzip header + * + * The fields used are all ignored on receive in ResettableGzipInputStream + * + * See also ResettableGzipOutputStream. + * Ref: RFC 1952 + * + */ + + /** OS byte in gzip header */ + private static final int PROTO_BYTE = 9; + + /** Upper two bytes of MTIME in gzip header */ + private static final int FROMPORT_BYTES = 4; + + /** Lower two bytes of MTIME in gzip header */ + private static final int TOPORT_BYTES = 6; + + /** Non-muxed sets the OS byte to 0xff */ + private static int getProto(MessagePayloadMessage msg) { + int rv = getByte(msg, PROTO_BYTE) & 0xff; + return rv == 0xff ? PROTO_UNSPECIFIED : rv; + } + + /** Non-muxed sets the MTIME bytes to 0 */ + private static int getFromPort(MessagePayloadMessage msg) { + return (((getByte(msg, FROMPORT_BYTES) & 0xff) << 8) | + (getByte(msg, FROMPORT_BYTES + 1) & 0xff)); + } + + /** Non-muxed sets the MTIME bytes to 0 */ + private static int getToPort(MessagePayloadMessage msg) { + return (((getByte(msg, TOPORT_BYTES) & 0xff) << 8) | + (getByte(msg, TOPORT_BYTES + 1) & 0xff)); + } + + private static int getByte(MessagePayloadMessage msg, int i) { + return msg.getPayload().getUnencryptedData()[i] & 0xff; + } + + private static void setProto(byte[] payload, int p) { + payload[PROTO_BYTE] = (byte) (p & 0xff); + } + + private static void setFromPort(byte[] payload, int p) { + payload[FROMPORT_BYTES] = (byte) ((p >> 8) & 0xff); + payload[FROMPORT_BYTES + 1] = (byte) (p & 0xff); + } + + private static void setToPort(byte[] payload, int p) { + payload[TOPORT_BYTES] = (byte) ((p >> 8) & 0xff); + payload[TOPORT_BYTES + 1] = (byte) (p & 0xff); + } +} diff --git a/core/java/src/net/i2p/client/I2PSessionMuxedListener.java b/core/java/src/net/i2p/client/I2PSessionMuxedListener.java new file mode 100644 index 000000000..118dc75ca --- /dev/null +++ b/core/java/src/net/i2p/client/I2PSessionMuxedListener.java @@ -0,0 +1,62 @@ +package net.i2p.client; + +/* + * public domain + */ + +/** + * Define a means for the router to asynchronously notify the client that a + * new message is available or the router is under attack. + * + * @author zzz extends I2PSessionListener + */ +public interface I2PSessionMuxedListener extends I2PSessionListener { + + /** + * Will be called only if you register via + * setSessionListener() or addSessionListener(). + * And if you are doing that, just use I2PSessionListener. + * + * If you register via addSessionListener(), + * this will be called only for the proto(s) and toport(s) you register for. + * + * @param session session to notify + * @param msgId message number available + * @param size size of the message - why it's a long and not an int is a mystery + */ + void messageAvailable(I2PSession session, int msgId, long size); + + /** + * Instruct the client that the given session has received a message + * + * Will be called only if you register via addMuxedSessionListener(). + * Will be called only for the proto(s) and toport(s) you register for. + * + * @param session session to notify + * @param msgId message number available + * @param size size of the message - why it's a long and not an int is a mystery + * @param proto 1-254 or 0 for unspecified + * @param fromport 1-65535 or 0 for unspecified + * @param toport 1-65535 or 0 for unspecified + */ + void messageAvailable(I2PSession session, int msgId, long size, int proto, int fromport, int toport); + + /** Instruct the client that the session specified seems to be under attack + * and that the client may wish to move its destination to another router. + * @param session session to report abuse to + * @param severity how bad the abuse is + */ + void reportAbuse(I2PSession session, int severity); + + /** + * Notify the client that the session has been terminated + * + */ + void disconnected(I2PSession session); + + /** + * Notify the client that some error occurred + * + */ + void errorOccurred(I2PSession session, String message, Throwable error); +} From 3733b78ccf727b7dd4090b5b884d37c68285bfc2 Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 27 Feb 2009 21:24:40 +0000 Subject: [PATCH 019/688] * I2PTunnelUDPClientBase: Fix client close, client target host * I2CP Mux: Fix UDP sends --- .../java/src/net/i2p/i2ptunnel/TunnelController.java | 9 ++++++++- .../net/i2p/i2ptunnel/streamr/StreamrConsumer.java | 1 + .../net/i2p/i2ptunnel/streamr/StreamrProducer.java | 1 + .../src/net/i2p/i2ptunnel/udp/I2PSinkAnywhere.java | 3 ++- .../i2ptunnel/udpTunnel/I2PTunnelUDPClientBase.java | 12 ++---------- .../i2ptunnel/udpTunnel/I2PTunnelUDPServerBase.java | 3 +-- apps/i2ptunnel/jsp/editServer.jsp | 6 ++++-- .../java/src/net/i2p/client/I2PSessionMuxedImpl.java | 5 +++-- 8 files changed, 22 insertions(+), 18 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java index 6c5fa4eb9..9cb3762ac 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java @@ -213,8 +213,15 @@ public class TunnelController implements Logging { _tunnel.runStreamrClient(new String[] { targetHost, targetPort, dest }, this); } - /** Streamr server is a UDP client, use the targetPort field for listenPort */ + /** + * Streamr server is a UDP client, use the targetPort field for listenPort + * and the targetHost field for the listenOnInterface + */ private void startStreamrServer() { + String listenOn = getTargetHost(); + if ( (listenOn != null) && (listenOn.length() > 0) ) { + _tunnel.runListenOn(new String[] { listenOn }, this); + } String listenPort = getTargetPort(); String privKeyFile = getPrivKeyFile(); _tunnel.runStreamrServer(new String[] { listenPort, privKeyFile }, this); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrConsumer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrConsumer.java index 02b443443..87ea0eefe 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrConsumer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrConsumer.java @@ -42,6 +42,7 @@ public class StreamrConsumer extends I2PTunnelUDPClientBase { super.startRunning(); // send subscribe-message this.pinger.start(); + l.log("Streamr client ready"); } public boolean close(boolean forced) { diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrProducer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrProducer.java index c3963b6a6..b801cb94f 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrProducer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/StreamrProducer.java @@ -48,6 +48,7 @@ public class StreamrProducer extends I2PTunnelUDPServerBase { public final void startRunning() { super.startRunning(); this.server.start(); + l.log("Streamr server ready"); } public boolean close(boolean forced) { diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSinkAnywhere.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSinkAnywhere.java index 09385d46f..58c5bfda4 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSinkAnywhere.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSinkAnywhere.java @@ -27,7 +27,8 @@ public class I2PSinkAnywhere implements Sink { this.raw = raw; // create maker - this.maker = new I2PDatagramMaker(this.sess); + if (!raw) + this.maker = new I2PDatagramMaker(this.sess); } /** @param to - where it's going */ diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udpTunnel/I2PTunnelUDPClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udpTunnel/I2PTunnelUDPClientBase.java index 0123be6ea..c92da6ae8 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udpTunnel/I2PTunnelUDPClientBase.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udpTunnel/I2PTunnelUDPClientBase.java @@ -141,9 +141,6 @@ public abstract class I2PTunnelUDPClientBase extends I2PTunnelTask implements So } else { _i2pSink = new I2PSinkAnywhere(_session, false); } - - //configurePool(tunnel); - } /** @@ -165,13 +162,7 @@ public abstract class I2PTunnelUDPClientBase extends I2PTunnelTask implements So startRunning = true; startLock.notify(); } - - if (open && listenerReady) { - notifyEvent("openBaseClientResult", "ok"); - } else { - l.log("Error listening - please see the logs!"); - notifyEvent("openBaseClientResult", "error"); - } + open = true; } /** @@ -187,6 +178,7 @@ public abstract class I2PTunnelUDPClientBase extends I2PTunnelTask implements So } catch (I2PSessionException ise) {} } l.log("Closing client " + toString()); + open = false; return true; } diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udpTunnel/I2PTunnelUDPServerBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udpTunnel/I2PTunnelUDPServerBase.java index fe129fb13..8dcd66a36 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udpTunnel/I2PTunnelUDPServerBase.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udpTunnel/I2PTunnelUDPServerBase.java @@ -41,7 +41,7 @@ public class I2PTunnelUDPServerBase extends I2PTunnelTask implements Source, Sin private static volatile long __serverId = 0; - private Logging l; + protected Logging l; private static final long DEFAULT_READ_TIMEOUT = -1; // 3*60*1000; /** default timeout to 3 minutes - override if desired */ @@ -137,7 +137,6 @@ public class I2PTunnelUDPServerBase extends I2PTunnelTask implements Source, Sin start(); //} - l.log("Ready!"); notifyEvent("openServerResult", "ok"); open = true; } diff --git a/apps/i2ptunnel/jsp/editServer.jsp b/apps/i2ptunnel/jsp/editServer.jsp index 0e9f9c0ca..70a9df9f7 100644 --- a/apps/i2ptunnel/jsp/editServer.jsp +++ b/apps/i2ptunnel/jsp/editServer.jsp @@ -88,14 +88,16 @@ <% } %> - <% if (!"streamrserver".equals(tunnelType)) { %>
          - <% } // !streamrserver %>
          -
          - -
          -
          - - class="tickbox" /> -
          -
          - - class="tickbox" /> -
          -
          - - -
          - -
          -
          -
          -
          +
          + +
          +
          + + class="tickbox" /> +
          +
          + + +
          class="tickbox" /> + Enable + class="tickbox" /> + Disable +
          +
          +
          + + +
          + +
          +
          +
          + +
          + +
          +
          + + class="tickbox" /> +
          +
          + + +
          + +
          +
          +
          +
          - + (Tunnel must be stopped first)
          From 59b624a4a4fb9fd5857b424c46faeab6bf6b16fe Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 1 Mar 2009 20:44:01 +0000 Subject: [PATCH 024/688] add reasonable privkey file name default --- apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java index e68d140c5..941edd0bd 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java @@ -60,8 +60,9 @@ public class EditBean extends IndexBean { TunnelController tun = getController(tunnel); if (tun != null && tun.getPrivKeyFile() != null) return tun.getPrivKeyFile(); - else - return ""; + if (tunnel < 0) + tunnel = _group.getControllers().size(); + return "i2ptunnel" + tunnel + "-privKeys.dat"; } public boolean startAutomatically(int tunnel) { From c455fa6309fb7c66e9159d99d9f0e162010cddfa Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 1 Mar 2009 20:45:16 +0000 Subject: [PATCH 025/688] * OCMOSJ: - Change from 5% reply requests to at least once per minute, in hopes of reducing IRC drops - More clean up of the cache cleaning --- .../OutboundClientMessageOneShotJob.java | 95 +++++++++++-------- 1 file changed, 58 insertions(+), 37 deletions(-) diff --git a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java index 0e858ef77..20d69ea73 100644 --- a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java +++ b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java @@ -103,6 +103,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { private static final Object _initializeLock = new Object(); private static boolean _initialized = false; private static final int CLEAN_INTERVAL = 5*60*1000; + private static final int REPLY_REQUEST_INTERVAL = 60*1000; /** * Send the sucker @@ -212,7 +213,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { * * Key the cache on the source+dest pair. */ - private static HashMap _leaseSetCache = new HashMap(); + private static HashMap _leaseSetCache = new HashMap(); private LeaseSet getReplyLeaseSet(boolean force) { LeaseSet newLS = getContext().netDb().lookupLeaseSetLocally(_from.calculateHash()); if (newLS == null) @@ -247,7 +248,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { long now = getContext().clock().now(); synchronized (_leaseSetCache) { if (!force) { - LeaseSet ls = (LeaseSet) _leaseSetCache.get(hashPair()); + LeaseSet ls = _leaseSetCache.get(hashPair()); if (ls != null) { if (ls.equals(newLS)) { // still good, send it 10% of the time @@ -312,7 +313,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { * lease). * */ - private static HashMap _leaseCache = new HashMap(); + private static HashMap _leaseCache = new HashMap(); private boolean getNextLease() { _leaseSet = getContext().netDb().lookupLeaseSetLocally(_to.calculateHash()); if (_leaseSet == null) { @@ -325,7 +326,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { // Use the same lease if it's still good // Even if _leaseSet changed, _leaseSet.getEncryptionKey() didn't... synchronized (_leaseCache) { - _lease = (Lease) _leaseCache.get(hashPair()); + _lease = _leaseCache.get(hashPair()); if (_lease != null) { // if outbound tunnel length == 0 && lease.firsthop.isBacklogged() don't use it ?? if (!_lease.isExpired(Router.CLOCK_FUDGE_FACTOR)) { @@ -446,6 +447,14 @@ public class OutboundClientMessageOneShotJob extends JobImpl { } } + /** + * This cache is used to ensure that we request a reply every so often. + * Hopefully this allows the router to recognize a failed tunnel and switch, + * before upper layers like streaming lib fail, even for low-bandwidth + * connections like IRC. + */ + private static HashMap _lastReplyRequestCache = new HashMap(); + /** * Send the message to the specified tunnel by creating a new garlic message containing * the (already created) payload clove as well as a new delivery status message. This garlic @@ -456,18 +465,27 @@ public class OutboundClientMessageOneShotJob extends JobImpl { */ private void send() { if (_finished) return; - if (getContext().clock().now() >= _overallExpiration) { + long now = getContext().clock().now(); + if (now >= _overallExpiration) { dieFatal(); return; } int existingTags = GarlicMessageBuilder.estimateAvailableTags(getContext(), _leaseSet.getEncryptionKey()); _outTunnel = selectOutboundTunnel(_to); + // boolean wantACK = _wantACK || existingTags <= 30 || getContext().random().nextInt(100) < 5; // what's the point of 5% random? possible improvements or replacements: - // - wantACK if we changed their inbound lease (getNextLease() sets _wantACK) - // - wantACK if we changed our outbound tunnel (selectOutboundTunnel() sets _wantACK) - // - wantACK if we haven't in last 1m (requires a new static cache probably) - boolean wantACK = _wantACK || existingTags <= 30 || getContext().random().nextInt(100) < 5; + // DONE (getNextLease() is called before this): wantACK if we changed their inbound lease (getNextLease() sets _wantACK) + // DONE (selectOutboundTunnel() moved above here): wantACK if we changed our outbound tunnel (selectOutboundTunnel() sets _wantACK) + // DONE (added new cache): wantACK if we haven't in last 1m (requires a new static cache probably) + boolean wantACK; + synchronized (_lastReplyRequestCache) { + Long lastSent = _lastReplyRequestCache.get(hashPair()); + wantACK = _wantACK || existingTags <= 30 || + lastSent == null || lastSent.longValue() < now - REPLY_REQUEST_INTERVAL; + if (wantACK) + _lastReplyRequestCache.put(hashPair(), Long.valueOf(now)); + } PublicKey key = _leaseSet.getEncryptionKey(); SessionKey sessKey = new SessionKey(); @@ -501,7 +519,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { // we dont receive the reply? hmm...) if (_log.shouldLog(Log.WARN)) _log.warn(getJobId() + ": Unable to create the garlic message (no tunnels left or too lagged) to " + _toString); - getContext().statManager().addRateData("client.dispatchNoTunnels", getContext().clock().now() - _start, 0); + getContext().statManager().addRateData("client.dispatchNoTunnels", now - _start, 0); dieFatal(); return; } @@ -539,12 +557,12 @@ public class OutboundClientMessageOneShotJob extends JobImpl { } else { if (_log.shouldLog(Log.WARN)) _log.warn(getJobId() + ": Could not find any outbound tunnels to send the payload through... this might take a while"); - getContext().statManager().addRateData("client.dispatchNoTunnels", getContext().clock().now() - _start, 0); + getContext().statManager().addRateData("client.dispatchNoTunnels", now - _start, 0); dieFatal(); } _clientMessage = null; _clove = null; - getContext().statManager().addRateData("client.dispatchPrepareTime", getContext().clock().now() - _start, 0); + getContext().statManager().addRateData("client.dispatchPrepareTime", now - _start, 0); if (!wantACK) getContext().statManager().addRateData("client.dispatchNoACK", 1, 0); } @@ -582,7 +600,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { /** * This is the place where we make I2P go fast. * - * We have four static caches. + * We have five static caches. * - The LeaseSet cache is used to decide whether to bundle our own leaseset, * which minimizes overhead. * - The Lease cache is used to persistently send to the same lease for the destination, @@ -590,6 +608,8 @@ public class OutboundClientMessageOneShotJob extends JobImpl { * - The Tunnel and BackloggedTunnel caches are used to persistently use the same outbound tunnel * for the same destination, * which keeps the streaming lib happy by minimizing out-of-order delivery. + * - The last reply requested cache ensures that a reply is requested every so often, + * so that failed tunnels are recognized. * */ @@ -629,17 +649,17 @@ public class OutboundClientMessageOneShotJob extends JobImpl { } if (_lease != null) { synchronized(_leaseCache) { - Lease l = (Lease) _leaseCache.get(key); + Lease l = _leaseCache.get(key); if (l != null && l.equals(_lease)) _leaseCache.remove(key); } } if (_outTunnel != null) { synchronized(_tunnelCache) { - TunnelInfo t =(TunnelInfo) _backloggedTunnelCache.get(key); + TunnelInfo t = _backloggedTunnelCache.get(key); if (t != null && t.equals(_outTunnel)) _backloggedTunnelCache.remove(key); - t = (TunnelInfo) _tunnelCache.get(key); + t = _tunnelCache.get(key); if (t != null && t.equals(_outTunnel)) _tunnelCache.remove(key); } @@ -652,17 +672,12 @@ public class OutboundClientMessageOneShotJob extends JobImpl { */ private static void cleanLeaseSetCache(RouterContext ctx, HashMap tc) { long now = ctx.clock().now(); - List deleteList = new ArrayList(); for (Iterator iter = tc.entrySet().iterator(); iter.hasNext(); ) { Map.Entry entry = (Map.Entry)iter.next(); String k = (String) entry.getKey(); LeaseSet l = (LeaseSet) entry.getValue(); if (l.getEarliestLeaseDate() < now) - deleteList.add(k); - } - for (Iterator iter = deleteList.iterator(); iter.hasNext(); ) { - String k = (String) iter.next(); - tc.remove(k); + iter.remove(); } } @@ -671,17 +686,12 @@ public class OutboundClientMessageOneShotJob extends JobImpl { * Caller must synchronize on tc. */ private static void cleanLeaseCache(HashMap tc) { - List deleteList = new ArrayList(); for (Iterator iter = tc.entrySet().iterator(); iter.hasNext(); ) { Map.Entry entry = (Map.Entry)iter.next(); String k = (String) entry.getKey(); Lease l = (Lease) entry.getValue(); if (l.isExpired(Router.CLOCK_FUDGE_FACTOR)) - deleteList.add(k); - } - for (Iterator iter = deleteList.iterator(); iter.hasNext(); ) { - String k = (String) iter.next(); - tc.remove(k); + iter.remove(); } } @@ -690,17 +700,25 @@ public class OutboundClientMessageOneShotJob extends JobImpl { * Caller must synchronize on tc. */ private static void cleanTunnelCache(RouterContext ctx, HashMap tc) { - List deleteList = new ArrayList(); for (Iterator iter = tc.entrySet().iterator(); iter.hasNext(); ) { Map.Entry entry = (Map.Entry)iter.next(); String k = (String) entry.getKey(); TunnelInfo tunnel = (TunnelInfo) entry.getValue(); if (!ctx.tunnelManager().isValidTunnel(sourceFromHashPair(k), tunnel)) - deleteList.add(k); + iter.remove(); } - for (Iterator iter = deleteList.iterator(); iter.hasNext(); ) { - String k = (String) iter.next(); - tc.remove(k); + } + + /** + * Clean out old reply times + * Caller must synchronize on tc. + */ + private static void cleanReplyCache(RouterContext ctx, HashMap tc) { + long now = ctx.clock().now(); + for (Iterator iter = tc.values().iterator(); iter.hasNext(); ) { + Long l = (Long) iter.next(); + if (l.longValue() < now - CLEAN_INTERVAL) + iter.remove(); } } @@ -720,6 +738,9 @@ public class OutboundClientMessageOneShotJob extends JobImpl { cleanTunnelCache(_ctx, _tunnelCache); cleanTunnelCache(_ctx, _backloggedTunnelCache); } + synchronized(_lastReplyRequestCache) { + cleanReplyCache(_ctx, _lastReplyRequestCache); + } } } @@ -731,8 +752,8 @@ public class OutboundClientMessageOneShotJob extends JobImpl { * Key the caches on the source+dest pair. * */ - private static HashMap _tunnelCache = new HashMap(); - private static HashMap _backloggedTunnelCache = new HashMap(); + private static HashMap _tunnelCache = new HashMap(); + private static HashMap _backloggedTunnelCache = new HashMap(); private TunnelInfo selectOutboundTunnel(Destination to) { TunnelInfo tunnel; long now = getContext().clock().now(); @@ -743,7 +764,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { * if you were the originator by backlogging the tunnel, then removing the * backlog and seeing if traffic came back or not. */ - tunnel = (TunnelInfo) _backloggedTunnelCache.get(hashPair()); + tunnel = _backloggedTunnelCache.get(hashPair()); if (tunnel != null) { if (getContext().tunnelManager().isValidTunnel(_from.calculateHash(), tunnel)) { if (!getContext().commSystem().isBacklogged(tunnel.getPeer(1))) { @@ -758,7 +779,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { _backloggedTunnelCache.remove(hashPair()); } // Use the same tunnel unless backlogged - tunnel = (TunnelInfo) _tunnelCache.get(hashPair()); + tunnel = _tunnelCache.get(hashPair()); if (tunnel != null) { if (getContext().tunnelManager().isValidTunnel(_from.calculateHash(), tunnel)) { if (tunnel.getLength() <= 1 || !getContext().commSystem().isBacklogged(tunnel.getPeer(1))) From 8f5257d5dc818dde614a9a414b1835a579bcf6bb Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 1 Mar 2009 23:14:38 +0000 Subject: [PATCH 026/688] make persistent client dests work --- .../java/src/net/i2p/i2ptunnel/I2PTunnel.java | 24 ++++++---- .../net/i2p/i2ptunnel/I2PTunnelClient.java | 4 +- .../i2p/i2ptunnel/I2PTunnelClientBase.java | 48 +++++++++++++++---- .../net/i2p/i2ptunnel/I2PTunnelIRCClient.java | 4 +- .../net/i2p/i2ptunnel/TunnelController.java | 16 +++++-- apps/i2ptunnel/jsp/editClient.jsp | 9 ++++ 6 files changed, 80 insertions(+), 25 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java index dc9dfd2fc..b11f954e7 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java @@ -552,14 +552,14 @@ public class I2PTunnel implements Logging, EventDispatcher { * Integer port number if the client is listening * sharedClient parameter is a String "true" or "false" * - * @param args {portNumber, destinationBase64 or "file:filename"[, sharedClient]} + * @param args {portNumber, destinationBase64 or "file:filename"[, sharedClient [, privKeyFile]]} * @param l logger to receive events and output */ public void runClient(String args[], Logging l) { boolean isShared = true; - if (args.length == 3) + if (args.length >= 3) isShared = Boolean.valueOf(args[2].trim()).booleanValue(); - if ( (args.length == 2) || (args.length == 3) ) { + if (args.length >= 2) { int portNum = -1; try { portNum = Integer.parseInt(args[0]); @@ -572,7 +572,10 @@ public class I2PTunnel implements Logging, EventDispatcher { I2PTunnelTask task; ownDest = !isShared; try { - task = new I2PTunnelClient(portNum, args[1], l, ownDest, (EventDispatcher) this, this); + String privateKeyFile = null; + if (args.length >= 4) + privateKeyFile = args[3]; + task = new I2PTunnelClient(portNum, args[1], l, ownDest, (EventDispatcher) this, this, privateKeyFile); addtask(task); notifyEvent("clientTaskId", Integer.valueOf(task.getId())); } catch (IllegalArgumentException iae) { @@ -581,7 +584,7 @@ public class I2PTunnel implements Logging, EventDispatcher { notifyEvent("clientTaskId", Integer.valueOf(-1)); } } else { - l.log("client [,]|file:[ ]"); + l.log("client [,]|file:[ ] []"); l.log(" creates a client that forwards port to the pubkey.\n" + " use 0 as port to get a free port assigned. If you specify\n" + " a comma delimited list of pubkeys, it will rotate among them\n" @@ -720,11 +723,11 @@ public class I2PTunnel implements Logging, EventDispatcher { * Also sets "ircclientStatus" = "ok" or "error" after the client tunnel has started. * parameter sharedClient is a String, either "true" or "false" * - * @param args {portNumber,destinationBase64 or "file:filename" [, sharedClient]} + * @param args {portNumber,destinationBase64 or "file:filename" [, sharedClient [, privKeyFile]]} * @param l logger to receive events and output */ public void runIrcClient(String args[], Logging l) { - if (args.length >= 2 && args.length <= 3) { + if (args.length >= 2) { int port = -1; try { port = Integer.parseInt(args[0]); @@ -751,7 +754,10 @@ public class I2PTunnel implements Logging, EventDispatcher { I2PTunnelTask task; ownDest = !isShared; try { - task = new I2PTunnelIRCClient(port, args[1],l, ownDest, (EventDispatcher) this, this); + String privateKeyFile = null; + if (args.length >= 4) + privateKeyFile = args[3]; + task = new I2PTunnelIRCClient(port, args[1], l, ownDest, (EventDispatcher) this, this, privateKeyFile); addtask(task); notifyEvent("ircclientTaskId", Integer.valueOf(task.getId())); } catch (IllegalArgumentException iae) { @@ -760,7 +766,7 @@ public class I2PTunnel implements Logging, EventDispatcher { notifyEvent("ircclientTaskId", Integer.valueOf(-1)); } } else { - l.log("ircclient []"); + l.log("ircclient [ []]"); l.log(" creates a client that filter IRC protocol."); l.log(" (optional) indicates if this client shares tunnels with other clients (true of false)"); notifyEvent("ircclientTaskId", Integer.valueOf(-1)); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java index 4739a07f4..502bb28d5 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java @@ -31,8 +31,8 @@ public class I2PTunnelClient extends I2PTunnelClientBase { */ public I2PTunnelClient(int localPort, String destinations, Logging l, boolean ownDest, EventDispatcher notifyThis, - I2PTunnel tunnel) throws IllegalArgumentException { - super(localPort, ownDest, l, notifyThis, "SynSender", tunnel); + I2PTunnel tunnel, String pkf) throws IllegalArgumentException { + super(localPort, ownDest, l, notifyThis, "SynSender", tunnel, pkf); if (waitEventValue("openBaseClientResult").equals("error")) { notifyEvent("openClientResult", "error"); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java index c926156f4..fadbf9fa1 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java @@ -3,6 +3,7 @@ */ package net.i2p.i2ptunnel; +import java.io.FileInputStream; import java.io.IOException; import java.io.InterruptedIOException; import java.net.ConnectException; @@ -59,6 +60,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna private byte[] pubkey; private String handlerName; + private String privKeyFile; private Object conLock = new Object(); @@ -91,18 +93,28 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna // I2PTunnelClientBase(localPort, ownDest, l, (EventDispatcher)null); //} + public I2PTunnelClientBase(int localPort, boolean ownDest, Logging l, + EventDispatcher notifyThis, String handlerName, + I2PTunnel tunnel) throws IllegalArgumentException { + this(localPort, ownDest, l, notifyThis, handlerName, tunnel, null); + } + /** + * @param privKeyFile null to generate a transient key + * * @throws IllegalArgumentException if the I2CP configuration is b0rked so * badly that we cant create a socketManager */ public I2PTunnelClientBase(int localPort, boolean ownDest, Logging l, EventDispatcher notifyThis, String handlerName, - I2PTunnel tunnel) throws IllegalArgumentException{ + I2PTunnel tunnel, String pkf) throws IllegalArgumentException{ super(localPort + " (uninitialized)", notifyThis, tunnel); _clientId = ++__clientId; this.localPort = localPort; this.l = l; this.handlerName = handlerName + _clientId; + this.privKeyFile = pkf; + _context = tunnel.getContext(); _context.statManager().createRateStat("i2ptunnel.client.closeBacklog", "How many pending sockets remain when we close one due to backlog?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 }); @@ -195,28 +207,34 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna private static I2PSocketManager socketManager; protected synchronized I2PSocketManager getSocketManager() { - return getSocketManager(getTunnel()); + return getSocketManager(getTunnel(), this.privKeyFile); } protected static synchronized I2PSocketManager getSocketManager(I2PTunnel tunnel) { + return getSocketManager(tunnel, null); + } + protected static synchronized I2PSocketManager getSocketManager(I2PTunnel tunnel, String pkf) { if (socketManager != null) { I2PSession s = socketManager.getSession(); if ( (s == null) || (s.isClosed()) ) { _log.info("Building a new socket manager since the old one closed [s=" + s + "]"); - socketManager = buildSocketManager(tunnel); + socketManager = buildSocketManager(tunnel, pkf); } else { _log.info("Not building a new socket manager since the old one is open [s=" + s + "]"); } } else { _log.info("Building a new socket manager since there is no other one"); - socketManager = buildSocketManager(tunnel); + socketManager = buildSocketManager(tunnel, pkf); } return socketManager; } protected I2PSocketManager buildSocketManager() { - return buildSocketManager(getTunnel()); + return buildSocketManager(getTunnel(), this.privKeyFile); } protected static I2PSocketManager buildSocketManager(I2PTunnel tunnel) { + return buildSocketManager(tunnel, null); + } + protected static I2PSocketManager buildSocketManager(I2PTunnel tunnel, String pkf) { Properties props = new Properties(); props.putAll(tunnel.getClientOptions()); int portNum = 7654; @@ -230,10 +248,22 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna I2PSocketManager sockManager = null; while (sockManager == null) { - // if persistent dest - // sockManager = I2PSocketManagerFactory.createManager(privData, tunnel.host, portNum, props); - // else - sockManager = I2PSocketManagerFactory.createManager(tunnel.host, portNum, props); + if (pkf != null) { + // Persistent client dest + FileInputStream fis = null; + try { + fis = new FileInputStream(pkf); + sockManager = I2PSocketManagerFactory.createManager(fis, tunnel.host, portNum, props); + } catch (IOException ioe) { + _log.error("Error opening key file", ioe); + // this is going to loop but if we break we'll get a NPE + } finally { + if (fis != null) + try { fis.close(); } catch (IOException ioe) {} + } + } else { + sockManager = I2PSocketManagerFactory.createManager(tunnel.host, portNum, props); + } if (sockManager == null) { _log.log(Log.CRIT, "Unable to create socket manager"); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java index 5b223b1a4..732c222a7 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java @@ -39,12 +39,12 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable Logging l, boolean ownDest, EventDispatcher notifyThis, - I2PTunnel tunnel) throws IllegalArgumentException { + I2PTunnel tunnel, String pkf) throws IllegalArgumentException { super(localPort, ownDest, l, notifyThis, - "IRCHandler " + (++__clientId), tunnel); + "IRCHandler " + (++__clientId), tunnel, pkf); StringTokenizer tok = new StringTokenizer(destinations, ","); dests = new ArrayList(1); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java index 727b18158..419e5a899 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java @@ -192,7 +192,12 @@ public class TunnelController implements Logging { String listenPort = getListenPort(); String dest = getTargetDestination(); String sharedClient = getSharedClient(); - _tunnel.runIrcClient(new String[] { listenPort, dest, sharedClient }, this); + if (getPersistentClientKey()) { + String privKeyFile = getPrivKeyFile(); + _tunnel.runIrcClient(new String[] { listenPort, dest, sharedClient, privKeyFile }, this); + } else { + _tunnel.runIrcClient(new String[] { listenPort, dest, sharedClient }, this); + } } private void startSocksClient() { @@ -264,7 +269,12 @@ public class TunnelController implements Logging { String listenPort = getListenPort(); String dest = getTargetDestination(); String sharedClient = getSharedClient(); - _tunnel.runClient(new String[] { listenPort, dest, sharedClient }, this); + if (getPersistentClientKey()) { + String privKeyFile = getPrivKeyFile(); + _tunnel.runClient(new String[] { listenPort, dest, sharedClient, privKeyFile }, this); + } else { + _tunnel.runClient(new String[] { listenPort, dest, sharedClient }, this); + } } private void startServer() { @@ -395,7 +405,7 @@ public class TunnelController implements Logging { public String getProxyList() { return _config.getProperty("proxyList"); } public String getSharedClient() { return _config.getProperty("sharedClient", "true"); } public boolean getStartOnLoad() { return "true".equalsIgnoreCase(_config.getProperty("startOnLoad", "true")); } - public boolean getPersistentClientKey() { return Boolean.valueOf(_config.getProperty("persistentClientKey")).booleanValue(); } + public boolean getPersistentClientKey() { return Boolean.valueOf(_config.getProperty("option.persistentClientKey")).booleanValue(); } public String getMyDestination() { if (_tunnel != null) { List sessions = _tunnel.getSessions(); diff --git a/apps/i2ptunnel/jsp/editClient.jsp b/apps/i2ptunnel/jsp/editClient.jsp index 3c046badd..178f564ef 100644 --- a/apps/i2ptunnel/jsp/editClient.jsp +++ b/apps/i2ptunnel/jsp/editClient.jsp @@ -351,6 +351,7 @@
          + <% if ("client".equals(tunnelType) || "ircclient".equals(tunnelType)) { %>
          +
          + + + (if known) +

          + <% } %>
          - (if known) + <% if (!"".equals(editBean.getDestinationBase64(curTunnel))) { %> + Add to local addressbook + <% } %>
*/ - public static void main(String args[]) { + public static void main2(String args[]) { RouterContext ctx = new RouterContext(new net.i2p.router.Router()); DecimalFormat fmt = new DecimalFormat("0,000.0"); fmt.setPositivePrefix("+"); From 91b8f7c2ae734995eb7eb646bb9e739d81cb2b9f Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 9 Mar 2009 14:41:48 +0000 Subject: [PATCH 033/688] fix typo in comment --- installer/resources/blocklist.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installer/resources/blocklist.txt b/installer/resources/blocklist.txt index 8a1614e7f..1eedb0232 100644 --- a/installer/resources/blocklist.txt +++ b/installer/resources/blocklist.txt @@ -10,7 +10,7 @@ # A more reasonable list: http://www.bluetack.co.uk/config/level1.zip # # You may also wish to add the bogons from http://www.cymru.com/Documents/bogon-list.html , -# but you will have top update your blocklist manually as IP ranges are assigned. +# but you will have to update your blocklist manually as IP ranges are assigned. # You must update this list yourself, it is not overwritten by the update process. # # * Acceptable formats (IPV4 only): From f3ddf3fa936bd578d34e8ec9514bcba250e6de96 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 9 Mar 2009 15:10:46 +0000 Subject: [PATCH 034/688] remove http from add torrent box --- .../src/org/klomp/snark/ConnectionAcceptor.java | 1 + .../i2psnark/java/src/org/klomp/snark/Snark.java | 2 +- .../src/org/klomp/snark/web/I2PSnarkServlet.java | 16 ++++++++++------ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/apps/i2psnark/java/src/org/klomp/snark/ConnectionAcceptor.java b/apps/i2psnark/java/src/org/klomp/snark/ConnectionAcceptor.java index 6d4aad1a3..2e45749b8 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/ConnectionAcceptor.java +++ b/apps/i2psnark/java/src/org/klomp/snark/ConnectionAcceptor.java @@ -152,6 +152,7 @@ public class ConnectionAcceptor implements Runnable _util.debug("Error while accepting: " + ioe, Snark.ERROR); stop = true; } + // catch oom? } try diff --git a/apps/i2psnark/java/src/org/klomp/snark/Snark.java b/apps/i2psnark/java/src/org/klomp/snark/Snark.java index f10ef41d3..e124955cf 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Snark.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Snark.java @@ -232,7 +232,7 @@ public class Snark } // Explicit shutdown. - Runtime.getRuntime().removeShutdownHook(snarkhook); + //Runtime.getRuntime().removeShutdownHook(snarkhook); snarkhook.start(); } } diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java index d42d3b520..52e109573 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -202,10 +202,14 @@ public class I2PSnarkServlet extends HttpServlet { } catch (IOException ioe) { _log.warn("hrm: " + local, ioe); } - } else if ( (newURL != null) && (newURL.trim().length() > "http://.i2p/".length()) ) { - _manager.addMessage("Fetching " + newURL); - I2PAppThread fetch = new I2PAppThread(new FetchAndAdd(_manager, newURL), "Fetch and add"); - fetch.start(); + } else if (newURL != null) { + if (newURL.startsWith("http://")) { + _manager.addMessage("Fetching " + newURL); + I2PAppThread fetch = new I2PAppThread(new FetchAndAdd(_manager, newURL), "Fetch and add"); + fetch.start(); + } else { + _manager.addMessage("Invalid URL - must start with http://"); + } } else { // no file or URL specified } @@ -644,7 +648,7 @@ public class I2PSnarkServlet extends HttpServlet { private void writeAddForm(PrintWriter out, HttpServletRequest req) throws IOException { String uri = req.getRequestURI(); String newURL = req.getParameter("newURL"); - if ( (newURL == null) || (newURL.trim().length() <= 0) ) newURL = "http://"; + if ( (newURL == null) || (newURL.trim().length() <= 0) ) newURL = ""; String newFile = req.getParameter("newFile"); if ( (newFile == null) || (newFile.trim().length() <= 0) ) newFile = ""; @@ -772,7 +776,7 @@ public class I2PSnarkServlet extends HttpServlet { return bytes + "B"; else if (bytes < 5*1024*1024) return ((bytes + 512)/1024) + "KB"; - else if (bytes < 5*1024*1024*1024l) + else if (bytes < 10*1024*1024*1024l) return ((bytes + 512*1024)/(1024*1024)) + "MB"; else return ((bytes + 512*1024*1024)/(1024*1024*1024)) + "GB"; From 7179a64feea93bf06f0fa0058eeb0b55e8f1f944 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 9 Mar 2009 15:11:45 +0000 Subject: [PATCH 035/688] I2PTunnel: Add delay-open option for clients --- .../i2p/i2ptunnel/I2PTunnelClientBase.java | 48 ++++++++++++------- .../src/net/i2p/i2ptunnel/web/EditBean.java | 4 ++ .../src/net/i2p/i2ptunnel/web/IndexBean.java | 5 +- apps/i2ptunnel/jsp/editClient.jsp | 16 +++++++ 4 files changed, 55 insertions(+), 18 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java index 1b308eb37..94bc959c0 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java @@ -126,26 +126,28 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna // be looked up tunnel.getClientOptions().setProperty("i2cp.dontPublishLeaseSet", "true"); - while (sockMgr == null) { - synchronized (sockLock) { - if (ownDest) { - sockMgr = buildSocketManager(); - } else { - sockMgr = getSocketManager(); + boolean openNow = !Boolean.valueOf(tunnel.getClientOptions().getProperty("i2cp.delayOpen")).booleanValue(); + if (openNow) { + while (sockMgr == null) { + synchronized (sockLock) { + if (ownDest) { + sockMgr = buildSocketManager(); + } else { + sockMgr = getSocketManager(); + } + } + if (sockMgr == null) { + _log.log(Log.CRIT, "Unable to create socket manager (our own? " + ownDest + ")"); + try { Thread.sleep(10*1000); } catch (InterruptedException ie) {} } } if (sockMgr == null) { - _log.log(Log.CRIT, "Unable to create socket manager (our own? " + ownDest + ")"); - try { Thread.sleep(10*1000); } catch (InterruptedException ie) {} + l.log("Invalid I2CP configuration"); + throw new IllegalArgumentException("Socket manager could not be created"); } - } - if (sockMgr == null) { - l.log("Invalid I2CP configuration"); - throw new IllegalArgumentException("Socket manager could not be created"); - } - l.log("I2P session created"); + l.log("I2P session created"); - getTunnel().addSession(sockMgr.getSession()); + } // else delay creating session until createI2PSocket() is called Thread t = new I2PThread(this); t.setName("Client " + _clientId); @@ -165,7 +167,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna configurePool(tunnel); if (open && listenerReady) { - l.log("Ready! Port " + getLocalPort()); + if (openNow) + l.log("Ready! Port " + getLocalPort()); + else + l.log("Listening on port " + getLocalPort() + ", delaying tunnel open until required"); notifyEvent("openBaseClientResult", "ok"); } else { l.log("Error listening - please see the logs!"); @@ -217,6 +222,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna I2PSession s = socketManager.getSession(); if ( (s == null) || (s.isClosed()) ) { _log.info("Building a new socket manager since the old one closed [s=" + s + "]"); + if (s != null) + tunnel.removeSession(s); socketManager = buildSocketManager(tunnel, pkf); } else { _log.info("Not building a new socket manager since the old one is open [s=" + s + "]"); @@ -335,6 +342,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna * @return a new I2PSocket */ public I2PSocket createI2PSocket(Destination dest) throws I2PException, ConnectException, NoRouteToHostException, InterruptedIOException { + if (sockMgr == null) { + // we need this before getDefaultOptions() + sockMgr = getSocketManager(); + } return createI2PSocket(dest, getDefaultOptions()); } @@ -355,7 +366,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna public I2PSocket createI2PSocket(Destination dest, I2PSocketOptions opt) throws I2PException, ConnectException, NoRouteToHostException, InterruptedIOException { I2PSocket i2ps; - if (Boolean.valueOf(getTunnel().getClientOptions().getProperty("i2cp.newDestOnResume")).booleanValue()) { + if (sockMgr == null) { + // delayed open - call get instead of build because the locking is up there + sockMgr = getSocketManager(); + } else if (Boolean.valueOf(getTunnel().getClientOptions().getProperty("i2cp.newDestOnResume")).booleanValue()) { synchronized(sockMgr) { I2PSocketManager oldSockMgr = sockMgr; // This will build a new socket manager and a new dest if the session is closed. diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java index 941edd0bd..004114b56 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java @@ -161,6 +161,10 @@ public class EditBean extends IndexBean { return getBooleanProperty(tunnel, "persistentClientKey"); } + public boolean getDelayOpen(int tunnel) { + return getBooleanProperty(tunnel, "i2cp.delayOpen"); + } + private int getProperty(int tunnel, String prop, int def) { TunnelController tun = getController(tunnel); if (tun != null) { diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java index cc1c0d558..fcf45d94d 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java @@ -614,6 +614,9 @@ public class IndexBean { public void setAccess(String moo) { _booleanOptions.add("i2cp.enableAccessList"); } + public void setDelayOpen(String moo) { + _booleanOptions.add("i2cp.delayOpen"); + } public void setNewDest(String val) { if ("1".equals(val)) _booleanOptions.add("i2cp.newDestOnResume"); @@ -820,7 +823,7 @@ public class IndexBean { "inbound.nickname", "outbound.nickname", "i2p.streaming.connectDelay", "i2p.streaming.maxWindowSize" }; private static final String _booleanClientOpts[] = { - "i2cp.reduceOnIdle", "i2cp.closeOnIdle", "i2cp.newDestOnResume", "persistentClientKey" + "i2cp.reduceOnIdle", "i2cp.closeOnIdle", "i2cp.newDestOnResume", "persistentClientKey", "i2cp.delayOpen" }; private static final String _booleanServerOpts[] = { "i2cp.reduceOnIdle", "i2cp.encryptLeaseSet", "i2cp.enableAccessList" diff --git a/apps/i2ptunnel/jsp/editClient.jsp b/apps/i2ptunnel/jsp/editClient.jsp index 178f564ef..d20c9f58d 100644 --- a/apps/i2ptunnel/jsp/editClient.jsp +++ b/apps/i2ptunnel/jsp/editClient.jsp @@ -351,6 +351,22 @@
+
+ +
+
+ + class="tickbox" /> +
+ +
+
+
+ <% if ("client".equals(tunnelType) || "ircclient".equals(tunnelType)) { %>
@@ -329,7 +329,7 @@
<% } // end iterating over required groups for the current stat %>
diff --git a/apps/i2ptunnel/jsp/editServer.jsp b/apps/i2ptunnel/jsp/editServer.jsp index eb407ec16..b3917cced 100644 --- a/apps/i2ptunnel/jsp/editServer.jsp +++ b/apps/i2ptunnel/jsp/editServer.jsp @@ -326,7 +326,7 @@
@@ -354,7 +354,7 @@
From ca3b6eb00daf0ec644f8f4dba09b53d7f3a7a28e Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 13 Mar 2009 16:57:51 +0000 Subject: [PATCH 042/688] catch a reported NPE ? --- .../i2p/i2ptunnel/I2PTunnelHTTPServer.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java index 536844af9..d6cb40a25 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java @@ -171,7 +171,24 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { sender.start(); browserout = _browser.getOutputStream(); - serverin = _webserver.getInputStream(); + // NPE seen here in 0.7-7, caused by addition of socket.close() in the + // catch (IOException ioe) block above in blockingHandle() ??? + // CRIT [ad-130280.hc] net.i2p.util.I2PThread : Killing thread Thread-130280.hc + // java.lang.NullPointerException + // at java.io.FileInputStream.(FileInputStream.java:131) + // at java.net.SocketInputStream.(SocketInputStream.java:44) + // at java.net.PlainSocketImpl.getInputStream(PlainSocketImpl.java:401) + // at java.net.Socket$2.run(Socket.java:779) + // at java.security.AccessController.doPrivileged(Native Method) + // at java.net.Socket.getInputStream(Socket.java:776) + // at net.i2p.i2ptunnel.I2PTunnelHTTPServer$CompressedRequestor.run(I2PTunnelHTTPServer.java:174) + // at java.lang.Thread.run(Thread.java:619) + // at net.i2p.util.I2PThread.run(I2PThread.java:71) + try { + serverin = _webserver.getInputStream(); + } catch (NullPointerException npe) { + throw new IOException("getInputStream NPE"); + } CompressedResponseOutputStream compressedOut = new CompressedResponseOutputStream(browserout); Sender s = new Sender(compressedOut, serverin, "server: server to browser"); if (_log.shouldLog(Log.INFO)) From cf02abd19cf15c00f725866846e486ae5f6afcd3 Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 13 Mar 2009 16:58:23 +0000 Subject: [PATCH 043/688] allow .onion addresses for testing --- .../java/src/net/i2p/i2ptunnel/socks/SOCKS4aServer.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS4aServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS4aServer.java index 2745cb0fa..23ec70c3f 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS4aServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS4aServer.java @@ -198,7 +198,8 @@ public class SOCKS4aServer extends SOCKSServer { I2PSocket destSock; try { - if (connHostName.toLowerCase().endsWith(".i2p")) { + if (connHostName.toLowerCase().endsWith(".i2p") || + connHostName.toLowerCase().endsWith(".onion")) { _log.debug("connecting to " + connHostName + "..."); // Let's not due a new Dest for every request, huh? //I2PSocketManager sm = I2PSocketManagerFactory.createManager(); @@ -224,7 +225,7 @@ public class SOCKS4aServer extends SOCKSServer { } else { List proxies = t.getProxies(connPort); if (proxies == null || proxies.size() <= 0) { - String err = "No outproxy configured for port " + connPort + " and no default configured either"; + String err = "No outproxy configured for port " + connPort + " and no default configured either - host: " + connHostName; _log.error(err); try { sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out); From 5a8b3eb8f34d048df9559deb8581e09c99e34267 Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 13 Mar 2009 18:27:29 +0000 Subject: [PATCH 044/688] Move HMac to I2PHMac, as jrandom implemented changes that make it incompatible with the HMac in the android libraries. --- .../src/net/i2p/crypto/HMAC256Generator.java | 9 +++++---- core/java/src/net/i2p/crypto/HMACGenerator.java | 17 +++++++++-------- .../crypto/macs/{HMac.java => I2PHMac.java} | 10 +++++++--- 3 files changed, 21 insertions(+), 15 deletions(-) rename core/java/src/org/bouncycastle/crypto/macs/{HMac.java => I2PHMac.java} (95%) diff --git a/core/java/src/net/i2p/crypto/HMAC256Generator.java b/core/java/src/net/i2p/crypto/HMAC256Generator.java index 2fcaa7b5e..7d6f67a76 100644 --- a/core/java/src/net/i2p/crypto/HMAC256Generator.java +++ b/core/java/src/net/i2p/crypto/HMAC256Generator.java @@ -7,7 +7,8 @@ import net.i2p.data.Hash; import net.i2p.data.SessionKey; import org.bouncycastle.crypto.Digest; -import org.bouncycastle.crypto.macs.HMac; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.macs.I2PHMac; /** * Calculate the HMAC-SHA256 of a key+message. All the good stuff occurs @@ -19,15 +20,15 @@ public class HMAC256Generator extends HMACGenerator { public HMAC256Generator(I2PAppContext context) { super(context); } @Override - protected HMac acquire() { + protected Mac acquire() { synchronized (_available) { if (_available.size() > 0) - return (HMac)_available.remove(0); + return (Mac)_available.remove(0); } // the HMAC is hardcoded to use SHA256 digest size // for backwards compatability. next time we have a backwards // incompatible change, we should update this by removing ", 32" - return new HMac(new Sha256ForMAC()); + return new I2PHMac(new Sha256ForMAC()); } private class Sha256ForMAC extends Sha256Standalone implements Digest { diff --git a/core/java/src/net/i2p/crypto/HMACGenerator.java b/core/java/src/net/i2p/crypto/HMACGenerator.java index 8388590a2..52b29e19b 100644 --- a/core/java/src/net/i2p/crypto/HMACGenerator.java +++ b/core/java/src/net/i2p/crypto/HMACGenerator.java @@ -10,7 +10,8 @@ import net.i2p.data.Hash; import net.i2p.data.SessionKey; import org.bouncycastle.crypto.digests.MD5Digest; -import org.bouncycastle.crypto.macs.HMac; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.macs.I2PHMac; /** * Calculate the HMAC-MD5 of a key+message. All the good stuff occurs @@ -49,7 +50,7 @@ public class HMACGenerator { if ((key == null) || (key.getData() == null) || (data == null)) throw new NullPointerException("Null arguments for HMAC"); - HMac mac = acquire(); + Mac mac = acquire(); mac.init(key.getData()); mac.update(data, offset, length); //byte rv[] = new byte[Hash.HASH_LENGTH]; @@ -73,7 +74,7 @@ public class HMACGenerator { if ((key == null) || (key.getData() == null) || (curData == null)) throw new NullPointerException("Null arguments for HMAC"); - HMac mac = acquire(); + Mac mac = acquire(); mac.init(key.getData()); mac.update(curData, curOffset, curLength); byte rv[] = acquireTmp(); @@ -86,17 +87,17 @@ public class HMACGenerator { return eq; } - protected HMac acquire() { + protected Mac acquire() { synchronized (_available) { if (_available.size() > 0) - return (HMac)_available.remove(0); + return (Mac)_available.remove(0); } // the HMAC is hardcoded to use SHA256 digest size // for backwards compatability. next time we have a backwards // incompatible change, we should update this by removing ", 32" - return new HMac(new MD5Digest(), 32); + return new I2PHMac(new MD5Digest(), 32); } - private void release(HMac mac) { + private void release(Mac mac) { synchronized (_available) { if (_available.size() < 64) _available.add(mac); @@ -122,4 +123,4 @@ public class HMACGenerator { _availableTmp.add((Object)tmp); } } -} \ No newline at end of file +} diff --git a/core/java/src/org/bouncycastle/crypto/macs/HMac.java b/core/java/src/org/bouncycastle/crypto/macs/I2PHMac.java similarity index 95% rename from core/java/src/org/bouncycastle/crypto/macs/HMac.java rename to core/java/src/org/bouncycastle/crypto/macs/I2PHMac.java index 7176c8aca..a566e8a79 100644 --- a/core/java/src/org/bouncycastle/crypto/macs/HMac.java +++ b/core/java/src/org/bouncycastle/crypto/macs/I2PHMac.java @@ -42,8 +42,12 @@ import org.bouncycastle.crypto.Mac; * a frequently used buffer (called on doFinal). changes released into the public * domain in 2005. * + * This is renamed from HMac because the constructor HMac(digest, sz) does not exist + * in the standard bouncycastle library, thus it conflicts in JVMs that contain the + * standard library (Android). + * */ -public class HMac +public class I2PHMac implements Mac { private final static int BLOCK_LENGTH = 64; @@ -56,12 +60,12 @@ implements Mac private byte[] inputPad = new byte[BLOCK_LENGTH]; private byte[] outputPad = new byte[BLOCK_LENGTH]; - public HMac( + public I2PHMac( Digest digest) { this(digest, digest.getDigestSize()); } - public HMac( + public I2PHMac( Digest digest, int sz) { this.digest = digest; From b8f22bf3bf682291c6530cfeba92ce748586a8be Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 13 Mar 2009 18:56:16 +0000 Subject: [PATCH 045/688] - Add FileStreamFactory and I2PFile to deal with the problems from the code CWD is / but the only writable directory is /data/data/net.i2p.router/files/ - still a ton of places to be fixed, will be fixed up as things get working - Load some config files from resources at startup - Fix up logging - Add reseed capability, by copying some code over from routerconsole - Deal with conflicting bouncycastle libs --- android/AndroidManifest.xml | 4 +- android/build.xml | 61 ++++++++++- android/res/layout/main.xml | 5 + android/res/raw/logger_config | 3 + android/res/raw/router_config | 7 ++ android/src/net/i2p/router/I2PAndroid.java | 100 ++++++++++++++++++ .../src/net/i2p/router/web/ReseedChecker.java | 37 +++++++ .../src/net/i2p/util/FileStreamFactory.java | 64 +++++++++++ android/src/net/i2p/util/I2PFile.java | 29 +++++ android/src/net/i2p/util/LogWriter.java | 40 ++++--- .../src/net/i2p/router/web/ReseedHandler.java | 6 +- core/java/src/net/i2p/data/DataHelper.java | 3 +- .../src/net/i2p/util/FileStreamFactory.java | 36 +++++++ core/java/src/net/i2p/util/I2PFile.java | 24 +++++ core/java/src/net/i2p/util/LogManager.java | 8 +- .../java/src/net/i2p/router/KeyManager.java | 5 +- router/java/src/net/i2p/router/Router.java | 10 +- .../kademlia/PersistentDataStore.java | 12 ++- .../peermanager/ProfilePersistenceHelper.java | 12 ++- .../router/startup/CreateRouterInfoJob.java | 5 +- .../i2p/router/startup/LoadRouterInfoJob.java | 10 +- .../router/startup/RebuildRouterInfoJob.java | 8 +- .../router/transport/ntcp/EventPumper.java | 2 +- 23 files changed, 437 insertions(+), 54 deletions(-) create mode 100644 android/res/raw/logger_config create mode 100644 android/res/raw/router_config create mode 100644 android/src/net/i2p/router/web/ReseedChecker.java create mode 100644 android/src/net/i2p/util/FileStreamFactory.java create mode 100644 android/src/net/i2p/util/I2PFile.java create mode 100644 core/java/src/net/i2p/util/FileStreamFactory.java create mode 100644 core/java/src/net/i2p/util/I2PFile.java diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index b45a40613..cb63a80c1 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -3,9 +3,11 @@ package="net.i2p.router" android:versionCode="1" android:versionName="1.0.0"> + + android:label="@string/app_name" + android:launchMode="singleTask" > diff --git a/android/build.xml b/android/build.xml index a7b411996..5875c5fed 100644 --- a/android/build.xml +++ b/android/build.xml @@ -101,12 +101,15 @@ Creating output directories if needed... + + + @@ -140,7 +143,7 @@ - + - + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + Converting compiled files and external libraries into ${outdir}/${dex-file}... diff --git a/android/res/layout/main.xml b/android/res/layout/main.xml index 3bfc31cff..d76411527 100644 --- a/android/res/layout/main.xml +++ b/android/res/layout/main.xml @@ -9,5 +9,10 @@ android:layout_height="wrap_content" android:text="Hello World, I2PAndroid" /> + diff --git a/android/res/raw/logger_config b/android/res/raw/logger_config new file mode 100644 index 000000000..2aeabb9f6 --- /dev/null +++ b/android/res/raw/logger_config @@ -0,0 +1,3 @@ +logger.defaultLevel=INFO +logger.record.net.i2p.router.transport.FIFOBandwidthRefiller=ERROR +logger.record.net.i2p.stat.Rate=ERROR diff --git a/android/res/raw/router_config b/android/res/raw/router_config new file mode 100644 index 000000000..19c609542 --- /dev/null +++ b/android/res/raw/router_config @@ -0,0 +1,7 @@ +# initial router.config +# save memory +router.prng.buffers=2 +router.decayingBloomFilterM=20 +stat.full=false +# no I2CP +i2p.dummyClientFacade=true diff --git a/android/src/net/i2p/router/I2PAndroid.java b/android/src/net/i2p/router/I2PAndroid.java index 6b65bac52..c08670e36 100644 --- a/android/src/net/i2p/router/I2PAndroid.java +++ b/android/src/net/i2p/router/I2PAndroid.java @@ -1,18 +1,118 @@ package net.i2p.router; import android.app.Activity; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.Resources.NotFoundException; import android.os.Bundle; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.IOException; + import net.i2p.router.Router; +import net.i2p.router.web.ReseedChecker; +import net.i2p.util.I2PFile; public class I2PAndroid extends Activity { + static Context _context; + /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); + + _context = this; // Activity extends Context + debugStuff(); + initialize(); + Router.main(null); + System.err.println("Router.main finished"); + + ReseedChecker.checkReseed(); } + + public void onRestart() + { + System.err.println("onRestart called"); + super.onRestart(); + } + + public void onResume() + { + System.err.println("onResume called"); + super.onResume(); + } + + public void onPause() + { + System.err.println("onPause called"); + super.onPause(); + } + + public void onStop() + { + System.err.println("onStop called"); + super.onStop(); + } + + public void onDestroy() + { + System.err.println("onDestroy called"); + super.onDestroy(); + } + + public static Context getContext() { + return _context; + } + + private void debugStuff() { + System.err.println("java.vendor" + ": " + System.getProperty("java.vendor")); + System.err.println("java.version" + ": " + System.getProperty("java.version")); + System.err.println("os.arch" + ": " + System.getProperty("os.arch")); + System.err.println("os.name" + ": " + System.getProperty("os.name")); + System.err.println("os.version" + ": " + System.getProperty("os.version")); + System.err.println("user.dir" + ": " + System.getProperty("user.dir")); + System.err.println("user.home" + ": " + System.getProperty("user.home")); + System.err.println("user.name" + ": " + System.getProperty("user.name")); + } + + private void initialize() { + // Until we can edit the router.config on the device, + // copy it from the resource every time. + // File f = new I2PFile("router.config"); + // if (!f.exists()) { + copyResourceToFile(R.raw.router_config, "router.config"); + copyResourceToFile(R.raw.logger_config, "logger.config"); + copyResourceToFile(R.raw.blocklist_txt, "blocklist.txt"); + // } + } + + private void copyResourceToFile(int resID, String f) { + InputStream in = null; + FileOutputStream out = null; + + System.err.println("Creating file " + f + " from resource"); + byte buf[] = new byte[4096]; + try { + // Context methods + in = getResources().openRawResource(resID); + out = openFileOutput(f, 0); + + int read = 0; + while ( (read = in.read(buf)) != -1) + out.write(buf, 0, read); + + } catch (IOException ioe) { + } catch (Resources.NotFoundException nfe) { + } finally { + if (in != null) try { in.close(); } catch (IOException ioe) {} + if (out != null) try { out.close(); } catch (IOException ioe) {} + } + } + } diff --git a/android/src/net/i2p/router/web/ReseedChecker.java b/android/src/net/i2p/router/web/ReseedChecker.java new file mode 100644 index 000000000..96eccce51 --- /dev/null +++ b/android/src/net/i2p/router/web/ReseedChecker.java @@ -0,0 +1,37 @@ +package net.i2p.router.web; + +import java.io.File; + +import net.i2p.router.web.ReseedHandler; +import net.i2p.util.I2PFile; + +/** + * Copied from RouterConsoleRunner.java + */ +public class ReseedChecker { + + public static void checkReseed() { + + System.err.println("Checking to see if we should reseed"); + // we check the i2p installation directory (.) for a flag telling us not to reseed, + // but also check the home directory for that flag too, since new users installing i2p + // don't have an installation directory that they can put the flag in yet. + File noReseedFile = new I2PFile(new I2PFile(System.getProperty("user.home")), ".i2pnoreseed"); + File noReseedFileAlt1 = new I2PFile(new I2PFile(System.getProperty("user.home")), "noreseed.i2p"); + File noReseedFileAlt2 = new I2PFile(".i2pnoreseed"); + File noReseedFileAlt3 = new I2PFile("noreseed.i2p"); + if (!noReseedFile.exists() && !noReseedFileAlt1.exists() && !noReseedFileAlt2.exists() && !noReseedFileAlt3.exists()) { + File netDb = new I2PFile("netDb"); + // sure, some of them could be "my.info" or various leaseSet- files, but chances are, + // if someone has those files, they've already been seeded (at least enough to let them + // get i2p started - they can reseed later in the web console) + String names[] = (netDb.exists() ? netDb.list() : null); + if ( (names == null) || (names.length < 15) ) { + System.err.println("Yes, reseeding now"); + ReseedHandler reseedHandler = new ReseedHandler(); + reseedHandler.requestReseed(); + } + } + } + +} diff --git a/android/src/net/i2p/util/FileStreamFactory.java b/android/src/net/i2p/util/FileStreamFactory.java new file mode 100644 index 000000000..b7a65e4f2 --- /dev/null +++ b/android/src/net/i2p/util/FileStreamFactory.java @@ -0,0 +1,64 @@ +/* + * This is free software, do as you please. + */ + +package net.i2p.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; + +import net.i2p.router.I2PAndroid; + +/** + * Use android static file stream methods + * gaaah: + * 1) the CWD is / + * 2) we can only access /data/data/net.i2p.router/files/ + * 3) you can't change your CWD in Java + * so we have this lovely and the one in I2PFile. + * + * @author zzz + */ +public class FileStreamFactory { + private static final String DIR = "/data/data/net.i2p.router/files/"; + + /** hopefully no path separators in string */ + public static FileInputStream getFileInputStream(String f) throws FileNotFoundException { + System.err.println("Input file-s: " + I2PAndroid.getContext().getFileStreamPath(f).getAbsolutePath()); + return I2PAndroid.getContext().openFileInput(f); + } + + public static FileInputStream getFileInputStream(File f) throws FileNotFoundException { + System.err.println("Input file-f: " + getPath(f) + + ' ' + f.getAbsolutePath()); + //return I2PAndroid.getContext().openFileInput(f.getName()); + return new FileInputStream(getPath(f)); + } + + /** hopefully no path separators in string */ + public static FileOutputStream getFileOutputStream(String f) throws FileNotFoundException { + System.err.println("Output file-s: " + I2PAndroid.getContext().getFileStreamPath(f).getAbsolutePath()); + return I2PAndroid.getContext().openFileOutput(f, 0); + } + + public static FileOutputStream getFileOutputStream(File f) throws FileNotFoundException { + System.err.println("Output file-f: " + getPath(f) + + ' ' + f.getAbsolutePath()); + //return I2PAndroid.getContext().openFileOutput(f.getName(), 0); + return new FileOutputStream(getPath(f)); + } + + /** + * preserve path but convert /foo/blah to /data/data/net.i2p.router/files/foo/blah + * Although if the File arg was created with new I2PFile() then this isn't required + * + */ + private static String getPath(File f) { + String abs = f.getAbsolutePath(); + if (abs.startsWith(DIR)) + return abs; + return DIR + abs.substring(1); // strip extra '/' + } +} diff --git a/android/src/net/i2p/util/I2PFile.java b/android/src/net/i2p/util/I2PFile.java new file mode 100644 index 000000000..94bda029b --- /dev/null +++ b/android/src/net/i2p/util/I2PFile.java @@ -0,0 +1,29 @@ +/* + * This is free software, do as you please. + */ + +package net.i2p.util; + +import java.io.File; + +/** + * gaaah: + * 1) the CWD is / + * 2) we can only access /data/data/net.i2p.router/files/ + * 3) you can't change your CWD in Java + * so we have this lovely and the one in FileStreamFactory. + * + * @author zzz + */ +public class I2PFile extends File { + + public I2PFile (String f) { + super("/data/data/net.i2p.router/files/" + f); + } + + /** one level deep only */ + public I2PFile (File p, String f) { + super("/data/data/net.i2p.router/files/" + p.getName(), f); + } + +} diff --git a/android/src/net/i2p/util/LogWriter.java b/android/src/net/i2p/util/LogWriter.java index 847730d4f..03f5577ae 100644 --- a/android/src/net/i2p/util/LogWriter.java +++ b/android/src/net/i2p/util/LogWriter.java @@ -87,36 +87,44 @@ class LogWriter implements Runnable { private void writeRecord(LogRecord rec) { if (rec.getThrowable() == null) - log(rec.getPriority(), rec.getSourceName(), null, rec.getThreadName(), rec.getMessage()); + log(rec.getPriority(), rec.getSource(), rec.getSourceName(), rec.getThreadName(), rec.getMessage()); else - log(rec.getPriority(), rec.getSourceName(), null, rec.getThreadName(), rec.getMessage(), rec.getThrowable()); + log(rec.getPriority(), rec.getSource(), rec.getSourceName(), rec.getThreadName(), rec.getMessage(), rec.getThrowable()); } - public void log(int priority, String className, String name, String threadName, String msg) { - if (className != null) + public void log(int priority, Class src, String name, String threadName, String msg) { + if (src != null) { + String tag = src.getName(); + int dot = tag.lastIndexOf("."); + if (dot >= 0) + tag = tag.substring(dot + 1); android.util.Log.println(toAndroidLevel(priority), - className, - threadName + ' ' + msg); - else if (name != null) + tag, + '[' + threadName + "] " + msg); + } else if (name != null) android.util.Log.println(toAndroidLevel(priority), name, - threadName + ' ' + msg); + '[' + threadName + "] " + msg); else android.util.Log.println(toAndroidLevel(priority), threadName, msg); } - public void log(int priority, String className, String name, String threadName, String msg, Throwable t) { - if (className != null) + public void log(int priority, Class src, String name, String threadName, String msg, Throwable t) { + if (src != null) { + String tag = src.getName(); + int dot = tag.lastIndexOf("."); + if (dot >= 0) + tag = tag.substring(dot + 1); android.util.Log.println(toAndroidLevel(priority), - className, - threadName + ' ' + msg + - msg + ' ' + t.toString() + ' ' + android.util.Log.getStackTraceString(t)); - else if (name != null) + tag, + '[' + threadName + "] " + msg + + ' ' + t.toString() + ' ' + android.util.Log.getStackTraceString(t)); + } else if (name != null) android.util.Log.println(toAndroidLevel(priority), name, - threadName + ' ' + msg + - msg + ' ' + t.toString() + ' ' + android.util.Log.getStackTraceString(t)); + '[' + threadName + "] " + msg + + ' ' + t.toString() + ' ' + android.util.Log.getStackTraceString(t)); else android.util.Log.println(toAndroidLevel(priority), threadName, diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java index 066cb1144..3d3d603fa 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java @@ -15,6 +15,8 @@ import java.util.StringTokenizer; import net.i2p.I2PAppContext; import net.i2p.router.RouterContext; import net.i2p.util.EepGet; +import net.i2p.util.FileStreamFactory; +import net.i2p.util.I2PFile; import net.i2p.util.I2PThread; import net.i2p.util.Log; @@ -250,11 +252,11 @@ public class ReseedHandler { private void writeSeed(String name, byte data[]) throws Exception { String dirName = "netDb"; // _context.getProperty("router.networkDatabase.dbDir", "netDb"); - File netDbDir = new File(dirName); + File netDbDir = new I2PFile(dirName); if (!netDbDir.exists()) { boolean ok = netDbDir.mkdirs(); } - FileOutputStream fos = new FileOutputStream(new File(netDbDir, "routerInfo-" + name + ".dat")); + FileOutputStream fos = FileStreamFactory.getFileOutputStream(new I2PFile(netDbDir, "routerInfo-" + name + ".dat")); fos.write(data); fos.close(); } diff --git a/core/java/src/net/i2p/data/DataHelper.java b/core/java/src/net/i2p/data/DataHelper.java index 53e32a347..55b424562 100644 --- a/core/java/src/net/i2p/data/DataHelper.java +++ b/core/java/src/net/i2p/data/DataHelper.java @@ -37,6 +37,7 @@ import java.util.TreeMap; import java.util.zip.Deflater; import net.i2p.util.ByteCache; +import net.i2p.util.FileStreamFactory; import net.i2p.util.OrderedProperties; import net.i2p.util.ReusableGZIPInputStream; import net.i2p.util.ReusableGZIPOutputStream; @@ -217,7 +218,7 @@ public class DataHelper { loadProps(props, file, false); } public static void loadProps(Properties props, File file, boolean forceLowerCase) throws IOException { - loadProps(props, new FileInputStream(file), forceLowerCase); + loadProps(props, FileStreamFactory.getFileInputStream(file), forceLowerCase); } public static void loadProps(Properties props, InputStream inStr) throws IOException { loadProps(props, inStr, false); diff --git a/core/java/src/net/i2p/util/FileStreamFactory.java b/core/java/src/net/i2p/util/FileStreamFactory.java new file mode 100644 index 000000000..01d505665 --- /dev/null +++ b/core/java/src/net/i2p/util/FileStreamFactory.java @@ -0,0 +1,36 @@ +/* + * public domain + */ + +package net.i2p.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; + + +/** + * This is pulled out and replaced in the android build. + * + * @author zzz + */ +public class FileStreamFactory { + + public static FileInputStream getFileInputStream(String f) throws FileNotFoundException { + return new FileInputStream(f); + } + + public static FileInputStream getFileInputStream(File f) throws FileNotFoundException { + return new FileInputStream(f); + } + + public static FileOutputStream getFileOutputStream(String f) throws FileNotFoundException { + return new FileOutputStream(f); + } + + public static FileOutputStream getFileOutputStream(File f) throws FileNotFoundException { + return new FileOutputStream(f); + } + +} diff --git a/core/java/src/net/i2p/util/I2PFile.java b/core/java/src/net/i2p/util/I2PFile.java new file mode 100644 index 000000000..107286edf --- /dev/null +++ b/core/java/src/net/i2p/util/I2PFile.java @@ -0,0 +1,24 @@ +/* + * public domain + */ + +package net.i2p.util; + +import java.io.File; + +/** + * This is pulled out and replaced in the android build. + * + * @author zzz + */ +public class I2PFile extends File { + + public I2PFile (String f) { + super(f); + } + + public I2PFile (File p, String f) { + super(p, f); + } + +} diff --git a/core/java/src/net/i2p/util/LogManager.java b/core/java/src/net/i2p/util/LogManager.java index 1f957515c..52b828af9 100644 --- a/core/java/src/net/i2p/util/LogManager.java +++ b/core/java/src/net/i2p/util/LogManager.java @@ -240,7 +240,7 @@ public class LogManager { // private void loadConfig() { - File cfgFile = new File(_location); + File cfgFile = new I2PFile(_location); if (!cfgFile.exists()) { if (!_alreadyNoticedMissingConfig) { if (_log.shouldLog(Log.WARN)) @@ -268,11 +268,11 @@ public class LogManager { Properties p = new Properties(); FileInputStream fis = null; try { - fis = new FileInputStream(cfgFile); + fis = FileStreamFactory.getFileInputStream(cfgFile); p.load(fis); _configLastRead = _context.clock().now(); } catch (IOException ioe) { - System.err.println("Error loading logger config from " + new File(_location).getAbsolutePath()); + System.err.println("Error loading logger config from " + new I2PFile(_location).getAbsolutePath()); } finally { if (fis != null) try { fis.close(); @@ -540,7 +540,7 @@ public class LogManager { String config = createConfig(); FileOutputStream fos = null; try { - fos = new FileOutputStream(_location); + fos = FileStreamFactory.getFileOutputStream(_location); fos.write(config.getBytes()); return true; } catch (IOException ioe) { diff --git a/router/java/src/net/i2p/router/KeyManager.java b/router/java/src/net/i2p/router/KeyManager.java index 4e2ed2c51..f2150b923 100644 --- a/router/java/src/net/i2p/router/KeyManager.java +++ b/router/java/src/net/i2p/router/KeyManager.java @@ -26,6 +26,7 @@ import net.i2p.data.PublicKey; import net.i2p.data.SigningPrivateKey; import net.i2p.data.SigningPublicKey; import net.i2p.util.Clock; +import net.i2p.util.FileStreamFactory; import net.i2p.util.Log; /** @@ -205,12 +206,12 @@ public class KeyManager { FileInputStream in = null; try { if (exists) { - out = new FileOutputStream(keyFile); + out = FileStreamFactory.getFileOutputStream(keyFile); structure.writeBytes(out); return structure; } else { if (keyFile.exists()) { - in = new FileInputStream(keyFile); + in = FileStreamFactory.getFileInputStream(keyFile); structure.readBytes(in); return structure; } else { diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java index 13e801458..c33dc3e63 100644 --- a/router/java/src/net/i2p/router/Router.java +++ b/router/java/src/net/i2p/router/Router.java @@ -40,7 +40,9 @@ import net.i2p.router.transport.FIFOBandwidthLimiter; import net.i2p.stat.Rate; import net.i2p.stat.RateStat; import net.i2p.stat.StatManager; +import net.i2p.util.FileStreamFactory; import net.i2p.util.FileUtil; +import net.i2p.util.I2PFile; import net.i2p.util.I2PThread; import net.i2p.util.Log; import net.i2p.util.SimpleScheduler; @@ -292,7 +294,7 @@ public class Router { } Properties props = new Properties(); try { - File f = new File(filename); + File f = new I2PFile(filename); if (f.canRead()) { DataHelper.loadProps(props, f); // dont be a wanker @@ -945,7 +947,7 @@ public class Router { public boolean saveConfig() { FileOutputStream fos = null; try { - fos = new FileOutputStream(_configFilename); + fos = FileStreamFactory.getFileOutputStream(_configFilename); StringBuffer buf = new StringBuffer(8*1024); synchronized (_config) { TreeSet ordered = new TreeSet(_config.keySet()); @@ -1331,7 +1333,7 @@ class MarkLiveliness implements Runnable { private void ping() { FileOutputStream fos = null; try { - fos = new FileOutputStream(_pingFile); + fos = FileStreamFactory.getFileOutputStream(_pingFile); fos.write(("" + System.currentTimeMillis()).getBytes()); } catch (IOException ioe) { System.err.println("Error writing to ping file"); @@ -1378,7 +1380,7 @@ class PersistRouterInfoJob extends JobImpl { FileOutputStream fos = null; try { - fos = new FileOutputStream(infoFilename); + fos = FileStreamFactory.getFileOutputStream(infoFilename); info.writeBytes(fos); } catch (DataFormatException dfe) { _log.error("Error rebuilding the router information", dfe); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java index 5d0d219db..1d9490a33 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java @@ -26,6 +26,8 @@ import net.i2p.data.RouterInfo; import net.i2p.router.JobImpl; import net.i2p.router.Router; import net.i2p.router.RouterContext; +import net.i2p.util.FileStreamFactory; +import net.i2p.util.I2PFile; import net.i2p.util.I2PThread; import net.i2p.util.Log; @@ -171,11 +173,11 @@ class PersistentDataStore extends TransientDataStore { else throw new IOException("We don't know how to write objects of type " + data.getClass().getName()); - dbFile = new File(dbDir, filename); + dbFile = new I2PFile(dbDir, filename); long dataPublishDate = getPublishDate(data); if (dbFile.lastModified() < dataPublishDate) { // our filesystem is out of date, lets replace it - fos = new FileOutputStream(dbFile); + fos = FileStreamFactory.getFileOutputStream(dbFile); try { data.writeBytes(fos); fos.close(); @@ -278,7 +280,7 @@ class PersistentDataStore extends TransientDataStore { FileInputStream fis = null; boolean corrupt = false; try { - fis = new FileInputStream(_routerFile); + fis = FileStreamFactory.getFileInputStream(_routerFile); RouterInfo ri = new RouterInfo(); ri.readBytes(fis); if (ri.getNetworkId() != Router.NETWORK_ID) { @@ -312,7 +314,7 @@ class PersistentDataStore extends TransientDataStore { private File getDbDir() throws IOException { - File f = new File(_dbDir); + File f = new I2PFile(_dbDir); if (!f.exists()) { boolean created = f.mkdirs(); if (!created) @@ -362,7 +364,7 @@ class PersistentDataStore extends TransientDataStore { private void removeFile(Hash key, File dir) throws IOException { String riName = getRouterInfoName(key); - File f = new File(dir, riName); + File f = new I2PFile(dir, riName); if (f.exists()) { boolean removed = f.delete(); if (!removed) diff --git a/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java b/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java index fd88c406b..6b8e3105e 100644 --- a/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java +++ b/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java @@ -18,6 +18,8 @@ import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; import net.i2p.data.Hash; import net.i2p.router.RouterContext; +import net.i2p.util.FileStreamFactory; +import net.i2p.util.I2PFile; import net.i2p.util.Log; class ProfilePersistenceHelper { @@ -61,7 +63,7 @@ class ProfilePersistenceHelper { long before = _context.clock().now(); OutputStream fos = null; try { - fos = new BufferedOutputStream(new GZIPOutputStream(new FileOutputStream(f))); + fos = new BufferedOutputStream(new GZIPOutputStream(FileStreamFactory.getFileOutputStream(f))); writeProfile(profile, fos); } catch (IOException ioe) { _log.error("Error writing profile to " + f); @@ -264,10 +266,10 @@ class ProfilePersistenceHelper { private void loadProps(Properties props, File file) { try { - FileInputStream fin = new FileInputStream(file); + FileInputStream fin = FileStreamFactory.getFileInputStream(file); int c = fin.read(); fin.close(); - fin = new FileInputStream(file); // ghetto mark+reset + fin = FileStreamFactory.getFileInputStream(file); // ghetto mark+reset if (c == '#') { // uncompressed if (_log.shouldLog(Log.INFO)) @@ -299,7 +301,7 @@ class ProfilePersistenceHelper { } private File pickFile(PeerProfile profile) { - return new File(getProfileDir(), "profile-" + profile.getPeer().toBase64() + ".dat"); + return new I2PFile(getProfileDir(), "profile-" + profile.getPeer().toBase64() + ".dat"); } private File getProfileDir() { @@ -315,7 +317,7 @@ class ProfilePersistenceHelper { dir = DEFAULT_PEER_PROFILE_DIR; } } - _profileDir = new File(dir); + _profileDir = new I2PFile(dir); } return _profileDir; } diff --git a/router/java/src/net/i2p/router/startup/CreateRouterInfoJob.java b/router/java/src/net/i2p/router/startup/CreateRouterInfoJob.java index 92b176c30..12c6cee6a 100644 --- a/router/java/src/net/i2p/router/startup/CreateRouterInfoJob.java +++ b/router/java/src/net/i2p/router/startup/CreateRouterInfoJob.java @@ -25,6 +25,7 @@ import net.i2p.router.Job; import net.i2p.router.JobImpl; import net.i2p.router.Router; import net.i2p.router.RouterContext; +import net.i2p.util.FileStreamFactory; import net.i2p.util.Log; public class CreateRouterInfoJob extends JobImpl { @@ -79,13 +80,13 @@ public class CreateRouterInfoJob extends JobImpl { String infoFilename = getContext().router().getConfigSetting(Router.PROP_INFO_FILENAME); if (infoFilename == null) infoFilename = Router.PROP_INFO_FILENAME_DEFAULT; - fos1 = new FileOutputStream(infoFilename); + fos1 = FileStreamFactory.getFileOutputStream(infoFilename); info.writeBytes(fos1); String keyFilename = getContext().router().getConfigSetting(Router.PROP_KEYS_FILENAME); if (keyFilename == null) keyFilename = Router.PROP_KEYS_FILENAME_DEFAULT; - fos2 = new FileOutputStream(keyFilename); + fos2 = FileStreamFactory.getFileOutputStream(keyFilename); privkey.writeBytes(fos2); signingPrivKey.writeBytes(fos2); pubkey.writeBytes(fos2); diff --git a/router/java/src/net/i2p/router/startup/LoadRouterInfoJob.java b/router/java/src/net/i2p/router/startup/LoadRouterInfoJob.java index 0374922af..bdd3b9867 100644 --- a/router/java/src/net/i2p/router/startup/LoadRouterInfoJob.java +++ b/router/java/src/net/i2p/router/startup/LoadRouterInfoJob.java @@ -21,6 +21,8 @@ import net.i2p.data.SigningPublicKey; import net.i2p.router.JobImpl; import net.i2p.router.Router; import net.i2p.router.RouterContext; +import net.i2p.util.FileStreamFactory; +import net.i2p.util.I2PFile; import net.i2p.util.Log; public class LoadRouterInfoJob extends JobImpl { @@ -62,10 +64,10 @@ public class LoadRouterInfoJob extends JobImpl { if (keyFilename == null) keyFilename = Router.PROP_KEYS_FILENAME_DEFAULT; - File rif = new File(routerInfoFile); + File rif = new I2PFile(routerInfoFile); if (rif.exists()) _infoExists = true; - File rkf = new File(keyFilename); + File rkf = new I2PFile(keyFilename); if (rkf.exists()) _keysExist = true; @@ -73,14 +75,14 @@ public class LoadRouterInfoJob extends JobImpl { FileInputStream fis2 = null; try { if (_infoExists) { - fis1 = new FileInputStream(rif); + fis1 = FileStreamFactory.getFileInputStream(rif); info = new RouterInfo(); info.readBytes(fis1); _log.debug("Reading in routerInfo from " + rif.getAbsolutePath() + " and it has " + info.getAddresses().size() + " addresses"); } if (_keysExist) { - fis2 = new FileInputStream(rkf); + fis2 = FileStreamFactory.getFileInputStream(rkf); PrivateKey privkey = new PrivateKey(); privkey.readBytes(fis2); SigningPrivateKey signingPrivKey = new SigningPrivateKey(); diff --git a/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java b/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java index 967bc7a79..f3905f89f 100644 --- a/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java +++ b/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java @@ -25,6 +25,8 @@ import net.i2p.data.SigningPublicKey; import net.i2p.router.JobImpl; import net.i2p.router.Router; import net.i2p.router.RouterContext; +import net.i2p.util.FileStreamFactory; +import net.i2p.util.I2PFile; import net.i2p.util.Log; /** @@ -93,7 +95,7 @@ public class RebuildRouterInfoJob extends JobImpl { String keyFilename = getContext().router().getConfigSetting(Router.PROP_KEYS_FILENAME); if (keyFilename == null) keyFilename = Router.PROP_KEYS_FILENAME_DEFAULT; - File keyFile = new File(keyFilename); + File keyFile = new I2PFile(keyFilename); if (keyFile.exists()) { // ok, no need to rebuild a brand new identity, just update what we can @@ -102,7 +104,7 @@ public class RebuildRouterInfoJob extends JobImpl { info = new RouterInfo(); FileInputStream fis = null; try { - fis = new FileInputStream(keyFile); + fis = FileStreamFactory.getFileInputStream(keyFile); PrivateKey privkey = new PrivateKey(); privkey.readBytes(fis); SigningPrivateKey signingPrivKey = new SigningPrivateKey(); @@ -146,7 +148,7 @@ public class RebuildRouterInfoJob extends JobImpl { FileOutputStream fos = null; try { - fos = new FileOutputStream(infoFilename); + fos = FileStreamFactory.getFileOutputStream(infoFilename); info.writeBytes(fos); } catch (DataFormatException dfe) { _log.error("Error rebuilding the router information", dfe); diff --git a/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java b/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java index 9c75f5328..b3901f07e 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java +++ b/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java @@ -81,7 +81,7 @@ public class EventPumper implements Runnable { public void stopPumping() { _alive = false; - if (_selector.isOpen()) + if (_selector != null &&_selector.isOpen()) _selector.wakeup(); } From 502257542928a7a3548a7b201928ca76781fbf99 Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 13 Mar 2009 21:49:27 +0000 Subject: [PATCH 046/688] - Deal with conflicting bouncycastle libs take #2 - Disable NTCP - Shuffle the startup/shutdown tasks some --- android/res/raw/router_config | 2 ++ android/src/net/i2p/router/I2PAndroid.java | 19 ++++++++++++++----- .../src/net/i2p/router/web/ReseedChecker.java | 2 ++ .../src/net/i2p/router/web/ContextHelper.java | 2 +- .../src/net/i2p/crypto/HMAC256Generator.java | 4 ++-- .../src/net/i2p/crypto/HMACGenerator.java | 6 +++--- 6 files changed, 24 insertions(+), 11 deletions(-) diff --git a/android/res/raw/router_config b/android/res/raw/router_config index 19c609542..0e7cd2ced 100644 --- a/android/res/raw/router_config +++ b/android/res/raw/router_config @@ -5,3 +5,5 @@ router.decayingBloomFilterM=20 stat.full=false # no I2CP i2p.dummyClientFacade=true +# for now +i2np.ntcp.enable=false diff --git a/android/src/net/i2p/router/I2PAndroid.java b/android/src/net/i2p/router/I2PAndroid.java index c08670e36..bb77d290b 100644 --- a/android/src/net/i2p/router/I2PAndroid.java +++ b/android/src/net/i2p/router/I2PAndroid.java @@ -12,6 +12,7 @@ import java.io.InputStream; import java.io.IOException; import net.i2p.router.Router; +import net.i2p.router.web.ContextHelper; import net.i2p.router.web.ReseedChecker; import net.i2p.util.I2PFile; @@ -29,11 +30,6 @@ public class I2PAndroid extends Activity _context = this; // Activity extends Context debugStuff(); initialize(); - - Router.main(null); - System.err.println("Router.main finished"); - - ReseedChecker.checkReseed(); } public void onRestart() @@ -42,6 +38,16 @@ public class I2PAndroid extends Activity super.onRestart(); } + public void onStart() + { + System.err.println("onStart called"); + super.onStart(); + Router.main(null); + System.err.println("Router.main finished"); + + ReseedChecker.checkReseed(); + } + public void onResume() { System.err.println("onResume called"); @@ -58,6 +64,9 @@ public class I2PAndroid extends Activity { System.err.println("onStop called"); super.onStop(); + // shutdown() doesn't return so use shutdownGracefully() + ContextHelper.getContext(null).router().shutdownGracefully(Router.EXIT_HARD); + System.err.println("shutdown complete"); } public void onDestroy() diff --git a/android/src/net/i2p/router/web/ReseedChecker.java b/android/src/net/i2p/router/web/ReseedChecker.java index 96eccce51..8c8ff7bf9 100644 --- a/android/src/net/i2p/router/web/ReseedChecker.java +++ b/android/src/net/i2p/router/web/ReseedChecker.java @@ -30,6 +30,8 @@ public class ReseedChecker { System.err.println("Yes, reseeding now"); ReseedHandler reseedHandler = new ReseedHandler(); reseedHandler.requestReseed(); + } else { + System.err.println("No, we have " + names.length + " routers in the netDb"); } } } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ContextHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ContextHelper.java index 0aa250654..7d9b28e89 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ContextHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ContextHelper.java @@ -5,7 +5,7 @@ import java.util.List; import net.i2p.data.Hash; import net.i2p.router.RouterContext; -class ContextHelper { +public class ContextHelper { public static RouterContext getContext(String contextId) { List contexts = RouterContext.listContexts(); if ( (contexts == null) || (contexts.size() <= 0) ) diff --git a/core/java/src/net/i2p/crypto/HMAC256Generator.java b/core/java/src/net/i2p/crypto/HMAC256Generator.java index 7d6f67a76..0335d1e7e 100644 --- a/core/java/src/net/i2p/crypto/HMAC256Generator.java +++ b/core/java/src/net/i2p/crypto/HMAC256Generator.java @@ -20,10 +20,10 @@ public class HMAC256Generator extends HMACGenerator { public HMAC256Generator(I2PAppContext context) { super(context); } @Override - protected Mac acquire() { + protected I2PHMac acquire() { synchronized (_available) { if (_available.size() > 0) - return (Mac)_available.remove(0); + return (I2PHMac)_available.remove(0); } // the HMAC is hardcoded to use SHA256 digest size // for backwards compatability. next time we have a backwards diff --git a/core/java/src/net/i2p/crypto/HMACGenerator.java b/core/java/src/net/i2p/crypto/HMACGenerator.java index 52b29e19b..29d76cbd2 100644 --- a/core/java/src/net/i2p/crypto/HMACGenerator.java +++ b/core/java/src/net/i2p/crypto/HMACGenerator.java @@ -50,7 +50,7 @@ public class HMACGenerator { if ((key == null) || (key.getData() == null) || (data == null)) throw new NullPointerException("Null arguments for HMAC"); - Mac mac = acquire(); + I2PHMac mac = acquire(); mac.init(key.getData()); mac.update(data, offset, length); //byte rv[] = new byte[Hash.HASH_LENGTH]; @@ -87,10 +87,10 @@ public class HMACGenerator { return eq; } - protected Mac acquire() { + protected I2PHMac acquire() { synchronized (_available) { if (_available.size() > 0) - return (Mac)_available.remove(0); + return (I2PHMac)_available.remove(0); } // the HMAC is hardcoded to use SHA256 digest size // for backwards compatability. next time we have a backwards From 66eae60c48b1ad1958b68657f560dd1221521f60 Mon Sep 17 00:00:00 2001 From: dev Date: Sat, 14 Mar 2009 16:24:17 +0000 Subject: [PATCH 047/688] removed some hosts --- hosts.txt | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/hosts.txt b/hosts.txt index 95ca28af6..b30da6363 100644 --- a/hosts.txt +++ b/hosts.txt @@ -1,27 +1,6 @@ -tc.i2p=3RPLOkQGlq8anNyNWhjbMyHxpAvUyUJKbiUejI80DnPR59T3blc7-XrBhQ2iPbf-BRAR~v1j34Kpba1eDyhPk2gevsE6ULO1irarJ3~C9WcQH2wAbNiVwfWqbh6onQ~YmkSpGNwGHD6ytwbvTyXeBJcS8e6gmfNN-sYLn1aQu8UqWB3D6BmTfLtyS3eqWVk66Nrzmwy8E1Hvq5z~1lukYb~cyiDO1oZHAOLyUQtd9eN16yJY~2SRG8LiscpPMl9nSJUr6fmXMUubW-M7QGFH82Om-735PJUk6WMy1Hi9Vgh4Pxhdl7gfqGRWioFABdhcypb7p1Ca77p73uabLDFK-SjIYmdj7TwSdbNa6PCmzEvCEW~IZeZmnZC5B6pK30AdmD9vc641wUGce9xTJVfNRupf5L7pSsVIISix6FkKQk-FTW2RsZKLbuMCYMaPzLEx5gzODEqtI6Jf2teMd5xCz51RPayDJl~lJ-W0IWYfosnjM~KxYaqc4agviBuF5ZWeAAAA -dyad.i2p=W~JFpqSH8uopylox2V5hMbpcHSsb-dJkSKvdJ1vj~KQcUFJWXFyfbetBAukcGH5S559aK9oslU0qbVoMDlJITVC4OXfXSnVbJBP1IhsK8SvjSYicjmIi2fA~k4HvSh9Wxu~bg8yo~jgfHA8tjYppK9QKc56BpkJb~hx0nNGy4Ny9eW~6A5AwAmHvwdt5NqcREYRMjRd63dMGm8BcEe-6FbOyMo3dnIFcETWAe8TCeoMxm~S1n~6Jlinw3ETxv-L6lQkhFFWnC5zyzQ~4JhVxxT3taTMYXg8td4CBGmrS078jcjW63rlSiQgZBlYfN3iEYmurhuIEV9NXRcmnMrBOQUAoXPpVuRIxJbaQNDL71FO2iv424n4YjKs84suAho34GGQKq7WoL5V5KQgihfcl0f~xne-qP3FtpoPFeyA9x-sA2JWDAsxoZlfvgkiP5eyOn23prT9TJK47HCVilHSV11uTVaC4Jc5YsjoBCZadWbgQnMCKlZ4jk-bLE1PSWLg7AAAA -nightblade.i2p=nyErwSseXbsojcWtNkDyUul0YULtqr6qyWSzIp639Ygpe8juCdgPMLURVXcmlCvo~QPoHg6zt53KpgpGvB1-Wv2SGvc2Mvs~o8USw3ius8fP1URphqcBbulK8Ci0bgknt0kD0AfxqfMz-p~xk1QEMxq2kZEoB3oyIIFnQlpb2ByS74Lx8iKzXTrwWk19I3Dvu4nIq8CBDDwu3lYoCD2kC-jT5pjgglverGPEGN4o55LYVTtfSg4gAJFZeaE4KjBR5P1z7cca6UDjGMWfR0iCa8P3qpkY2ODYpk~8w2xgBbgDq-8Hzik~uraHc598ccS8QpwB0f0Jw~2PZcTjOPdZ-239U6p3tESXa7FXzRBCujv4Bx6CVFRhCmBHpyFnCD-MugZ~vR6XFSS2XBsCT~duXKq94HH2n1iAWslG4Vu44ut1JVhDPFzp~Dk7wujB0tCo2HXH2icRQxOWe37foU4LZSJ4oMpFDACBzwSfcZdIPsVRxGttKQx4yzgffR1Q~Jl7AAAA -bozo.i2p=ubMPUwY0op6B7Jr8SAjY2bQXze8m1sT6xF2N0cv43dIHwLTO0gUqn7FCP9jXZDodE9DR3fu8fG8x1Yz1SpXFk4WtFmuDuhdN7uaHuLIQ71PATC2GRhDS7NXqn7GsVZgQxhHKenaE5BKjIKt2amZ2~8CM0qBKTqwievUO-Y6zG~-8l~RpnAxDZUMOjKKy5R3~jEN9DFZCaKvXSNcOVFjZRGaD6d8NvkAJjndHdE3bFSJUDNv0qhhp09-mm~Se9C~FzjrAbhgappdNRiwQepXTWqRbjjt6lUPT2eJISPDxYxoeZkBGZa9XmfO9QH3hoMo0g~RbwLeBqtgeRGhVgFiC4pN8lFt3z7j8L-12575SUeOnJPIm3hQWXdTjKX1hqf4LopYBG84N95IeydPJegsmkIkAMzEb0d~-UZfVSP9yFgs37j~Fds5yxBsu-NFc6qmZihpXEd7jrfX1-HuJVmXFmwaZgyumRSRDbj714wxr7RP4Hb-liA3JrU-FbqNQFoTMAAAA duck.i2p=eFJdRYFmtjcpx9-Mw3JBdF6fwtb9cHRBR103Q8IGc91Jdfn0iYzK6Xx0oIzPvpbD4yOlPQm-C-7eTahrAkHa2FMCRTiiVq2a0nBp37W1uTvAToV-MKYPKTdFMxrXxvjS7qaSUXdJRcPaPexolfx-Gcjh~rN2tKCh0mz9beueiQ18~8qWGh6hUMb0yyA09ipL9vIkmHmooLwT9AZyzHXEzdLXZe1P~CG3L46QaXp9aTD7EkAwG6VBMjQrGiSJ-9FFhx4QcYAZWM-dfrtzbbVYfHxqQRBwzB27zLlaKVaqu4enC0N1cW1yy-cjnv0Wxokqe62B2uPzFYtloxQpBPfTLQZUfUzjskY-3Yg2AdSbEu37jYsnAJA95AlLz4t1W1vPTNiXzCaRqkkMX342SUkJK-HZE3wTjAgGPqJ9vMaC2YexPFViTxEO2Q0jDSjdPHG0D769qehkN5Bfb8MEI-yr3g9zLaY~w4r6T38ap33qfyISWlIJ5qCPcRVkeK8OqZ4vAAAA irc.duck.i2p=Bxqr5E7-56oJeya1lsDLBN6L1gKme-FUS6Bh~TQS3HswomK9rpjrYNeqBTBoE8TCFl161~FI3soWqbnmFhIdhskausZsO0ez5-4IXMJW8NTilWqXQ3OJxA20M9grohx3RjkZgXU1ooTx7wviSHtXQYiiqnzGnIzmmEZo5-Xx6VjXakctebWwbi2PrsE6XLxrxXBzB34l4KlVsyX504BJiOT6KXNaVZxvG61GfGVfNHdeXljMDE5d25UdFC6RJdDnJ3Z7Yb7EjAww78aowbR0VCfJDH~cB868-VOKIxmor3Rs7giaLXmUyW~GRtFX10COJj5V6BrhKs61XOXxfbyQKGVXZ0mM2A8cdZE1ftr96SZgGy~V8uUHKvoa1HpjMNrPL5Tr6EGfJxOxAy6PHwotn6a8UMnCZgEdTbQ6U3BTywU~x3SCAQvfOT~dl3sZ-5ujYWNFRp7RhdY-WHn1Kj59MfU-VpczGYdV3bRkwT5lpIjST~vopLfkYUeB6gcVSr49AAAA -jabber.duck.i2p=AzR8Zrms1sLXflvaZZ5CI2TS1cRvlO99D5Jh641-KS0lMSQPUnSApTfy9R4aPxZ3SWwpZPfSSGN-e7~kw8ucGQVB9EOHnht7WCz5UKC423vX4fFZqpTyqemDBx0CYXfhqRf-~7mjslXigR2nbQwh8NMl2bgLPknGe55wMlZPfsacS2WKNQYbdOvrgG6Zb6DK8RzTDrsum5h2jtIorv3CuMrNKLdfziXBDICep2Zp9jKDPBHgSsRgl6dW8A-5~WbkIPLpQwqeTzw90r8uJ9EIln~GlFulblJprCfLzxJ2LAFpxNcTkqzOJPElFfhjVrJOYbm6IfllBlHMVNbJjYTOsiJLqODxMiVt3pQx~AGFrx5mVtMUcz9cu9hVPnz2ZVmIDB~a4UGkexf~FCHhaQ6Emnr4nnZBiFSP3f4nASOMHLOs4SE3UKiYUB3ntmJssVdD1w-ZGrovHynkvXMjrJ9GhIeldFm0M5cOAhra0OtJagZ-bgphR0v99ADGPl7X5DoGAAAA -home.duck.i2p=dQmTRCAgcKlbt3VKdW-iFI4zd3WnDaM8ULsfIZb8oEdDuTq5Sl~1JguWH2Wl8PMRuxkhHpfOZ6IUJZtxo-rr3tfaDsYPl8SZSIbvGBs5QooLl2NNTPA~gQMJOHZkuM4KWMWYnFRWoaqAUt9KAmdyl5vgF20xv0rmSgRmm8t6c3QSTfNWSeSDYkRgEumaUKP-kRJnSKNeIOez1N2HQSlaDu~cH27E-VqEuAu8HppI87dV~v6nVKXR9-KgHzCdfHXxy2pxV9trJ8vmTEZO53ab11-BdDMoZjOv5sjMUADNDnhIW1RJ3c-ZcF9OotYsUrf7E1fRo3BwGg27LKhFnyBDmzI0a5uQJ9HCDlzSQ7yfHNMqNCtMfHgZE6Bw6F3tJfr5ISP1j5ibfj4k9tODyfWVqTionQfgrP5szHdyGu14Y4AtDW01-d9BNmBTHA53jCl~Jq6Qm5~CKcGZGS-05iyhmg9x1FtR-rW5Vf6o8uitSjtE5uPt~UHXacaGxQMJSy9vAAAA -nntp.duck.i2p=5SIb-1I6SVwFlW~XlYTNJU1NCpbyHJ3co8KdrpDxBosnHtdsZzq2qzYzGDfvkKK3WRoKmRGYCE77uXAvOQjEyWoTkkGeY2xl3B1t4t8K4j8dFvYtDJkRGePxGaY3MW-9ANGUQsGOhh6qq4lcQ7rv-AWyDftfZ3pGaHc79VukSo7-6OSh4WDw~0l~DjdFjQsZOVNTbKIfDxzSjKnkTpNc73nrLhRE5nmnMj7bljTzNtAHiVf~hFMndPxF80JZt6erLqy62-~XbevTWpn2KCTjYzhYUkwYW95-SW27ph5rIVZneizLEanSPtdUkDGhMEjjmy39Qr5rD5i9H-ioIP3NppRkjPwrtI9VJpsJtzv75uANIXy48RCBVXXA18ng8o7FVevdyjVb~C90IXpJF~mT7DI94rTT5QnzlpJVCid65kOoNzXFC80lP-iiwXMMBEcys8RGA9hdOvagkFDJ64l0GwrVpFGO2AVMsMenR0WHEvJOjNv62sT6rlntO3ZywhNXAAAA -pgp.duck.i2p=kRm2KRa2EiWO~XQFYxSg6UM9lXYHZ93IB80j3ShFhJOOZ4AN05BrTGjMeT9nhn5n1LMEUhy9HJuN-Lhkd89Mbhufk7di6M40wqySns2g0T7XUCFUgQ8kl4~BoY8M9U0pHpM3RJcCw8WJEFGEk~fX8tgC4XQB43UIXfrdKTlNfKhxZODE4UdvlFfFdOYMgH53x8UgMZUprn~URTRNg1uwcaXr2luMwts6HnDt1bDd8elitsWViOJiw42yAFMqBnf-7mhiTCsoYg-nsOiq0Jirt58cBjAGUL5ujZo~vfXkLslDKjHOP32Y7HIi5ANEsbbRr~8QrVUojnVoJwFXs8BQBTevRpGLkCSLnKa31jNSt3msOnEP-n729Jabwj8o3pdRk9e-3~~y9gfj4bcmpSH9sOJoqmipXDiqOvv0tL9twERqse3tAwjE1NgXTvl5e2Zc~F0xJ-L2aG~6BX175ihjjEiYGWRYaoEisHMZdMtivsAK92dKl1JVEkuDF3W8KjL8AAAA -scp.duck.i2p=AE2Ff7x-tJMI901UKEEXkcwb9~5KZKs--VBXEoZnnpC~mlgnqGsZr8MBvRvs6xAzP7xXLeL~cHQ~gvPFT60obEBitwH~JcKMahhJDGb0p23Z8B81QajXijypDpVfMDfFbMiqQGctXhnBidNKWe7HQEcJGZer-SCu3m8CiftcZ0t0g5Y67B2AtqLhza5xfepq6FQ-Tl6fpgS-UDcGUAqMpLUYfrBFB11oM09TRVsfNp~NIAvMdrvXiX8dTDUHJM4FRdpV2OsJiyDdgf5-W6s6ssxohviT5MdijUSYQw5sWj8~9Xkv~~aa11Dvty0E7K9IhXAeJlwBe3VuDizsAJGnFIU7PwZXV7-9-28Zgldarg1P-rh2QDvCqF1vjdyZe99BBcTiCqvE3zx2N-9eT~FeOURrgE~2rFBKVBZfuAASSORqiXIeAANKklCW2pGQrM3DkX8ybi93Weg~eBjwGQugO1FRZ-ISq7npRWruMiC7f~fWLqkmRUy9HahPNp8E41s9AAAA squid.i2p=8fiWZbRjOEzrj5n4jSqjN9UN54wTrsgEjqn7GRUQpLx1svf8lwckXPV5buP2VEYGo~83ftkIcDKyMLXkxSr8jqbb4yAEgPe2~w7OT~8LNvmVPz0xZhIO6fiw0WU4xD9x5PG1spYjWPnLFv7pynEvBpWFXaUlCacjWL2KkfViiGPXvveQqQIZs7VkxVD2HK-oT4yIjdqHpc7Y8nEV9xwds3-LX6to5p70jFe~kZJA2fjWHsSCm92TtPvoR3aTlL1VS3JUKpcH6BL5irsh-SKODEtDRCErPQI~j2SHzhcD6dMUsI7bm3AxivjFpSQHqyXLmLVdxECYsMET~nIHmuv8NYTHQQ0jM0XTQzwnQwEHjHRBd1~spR9uS2~LSnX4Pw~X1WTknJpPK1f4Cu1O44X4RYcLRCsxpEzytUBXA4BQizrbYgOJVGQa9-PNGxJeZsnNUZ3PxUi23Oh-c4jUaB0ZjyKTWJSpzj1GI6vc-gW-0ixGJ358TCSbKgqdBv~g~f7yAAAA -fillament.i2p=Pa50z7pU~ni5nWwUdaDZ5CJxG0fYjoarm9wlxnkgX~wHMX9RPgQAXz~r0Rr1Nadt2OA~dr9RMHswrMok0hutK3JZuFD707D7FjmWW2w979Ee9I3zxKyx9W5A2eE49PPT131NLa3uINXLXOYVA5frfDOmM75Dmvm533r8e2kloemJyj22HpvRiSXiQYgqYJGDMH3Hlnwk884eRkQu7P8DJL~hcuKpyY0FzLZtfxTNsdSavGjl7rKPMzJeP02-9TS5TkdHokZrstVM5Cn9ay1c8DQrMds7SPXJy13Ut34QRjb65JxRV0mrnY3teXewW6QFvFMXJCsf5C3i46t-9Fufy5D1H8cSd2Tx~Xl71MC5-1AJCcIS01Od23E9tFY3dU7IOSRhKC~FiAslyk3x-BnBSpKxbgl1w~LArBm5plNiCiUemJU88xYdn1UyukLer~yNrEHAWspckCRkXFwmUtPkaGNTvfwBSYns-skNHSd7MAUUoS-ewStBdmtnDgRkwSG9AAAA -eco.i2p=KhRG6BGxVPh-BUDDfIgy0570cppTdighytcaGVR0HzQo46tgRMBp9Shlpax5FQX4nLHn6qHQbdFFpFbAwe7CiDhURCVF9-CxYmPurGadxlMPHMjz9O3jHX0CQiv2iULsk4XPrYXF3PqBc4t1J6vVyBVO7uTUhDi0gF6sN1Ro-1GLcWcsoR8Kx-hb~Z4WqGD0QAROOBPHnSRSb236qVBkhFvSkfigfBq3jFgEsttadYJA9ZLSUj1XrFFRBjz~xkRra8kJQSZl5dbfg-eZMlL49h61U6Uta5n1~tL6sarmnl9CaTl2Qo27SKB1OmMLeZEteA5G0-~LiOjN0nxaKpwrCjKIOyvwbQy2QqE-GEb9m8SN8nC2bwYK9fH15pTMHY8GvPYGcUukbF6RhefwzkEJLZ~PaAECrZYuLsn9KE5C35uRnlWJiuJlJ25hG7da5tFMyDB95efzq5IMxPeI0pMigRfuVfRSaGDpNos6JxjfEIX8umk3jIJUPhz1d8gP4QgrAAAA -aum.i2p=09hSo56PTtkFLUEt1iUTO7zYTnO-B~ogsIsyyPWif6q1Iz4wz4JoBflAWZtedPmwGmH0nly4HYUS0gAADoUmBUnXwemmO6dxT-hPQkfEW-A7b3uEvYQWIN~kyFyg0Pa~FN6TaD9kGFttBN-GE4wxiHhXmWdzWNDVb0q5PVGnxMm6Jleik8xkd2Lgeexze8rIv8LCocAWx074USVkbCVQwoFi2P7EnjLq8odSz1cJAntbuCFeUZcjbslE3qmlcTFMCNCZXWKVzn7d5m4oszCQ83NidgekwxJ-S~iS6mBwIS0XKI--4iXiKXzzCFf0KtYfEWpvKCuqNJOcU8vQWAA2-i7~K28aLPzccDQn7acXWLKRTXF3tf0i6e-lSx-X6WTSWK-fuNitjAtKu~jqO10d~bCk7y~UPL-XwdH1XSTbk-Phhk7UoBTDiHY6zQHdD~iAzXER~9JXsJ4UoIrGFVabg7frzSt82CN7Ek5Li4AMz5gg3wq9H9HUa7xM5QfGIJpXAAAA -mp3.aum.i2p=vBOu1caCAajaL5WRMuwk4LwfXrlcn0WzA6iHUKV5ULhaBpJb9pR3SZpnQms2Ot2c5Fvu5I6Rp7WF6QRcyasAhUmC915ap~2~VG8KCDP0z3Quh1-eqGcmzErsIfXdh09j3CWuxN~fH84hd~KswqGudFkWtFTM9RcuQUGSC1efG7uF03uaDI-DKu7eb4VUV-hmpXb3Lqntgo5qSMBMmjyUND-f6RBoXnqM005mUZJpMoYfsBhnUEq37GG8u6P9T94nlMmtz9R3gNURpBJJKPlnEqCBN4mlE5rwspQ0ovxAlogVMhSCpQ4jr6cyWIbNx-nMzKGDj~hMQ0ndbVnWw3EDC3KsPnRnDv0yVz8Fc1YpoPwerHej7VnTupDKxc4T-j8XNA1dN8SfPmaKYEPfavlmy7HFAGcsbmeRZOq-PVvlDdrKNflug8Ysodd5XkDbh7y2k1pRDjwNBQ1EgDVAtL05-i9jerqekHkbFKJ6DlT76f06vj1R2v9qlQzAYjpcKbI3AAAA -ogg.aum.i2p=wR2ETKWn-mxsTurWwmSujvjpOiIjLg5TsldFUa4YFTgiRZdFIB-bXuK59shfnchlEgAZR0IR3~hH-O8bZ~j6wVBdZWq7bGTmyTxQ3MeYPdqK7wH7Jp147YUabFlqJkyI~DluwBDylJrIUyc2qw~ogJ67x-KyzIF7JLnoCC4E-T8Z0vmTAFWSa3XC-ncghrdZQCqEXaCMlG9PN~a7dcDq~qdWoNoyFcgLd0IQfE8JuJ1wSvmWUNEd9vkB2Zuu3EoSoDv4C53Fc0YhVACNug~VEEL-ZBGcCBcpVNud8dOMq-CbavkD5yKqHlvq~uzRi6BY5ajHI77uepJygkHcsm-8T0PXWXdc5ib4TtUI03tPkTar4Y2iVocY~oLk2jh7pQKZNioHJT4StWv9Pj8EWaVX4-emQB5kZOBwZItjo~EAGEoBT14NSM7CmKClgc6sg7fpvWF~-cNHkZsurBndni~~FKmUeWoO0FRQRF9Ao~C1DOt2V9oBbEW1~n6anjL5V~IyAAAA -fcp.entropy.i2p=jy0D13oJVmxSa1MltstV3FOfA5e2WAEEZJiYOJIZSUOcNnAkaR3ghE-AX4vuqyQPyOEUydpauD6cS8vfx4iZkb2U3ddlLcOU3YrFKdLrySpGtD~V126VO-9nOJFwDQOOaKAsiVGRKtMPLC64GkpU6TWSIhiVYWb7WmeAHXLLlR71DtgamAxEIlP3VhytxlS3vuvAoEH9ItsBwkv4N~7jec60WMQINl~c7uDDsuzKFY8wQlkHnLFQJCQ0VExfNYqK9nZ3x8TXNPmNKTMMQ1CUCowgwR783U7UAYqsxNrpkuWvTleadn7QcR9i2v4~L9zOeHd4nHBy8PAjO29g6nf6DIsYhg4c2HYnPYzktQ1NIElytmW83BhbXJXLgNBs1eI9gDaQmOiXi74FMgfg63IcXCYWeqCdwEzSouSphaXEHDcZZVTx7DE9R-1Bi4Dt~KvPOFsAoOqsjHCpHq1gS0u5HiL0hkSm1I1EMk4JBY0j4rM1nAt7e0ix~WiOz5jXlTVSAAAA -http.entropy.i2p=ON2Ud-B0-pJKbTR0Obpjp9wEG8grUpu55gEn5Mz3-dkVkPhHvHK6iLasr~P~Rf4kPPZvn-eK7z6rAVfsAytAJ9pcTH3lXERTjkd9FzVJJ0twbZSQ~XzX5d-24IPIMf00KegjnDkRJ82cRMKa-u4H-ayei~Y7xsSx64zC1eHv6qFxavtql3zRrS~du41~EHtpjqOtOo9Ea3lfFjhm2jUIJpYyVHqve3WbTfMBlguVALwGZIfenph7oQ1Hx~OnEtaviWuOEpupjm11LS9xqCNsccaEpJGvGt6ijxd9hrEuQZ5Ja~C0fNxf3xNtgRaUhakA8Xoo~jz8rCkV2vYQo58kj0E5xYrUQczomj8y-eDBZyq29BP8pfe2G1u3hpHA1z470LUeMPk8qVx8Cx8ZKmSK9XCvOl7WCnFS2~UUfzxbxSxPn9LfzxDZp05AVi9t~hJg-zkrL2n1wfEnScuUFapxarwK90rlAxNSnau-K61WfcXqyVMwDxl3leJOeVdHqhpbAAAA -www.mail.i2p=Y~V8YK2M-my6-Gw0lkrkJouxeqPuB03idp-4uT9pkIXCA5nki9m4YFfPObSPv0E7c2shBxwlUo-6beaRQ-7tCawJssDRc0C0PhRj12QUYYdtZP7JS8SQXy68gZIylY-wfyEXleIC4mYY5mSthhdUUfyo1lqzrdHc1NpjPBxRJcyMBFBGUeM7Of9E9M518jXpVl0bAmxSnr5dy7sgKAVNufzfqIBfEHnmL2ZYH78FoGnPybsV0F9~154emkmt89ZUbx0BuYvH3kT1zin8pSxKw1NqxvqYt7p8CElq1--U38rO9U5Y~kLB9f6F3RYJdkl28ANkvdgJUgqiHLVI5oPWATrJLAOokyGKhK4Xl4Bjp4SCuemxHwTOGyd-4Kl8cO41u3w1LksndX9stkV6U1X0gL9BeSIoa1997IgMLVbUiDMyCz7-cA0y2tc0EdQdlpc2y77nTdo7z23dMSJzWDXsrfmLhX7M24D70htLLc1dpwZ1BUEvM1uPqGfsBSrHdl-sAAAA -smtp.mail.i2p=gfSAYcvEsuU3oNGqeMpq1wZqH-whE1i~YCXEDwYzp8LrmukWsndvPER1~gF5QFrIp2RMXiietF3zEPtAJgevSG4ULxRU0s9MSAMXXlCACVhlf0m493J6kIYnkypOPC-Z8sulyF2kXM8BURLfSH57SS2uxLbx0hkc8j0kR9iys3ksxm5dgW-Rs7clAvLmmfASJkXZiU6DRhWbW84GbpAi9McE3ORhNLrWV5t1W4DXqzT0tzF2W0i8BEGns8XdOBQei9RAewzo5NRGPBmUl6ZKjEJ-2UtX189HPs7FcLknfsRxXhRcPQ1RombPezYCgcNhOgWY9owHq64mwGaDCnnpDSM01sdAuMlFfs1JJMoYNrILckjiHUNzV2XS8A0PIWdO4W0cT1EUs-V2a7Ocvg397HpR6Z4k-7fOrjs9yvpFsCPIEKYUD0mjr44N5pJIc61GGuNE~2ihQZGA3ju0OnUKTRZel3nK0rxl-qfFXsBsEB5vt-MTvKS73ZJdxUKWWzbWAAAA -pop.mail.i2p=aG6owmzirq7QZKYovpSVa4-WBLfI1uJ38cNmb6kkSkcS8A~JdoLWPj6eXieN8r3m7YJLxxyZhf3urmK9qJbiIPBp53M8bOSSkldpFz0NkQPWUWmYXiOrEsOBlugbJ8nNelDcOebqoieKBOTaF-WPJQil5C6RYdUy~PL50O6Qp-Hog1868zP26leYBBFiyzzWI3bOpOsgV~4bNXnqKQZeHXz1Ua2DkV-vDBpeamPzvNWQI6cNodf04PBKXK~TlduLZDK7v1yTt2LhPSBM5nE7ZRtS8KQIdh4o-nyYRmHjA~OQ70gowGpmsRqXHQxOpPro~C7w3gSe2N0zhqSHKstwoFJD-NmsBQ5opyOiKccATpWEQdAwmoICD6rw7TGG4XYXCtyTD2xxLffER0SEsJ-BJesKKhdm-qyEMAOQq00jatoEs9jBYoujFLFQMUgaDejRJdEHWkiGT~x4auosHGYavmrcm-0mdX0CWYgfjwVb~PORhBqHJ1G5IgRPjoZLuxiJAAAA -nm.i2p=UhUVbM972VwQgqS28SkGPtghCb~IpdpeMW7O9E7I3HtlB2I3XGbMeUAoya5RHsoG3TYxf~P6lA5IM5Z~mDZlcbZ~AG7255FE6Z3Jl9kfMArneou6AYaCfNBqNTRS-P7yX9s0Kss-vM63yBulxhS7CqmBKTZsXR27PNjJS0PMYsWBzciuy8UqUkE0YEhSLWSUYAXLP9FKs065CNjxsLumkkoF~MNUaBNEmbCrjpv5Ih9vrwz4XAJSE~S61qSMj6O-nvEPDVhTJJ5ymeoZnYMpIRt4r7FkCTH8vYSkXZkhqXUkLC11WPC33lw2wzh-irmIb5GQeab9o0-DuNQcvUnbK13Jxkq5XiilfK6kgKiPcEniqxMb-4paZAl8dj9Zp01LvhfjlS0c459Jv-gr7ZkjkX7hhTaEVvqwPyFgoVKnxQCitoZrK98WRKJwu7EQb-Kin2vzUsYfpGGI0aT68~gdr23oom2FsoZ~owVuur1h0bJr9mnCaMf6jaioQE7wezxgAAAA -mp3.tc.i2p=LRCWTiJouI6ohqtaVBNHWZe2ymjhtyj3z9KdeI2G2D9l0cYFsG0CRUVT5VPYOg~WykALRVBiL-2U24fbiQ28hhPdQgBMBDl9aQiZJM2hv~di0uVOdARhRSgCDgRQAWioAfpXeg6pyklzXU3TNLY4c2CRLe~9Y7wuLbK3lONsAApcxxKeHLrfGNkZZwJTKd7PcG78KAHU07E-TVNf4tQrOh8tSrHaMB1r7cQQv1Jl8mNUZWz4fGeNYEZ1wr04w74Em2~Z5K0VZ4mH5DhFGXc9ALYzZf6uOVzZKiUC0eOcdfGNdVUbIog2CYJGH69TgAX69d5vF~kEzvHSzX7RxUTt0y25Rlbi6rHSDF36xOfBrOUVnSPn5X~TdKLygz~zusYpRdGZwlsyOTKVTzJHKlU6Vp5Dofijj1bUwXDQ59XCpFUqEDA17nETiOO0H5jfieYBPS6Ke2cFTAhutSvaw~albCd1eV7RPqeGdw-vFfKoucDIEVUT40B5qalyFRKIxx3lAAAA mesh.firerabbit.i2p=BLt3pciNQcVIU-IGnTfSsrputh~b6drZpc1vH8qeA745XoE~nMCGtw8S7HGYX4uEbwk876nQRPV4qRwGtWWkWBs8BX9qX8NABoXFk4G-6NifB4TxEizC~ZAnXZ2uFs~nrqodhyCR8bBHJL0tzBYK3E36zc~SA-DKqQ9XSHWp7ScW7Z3cdQnYKma~x4u5eZmcS23uie3OIfOCk6pJOabtaE-YWRa1eUizhucI-ysm789GumjTo858vHR1mTQfrsPTqNri3yz0aIe~w06ifciq~UlNjVfx89lLEso1vmg8rfTQ-hwxS-qz-u3K5x5vtqdGp673vCvmEnQpU6GEycmkqoLCho9pNQzGbka-OVHg8fZqlFMeBfj2iLz~zlv170jvX6HTlMCNfBYnFqavs2RQJj7--dJ0g7JHReGMKL~TciQjxljrV5AoN-0afRzTZqtDg13PL4tltJm5U1~f-GcxlsjKLZAlv26LlZXsvTDU5plldsernv3fDcBev9UaKYCwAAAA chess.fillament.i2p=8xvXLwcBYu2MxqMAoG6DIahvkNAwBQs43kdZTg9SlouzC3JSQ25RHvspbrxWoGIVAlB~XCxVmBB6EolYEHoSZv2YCziOj4UVxEbVP7nHkh32-7Uc5T7xlcjk8-rsmZzdgz9NhxKVn2Uhp3xtcdVAiyG4mpXisvK-7NgHY-mXPNvj6goaH58roeDUZ5otJN7nCFmcRAUpaUk-nlQs8YfgSCCEsWKOWhsVnAaHwtqtDlpdTo1YKooACMRSa-DcV5W75Il80JEWBD79qpSAeONGAOMMPT~VEMwNNg001VG-UZbvyspZdxHaETw2yd7N9l3-mwI-sYcveTTnNXLWO8IjdgDLdUIP5qrIW6WS9XZIHRKqT2kpwEw7xsEgCA~qSNiZWeai8n6Zs0rLmdyeZeafVEEW9vr6CKcLZ5W7i1SMDqCKnzSbZdd2Hvpb9aWFf5foEjLt33n8w2KSaCUe4zmN~xuQMq2yiB-vQ9~5fgPmphlMxo3ca5MTCfbhshw5137YAAAA kaji.i2p=i-nivH~36AgabgzvS06oKHR~MLoy6NA0oSv8JuNJDLZB8FXEDzIiyzWISmw9XJm8I7QqZ1yFH8~xe84STCHHvMMIQQrfBmOUODLWbKZor~~KhiHdNLtfVZC5BpnXkBCJkklj~fMYSpWa0C~pVRrZl75RoGtBjDVP9P8hioTv5B6RC86g2ypBH5r093rY0wnzxSL8-ZuV3F~H48VYbqro8cRlbMmjx2oEsSHkDpQyjCMVkIYKaCijkSArqZTG~zX6op6Ng9CJwdrkjKSsbzjV6MLnE4aNv-jm2WaGGD5pR24h7e3ImDOGAr17tXRtmNX5ZEQ1udQp8qIhd8UMUumrnm962r8KJWK~9WNzcVeqDrIxaaxC7vcQmXxoPeEW2efbH0yKhVZ7OFu~I9cAapSe~aNWp9UK4URSpuJvOedt0axp3ORaaM-a5U7noW3Ao-HB83qfFEPU-6uUu16HNiPqCFMJiA0qODTOwHiyyx4HKQvbhjujh4mmknSbsuapdgR1AAAA From f70adf8da63afef2727a71026dedb30e0968454e Mon Sep 17 00:00:00 2001 From: dev Date: Sat, 14 Mar 2009 20:25:50 +0000 Subject: [PATCH 048/688] disapproval of revision '3ae245c48c0f90b0e70cf800de354e012801f6cd' --- hosts.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/hosts.txt b/hosts.txt index b30da6363..95ca28af6 100644 --- a/hosts.txt +++ b/hosts.txt @@ -1,6 +1,27 @@ +tc.i2p=3RPLOkQGlq8anNyNWhjbMyHxpAvUyUJKbiUejI80DnPR59T3blc7-XrBhQ2iPbf-BRAR~v1j34Kpba1eDyhPk2gevsE6ULO1irarJ3~C9WcQH2wAbNiVwfWqbh6onQ~YmkSpGNwGHD6ytwbvTyXeBJcS8e6gmfNN-sYLn1aQu8UqWB3D6BmTfLtyS3eqWVk66Nrzmwy8E1Hvq5z~1lukYb~cyiDO1oZHAOLyUQtd9eN16yJY~2SRG8LiscpPMl9nSJUr6fmXMUubW-M7QGFH82Om-735PJUk6WMy1Hi9Vgh4Pxhdl7gfqGRWioFABdhcypb7p1Ca77p73uabLDFK-SjIYmdj7TwSdbNa6PCmzEvCEW~IZeZmnZC5B6pK30AdmD9vc641wUGce9xTJVfNRupf5L7pSsVIISix6FkKQk-FTW2RsZKLbuMCYMaPzLEx5gzODEqtI6Jf2teMd5xCz51RPayDJl~lJ-W0IWYfosnjM~KxYaqc4agviBuF5ZWeAAAA +dyad.i2p=W~JFpqSH8uopylox2V5hMbpcHSsb-dJkSKvdJ1vj~KQcUFJWXFyfbetBAukcGH5S559aK9oslU0qbVoMDlJITVC4OXfXSnVbJBP1IhsK8SvjSYicjmIi2fA~k4HvSh9Wxu~bg8yo~jgfHA8tjYppK9QKc56BpkJb~hx0nNGy4Ny9eW~6A5AwAmHvwdt5NqcREYRMjRd63dMGm8BcEe-6FbOyMo3dnIFcETWAe8TCeoMxm~S1n~6Jlinw3ETxv-L6lQkhFFWnC5zyzQ~4JhVxxT3taTMYXg8td4CBGmrS078jcjW63rlSiQgZBlYfN3iEYmurhuIEV9NXRcmnMrBOQUAoXPpVuRIxJbaQNDL71FO2iv424n4YjKs84suAho34GGQKq7WoL5V5KQgihfcl0f~xne-qP3FtpoPFeyA9x-sA2JWDAsxoZlfvgkiP5eyOn23prT9TJK47HCVilHSV11uTVaC4Jc5YsjoBCZadWbgQnMCKlZ4jk-bLE1PSWLg7AAAA +nightblade.i2p=nyErwSseXbsojcWtNkDyUul0YULtqr6qyWSzIp639Ygpe8juCdgPMLURVXcmlCvo~QPoHg6zt53KpgpGvB1-Wv2SGvc2Mvs~o8USw3ius8fP1URphqcBbulK8Ci0bgknt0kD0AfxqfMz-p~xk1QEMxq2kZEoB3oyIIFnQlpb2ByS74Lx8iKzXTrwWk19I3Dvu4nIq8CBDDwu3lYoCD2kC-jT5pjgglverGPEGN4o55LYVTtfSg4gAJFZeaE4KjBR5P1z7cca6UDjGMWfR0iCa8P3qpkY2ODYpk~8w2xgBbgDq-8Hzik~uraHc598ccS8QpwB0f0Jw~2PZcTjOPdZ-239U6p3tESXa7FXzRBCujv4Bx6CVFRhCmBHpyFnCD-MugZ~vR6XFSS2XBsCT~duXKq94HH2n1iAWslG4Vu44ut1JVhDPFzp~Dk7wujB0tCo2HXH2icRQxOWe37foU4LZSJ4oMpFDACBzwSfcZdIPsVRxGttKQx4yzgffR1Q~Jl7AAAA +bozo.i2p=ubMPUwY0op6B7Jr8SAjY2bQXze8m1sT6xF2N0cv43dIHwLTO0gUqn7FCP9jXZDodE9DR3fu8fG8x1Yz1SpXFk4WtFmuDuhdN7uaHuLIQ71PATC2GRhDS7NXqn7GsVZgQxhHKenaE5BKjIKt2amZ2~8CM0qBKTqwievUO-Y6zG~-8l~RpnAxDZUMOjKKy5R3~jEN9DFZCaKvXSNcOVFjZRGaD6d8NvkAJjndHdE3bFSJUDNv0qhhp09-mm~Se9C~FzjrAbhgappdNRiwQepXTWqRbjjt6lUPT2eJISPDxYxoeZkBGZa9XmfO9QH3hoMo0g~RbwLeBqtgeRGhVgFiC4pN8lFt3z7j8L-12575SUeOnJPIm3hQWXdTjKX1hqf4LopYBG84N95IeydPJegsmkIkAMzEb0d~-UZfVSP9yFgs37j~Fds5yxBsu-NFc6qmZihpXEd7jrfX1-HuJVmXFmwaZgyumRSRDbj714wxr7RP4Hb-liA3JrU-FbqNQFoTMAAAA duck.i2p=eFJdRYFmtjcpx9-Mw3JBdF6fwtb9cHRBR103Q8IGc91Jdfn0iYzK6Xx0oIzPvpbD4yOlPQm-C-7eTahrAkHa2FMCRTiiVq2a0nBp37W1uTvAToV-MKYPKTdFMxrXxvjS7qaSUXdJRcPaPexolfx-Gcjh~rN2tKCh0mz9beueiQ18~8qWGh6hUMb0yyA09ipL9vIkmHmooLwT9AZyzHXEzdLXZe1P~CG3L46QaXp9aTD7EkAwG6VBMjQrGiSJ-9FFhx4QcYAZWM-dfrtzbbVYfHxqQRBwzB27zLlaKVaqu4enC0N1cW1yy-cjnv0Wxokqe62B2uPzFYtloxQpBPfTLQZUfUzjskY-3Yg2AdSbEu37jYsnAJA95AlLz4t1W1vPTNiXzCaRqkkMX342SUkJK-HZE3wTjAgGPqJ9vMaC2YexPFViTxEO2Q0jDSjdPHG0D769qehkN5Bfb8MEI-yr3g9zLaY~w4r6T38ap33qfyISWlIJ5qCPcRVkeK8OqZ4vAAAA irc.duck.i2p=Bxqr5E7-56oJeya1lsDLBN6L1gKme-FUS6Bh~TQS3HswomK9rpjrYNeqBTBoE8TCFl161~FI3soWqbnmFhIdhskausZsO0ez5-4IXMJW8NTilWqXQ3OJxA20M9grohx3RjkZgXU1ooTx7wviSHtXQYiiqnzGnIzmmEZo5-Xx6VjXakctebWwbi2PrsE6XLxrxXBzB34l4KlVsyX504BJiOT6KXNaVZxvG61GfGVfNHdeXljMDE5d25UdFC6RJdDnJ3Z7Yb7EjAww78aowbR0VCfJDH~cB868-VOKIxmor3Rs7giaLXmUyW~GRtFX10COJj5V6BrhKs61XOXxfbyQKGVXZ0mM2A8cdZE1ftr96SZgGy~V8uUHKvoa1HpjMNrPL5Tr6EGfJxOxAy6PHwotn6a8UMnCZgEdTbQ6U3BTywU~x3SCAQvfOT~dl3sZ-5ujYWNFRp7RhdY-WHn1Kj59MfU-VpczGYdV3bRkwT5lpIjST~vopLfkYUeB6gcVSr49AAAA +jabber.duck.i2p=AzR8Zrms1sLXflvaZZ5CI2TS1cRvlO99D5Jh641-KS0lMSQPUnSApTfy9R4aPxZ3SWwpZPfSSGN-e7~kw8ucGQVB9EOHnht7WCz5UKC423vX4fFZqpTyqemDBx0CYXfhqRf-~7mjslXigR2nbQwh8NMl2bgLPknGe55wMlZPfsacS2WKNQYbdOvrgG6Zb6DK8RzTDrsum5h2jtIorv3CuMrNKLdfziXBDICep2Zp9jKDPBHgSsRgl6dW8A-5~WbkIPLpQwqeTzw90r8uJ9EIln~GlFulblJprCfLzxJ2LAFpxNcTkqzOJPElFfhjVrJOYbm6IfllBlHMVNbJjYTOsiJLqODxMiVt3pQx~AGFrx5mVtMUcz9cu9hVPnz2ZVmIDB~a4UGkexf~FCHhaQ6Emnr4nnZBiFSP3f4nASOMHLOs4SE3UKiYUB3ntmJssVdD1w-ZGrovHynkvXMjrJ9GhIeldFm0M5cOAhra0OtJagZ-bgphR0v99ADGPl7X5DoGAAAA +home.duck.i2p=dQmTRCAgcKlbt3VKdW-iFI4zd3WnDaM8ULsfIZb8oEdDuTq5Sl~1JguWH2Wl8PMRuxkhHpfOZ6IUJZtxo-rr3tfaDsYPl8SZSIbvGBs5QooLl2NNTPA~gQMJOHZkuM4KWMWYnFRWoaqAUt9KAmdyl5vgF20xv0rmSgRmm8t6c3QSTfNWSeSDYkRgEumaUKP-kRJnSKNeIOez1N2HQSlaDu~cH27E-VqEuAu8HppI87dV~v6nVKXR9-KgHzCdfHXxy2pxV9trJ8vmTEZO53ab11-BdDMoZjOv5sjMUADNDnhIW1RJ3c-ZcF9OotYsUrf7E1fRo3BwGg27LKhFnyBDmzI0a5uQJ9HCDlzSQ7yfHNMqNCtMfHgZE6Bw6F3tJfr5ISP1j5ibfj4k9tODyfWVqTionQfgrP5szHdyGu14Y4AtDW01-d9BNmBTHA53jCl~Jq6Qm5~CKcGZGS-05iyhmg9x1FtR-rW5Vf6o8uitSjtE5uPt~UHXacaGxQMJSy9vAAAA +nntp.duck.i2p=5SIb-1I6SVwFlW~XlYTNJU1NCpbyHJ3co8KdrpDxBosnHtdsZzq2qzYzGDfvkKK3WRoKmRGYCE77uXAvOQjEyWoTkkGeY2xl3B1t4t8K4j8dFvYtDJkRGePxGaY3MW-9ANGUQsGOhh6qq4lcQ7rv-AWyDftfZ3pGaHc79VukSo7-6OSh4WDw~0l~DjdFjQsZOVNTbKIfDxzSjKnkTpNc73nrLhRE5nmnMj7bljTzNtAHiVf~hFMndPxF80JZt6erLqy62-~XbevTWpn2KCTjYzhYUkwYW95-SW27ph5rIVZneizLEanSPtdUkDGhMEjjmy39Qr5rD5i9H-ioIP3NppRkjPwrtI9VJpsJtzv75uANIXy48RCBVXXA18ng8o7FVevdyjVb~C90IXpJF~mT7DI94rTT5QnzlpJVCid65kOoNzXFC80lP-iiwXMMBEcys8RGA9hdOvagkFDJ64l0GwrVpFGO2AVMsMenR0WHEvJOjNv62sT6rlntO3ZywhNXAAAA +pgp.duck.i2p=kRm2KRa2EiWO~XQFYxSg6UM9lXYHZ93IB80j3ShFhJOOZ4AN05BrTGjMeT9nhn5n1LMEUhy9HJuN-Lhkd89Mbhufk7di6M40wqySns2g0T7XUCFUgQ8kl4~BoY8M9U0pHpM3RJcCw8WJEFGEk~fX8tgC4XQB43UIXfrdKTlNfKhxZODE4UdvlFfFdOYMgH53x8UgMZUprn~URTRNg1uwcaXr2luMwts6HnDt1bDd8elitsWViOJiw42yAFMqBnf-7mhiTCsoYg-nsOiq0Jirt58cBjAGUL5ujZo~vfXkLslDKjHOP32Y7HIi5ANEsbbRr~8QrVUojnVoJwFXs8BQBTevRpGLkCSLnKa31jNSt3msOnEP-n729Jabwj8o3pdRk9e-3~~y9gfj4bcmpSH9sOJoqmipXDiqOvv0tL9twERqse3tAwjE1NgXTvl5e2Zc~F0xJ-L2aG~6BX175ihjjEiYGWRYaoEisHMZdMtivsAK92dKl1JVEkuDF3W8KjL8AAAA +scp.duck.i2p=AE2Ff7x-tJMI901UKEEXkcwb9~5KZKs--VBXEoZnnpC~mlgnqGsZr8MBvRvs6xAzP7xXLeL~cHQ~gvPFT60obEBitwH~JcKMahhJDGb0p23Z8B81QajXijypDpVfMDfFbMiqQGctXhnBidNKWe7HQEcJGZer-SCu3m8CiftcZ0t0g5Y67B2AtqLhza5xfepq6FQ-Tl6fpgS-UDcGUAqMpLUYfrBFB11oM09TRVsfNp~NIAvMdrvXiX8dTDUHJM4FRdpV2OsJiyDdgf5-W6s6ssxohviT5MdijUSYQw5sWj8~9Xkv~~aa11Dvty0E7K9IhXAeJlwBe3VuDizsAJGnFIU7PwZXV7-9-28Zgldarg1P-rh2QDvCqF1vjdyZe99BBcTiCqvE3zx2N-9eT~FeOURrgE~2rFBKVBZfuAASSORqiXIeAANKklCW2pGQrM3DkX8ybi93Weg~eBjwGQugO1FRZ-ISq7npRWruMiC7f~fWLqkmRUy9HahPNp8E41s9AAAA squid.i2p=8fiWZbRjOEzrj5n4jSqjN9UN54wTrsgEjqn7GRUQpLx1svf8lwckXPV5buP2VEYGo~83ftkIcDKyMLXkxSr8jqbb4yAEgPe2~w7OT~8LNvmVPz0xZhIO6fiw0WU4xD9x5PG1spYjWPnLFv7pynEvBpWFXaUlCacjWL2KkfViiGPXvveQqQIZs7VkxVD2HK-oT4yIjdqHpc7Y8nEV9xwds3-LX6to5p70jFe~kZJA2fjWHsSCm92TtPvoR3aTlL1VS3JUKpcH6BL5irsh-SKODEtDRCErPQI~j2SHzhcD6dMUsI7bm3AxivjFpSQHqyXLmLVdxECYsMET~nIHmuv8NYTHQQ0jM0XTQzwnQwEHjHRBd1~spR9uS2~LSnX4Pw~X1WTknJpPK1f4Cu1O44X4RYcLRCsxpEzytUBXA4BQizrbYgOJVGQa9-PNGxJeZsnNUZ3PxUi23Oh-c4jUaB0ZjyKTWJSpzj1GI6vc-gW-0ixGJ358TCSbKgqdBv~g~f7yAAAA +fillament.i2p=Pa50z7pU~ni5nWwUdaDZ5CJxG0fYjoarm9wlxnkgX~wHMX9RPgQAXz~r0Rr1Nadt2OA~dr9RMHswrMok0hutK3JZuFD707D7FjmWW2w979Ee9I3zxKyx9W5A2eE49PPT131NLa3uINXLXOYVA5frfDOmM75Dmvm533r8e2kloemJyj22HpvRiSXiQYgqYJGDMH3Hlnwk884eRkQu7P8DJL~hcuKpyY0FzLZtfxTNsdSavGjl7rKPMzJeP02-9TS5TkdHokZrstVM5Cn9ay1c8DQrMds7SPXJy13Ut34QRjb65JxRV0mrnY3teXewW6QFvFMXJCsf5C3i46t-9Fufy5D1H8cSd2Tx~Xl71MC5-1AJCcIS01Od23E9tFY3dU7IOSRhKC~FiAslyk3x-BnBSpKxbgl1w~LArBm5plNiCiUemJU88xYdn1UyukLer~yNrEHAWspckCRkXFwmUtPkaGNTvfwBSYns-skNHSd7MAUUoS-ewStBdmtnDgRkwSG9AAAA +eco.i2p=KhRG6BGxVPh-BUDDfIgy0570cppTdighytcaGVR0HzQo46tgRMBp9Shlpax5FQX4nLHn6qHQbdFFpFbAwe7CiDhURCVF9-CxYmPurGadxlMPHMjz9O3jHX0CQiv2iULsk4XPrYXF3PqBc4t1J6vVyBVO7uTUhDi0gF6sN1Ro-1GLcWcsoR8Kx-hb~Z4WqGD0QAROOBPHnSRSb236qVBkhFvSkfigfBq3jFgEsttadYJA9ZLSUj1XrFFRBjz~xkRra8kJQSZl5dbfg-eZMlL49h61U6Uta5n1~tL6sarmnl9CaTl2Qo27SKB1OmMLeZEteA5G0-~LiOjN0nxaKpwrCjKIOyvwbQy2QqE-GEb9m8SN8nC2bwYK9fH15pTMHY8GvPYGcUukbF6RhefwzkEJLZ~PaAECrZYuLsn9KE5C35uRnlWJiuJlJ25hG7da5tFMyDB95efzq5IMxPeI0pMigRfuVfRSaGDpNos6JxjfEIX8umk3jIJUPhz1d8gP4QgrAAAA +aum.i2p=09hSo56PTtkFLUEt1iUTO7zYTnO-B~ogsIsyyPWif6q1Iz4wz4JoBflAWZtedPmwGmH0nly4HYUS0gAADoUmBUnXwemmO6dxT-hPQkfEW-A7b3uEvYQWIN~kyFyg0Pa~FN6TaD9kGFttBN-GE4wxiHhXmWdzWNDVb0q5PVGnxMm6Jleik8xkd2Lgeexze8rIv8LCocAWx074USVkbCVQwoFi2P7EnjLq8odSz1cJAntbuCFeUZcjbslE3qmlcTFMCNCZXWKVzn7d5m4oszCQ83NidgekwxJ-S~iS6mBwIS0XKI--4iXiKXzzCFf0KtYfEWpvKCuqNJOcU8vQWAA2-i7~K28aLPzccDQn7acXWLKRTXF3tf0i6e-lSx-X6WTSWK-fuNitjAtKu~jqO10d~bCk7y~UPL-XwdH1XSTbk-Phhk7UoBTDiHY6zQHdD~iAzXER~9JXsJ4UoIrGFVabg7frzSt82CN7Ek5Li4AMz5gg3wq9H9HUa7xM5QfGIJpXAAAA +mp3.aum.i2p=vBOu1caCAajaL5WRMuwk4LwfXrlcn0WzA6iHUKV5ULhaBpJb9pR3SZpnQms2Ot2c5Fvu5I6Rp7WF6QRcyasAhUmC915ap~2~VG8KCDP0z3Quh1-eqGcmzErsIfXdh09j3CWuxN~fH84hd~KswqGudFkWtFTM9RcuQUGSC1efG7uF03uaDI-DKu7eb4VUV-hmpXb3Lqntgo5qSMBMmjyUND-f6RBoXnqM005mUZJpMoYfsBhnUEq37GG8u6P9T94nlMmtz9R3gNURpBJJKPlnEqCBN4mlE5rwspQ0ovxAlogVMhSCpQ4jr6cyWIbNx-nMzKGDj~hMQ0ndbVnWw3EDC3KsPnRnDv0yVz8Fc1YpoPwerHej7VnTupDKxc4T-j8XNA1dN8SfPmaKYEPfavlmy7HFAGcsbmeRZOq-PVvlDdrKNflug8Ysodd5XkDbh7y2k1pRDjwNBQ1EgDVAtL05-i9jerqekHkbFKJ6DlT76f06vj1R2v9qlQzAYjpcKbI3AAAA +ogg.aum.i2p=wR2ETKWn-mxsTurWwmSujvjpOiIjLg5TsldFUa4YFTgiRZdFIB-bXuK59shfnchlEgAZR0IR3~hH-O8bZ~j6wVBdZWq7bGTmyTxQ3MeYPdqK7wH7Jp147YUabFlqJkyI~DluwBDylJrIUyc2qw~ogJ67x-KyzIF7JLnoCC4E-T8Z0vmTAFWSa3XC-ncghrdZQCqEXaCMlG9PN~a7dcDq~qdWoNoyFcgLd0IQfE8JuJ1wSvmWUNEd9vkB2Zuu3EoSoDv4C53Fc0YhVACNug~VEEL-ZBGcCBcpVNud8dOMq-CbavkD5yKqHlvq~uzRi6BY5ajHI77uepJygkHcsm-8T0PXWXdc5ib4TtUI03tPkTar4Y2iVocY~oLk2jh7pQKZNioHJT4StWv9Pj8EWaVX4-emQB5kZOBwZItjo~EAGEoBT14NSM7CmKClgc6sg7fpvWF~-cNHkZsurBndni~~FKmUeWoO0FRQRF9Ao~C1DOt2V9oBbEW1~n6anjL5V~IyAAAA +fcp.entropy.i2p=jy0D13oJVmxSa1MltstV3FOfA5e2WAEEZJiYOJIZSUOcNnAkaR3ghE-AX4vuqyQPyOEUydpauD6cS8vfx4iZkb2U3ddlLcOU3YrFKdLrySpGtD~V126VO-9nOJFwDQOOaKAsiVGRKtMPLC64GkpU6TWSIhiVYWb7WmeAHXLLlR71DtgamAxEIlP3VhytxlS3vuvAoEH9ItsBwkv4N~7jec60WMQINl~c7uDDsuzKFY8wQlkHnLFQJCQ0VExfNYqK9nZ3x8TXNPmNKTMMQ1CUCowgwR783U7UAYqsxNrpkuWvTleadn7QcR9i2v4~L9zOeHd4nHBy8PAjO29g6nf6DIsYhg4c2HYnPYzktQ1NIElytmW83BhbXJXLgNBs1eI9gDaQmOiXi74FMgfg63IcXCYWeqCdwEzSouSphaXEHDcZZVTx7DE9R-1Bi4Dt~KvPOFsAoOqsjHCpHq1gS0u5HiL0hkSm1I1EMk4JBY0j4rM1nAt7e0ix~WiOz5jXlTVSAAAA +http.entropy.i2p=ON2Ud-B0-pJKbTR0Obpjp9wEG8grUpu55gEn5Mz3-dkVkPhHvHK6iLasr~P~Rf4kPPZvn-eK7z6rAVfsAytAJ9pcTH3lXERTjkd9FzVJJ0twbZSQ~XzX5d-24IPIMf00KegjnDkRJ82cRMKa-u4H-ayei~Y7xsSx64zC1eHv6qFxavtql3zRrS~du41~EHtpjqOtOo9Ea3lfFjhm2jUIJpYyVHqve3WbTfMBlguVALwGZIfenph7oQ1Hx~OnEtaviWuOEpupjm11LS9xqCNsccaEpJGvGt6ijxd9hrEuQZ5Ja~C0fNxf3xNtgRaUhakA8Xoo~jz8rCkV2vYQo58kj0E5xYrUQczomj8y-eDBZyq29BP8pfe2G1u3hpHA1z470LUeMPk8qVx8Cx8ZKmSK9XCvOl7WCnFS2~UUfzxbxSxPn9LfzxDZp05AVi9t~hJg-zkrL2n1wfEnScuUFapxarwK90rlAxNSnau-K61WfcXqyVMwDxl3leJOeVdHqhpbAAAA +www.mail.i2p=Y~V8YK2M-my6-Gw0lkrkJouxeqPuB03idp-4uT9pkIXCA5nki9m4YFfPObSPv0E7c2shBxwlUo-6beaRQ-7tCawJssDRc0C0PhRj12QUYYdtZP7JS8SQXy68gZIylY-wfyEXleIC4mYY5mSthhdUUfyo1lqzrdHc1NpjPBxRJcyMBFBGUeM7Of9E9M518jXpVl0bAmxSnr5dy7sgKAVNufzfqIBfEHnmL2ZYH78FoGnPybsV0F9~154emkmt89ZUbx0BuYvH3kT1zin8pSxKw1NqxvqYt7p8CElq1--U38rO9U5Y~kLB9f6F3RYJdkl28ANkvdgJUgqiHLVI5oPWATrJLAOokyGKhK4Xl4Bjp4SCuemxHwTOGyd-4Kl8cO41u3w1LksndX9stkV6U1X0gL9BeSIoa1997IgMLVbUiDMyCz7-cA0y2tc0EdQdlpc2y77nTdo7z23dMSJzWDXsrfmLhX7M24D70htLLc1dpwZ1BUEvM1uPqGfsBSrHdl-sAAAA +smtp.mail.i2p=gfSAYcvEsuU3oNGqeMpq1wZqH-whE1i~YCXEDwYzp8LrmukWsndvPER1~gF5QFrIp2RMXiietF3zEPtAJgevSG4ULxRU0s9MSAMXXlCACVhlf0m493J6kIYnkypOPC-Z8sulyF2kXM8BURLfSH57SS2uxLbx0hkc8j0kR9iys3ksxm5dgW-Rs7clAvLmmfASJkXZiU6DRhWbW84GbpAi9McE3ORhNLrWV5t1W4DXqzT0tzF2W0i8BEGns8XdOBQei9RAewzo5NRGPBmUl6ZKjEJ-2UtX189HPs7FcLknfsRxXhRcPQ1RombPezYCgcNhOgWY9owHq64mwGaDCnnpDSM01sdAuMlFfs1JJMoYNrILckjiHUNzV2XS8A0PIWdO4W0cT1EUs-V2a7Ocvg397HpR6Z4k-7fOrjs9yvpFsCPIEKYUD0mjr44N5pJIc61GGuNE~2ihQZGA3ju0OnUKTRZel3nK0rxl-qfFXsBsEB5vt-MTvKS73ZJdxUKWWzbWAAAA +pop.mail.i2p=aG6owmzirq7QZKYovpSVa4-WBLfI1uJ38cNmb6kkSkcS8A~JdoLWPj6eXieN8r3m7YJLxxyZhf3urmK9qJbiIPBp53M8bOSSkldpFz0NkQPWUWmYXiOrEsOBlugbJ8nNelDcOebqoieKBOTaF-WPJQil5C6RYdUy~PL50O6Qp-Hog1868zP26leYBBFiyzzWI3bOpOsgV~4bNXnqKQZeHXz1Ua2DkV-vDBpeamPzvNWQI6cNodf04PBKXK~TlduLZDK7v1yTt2LhPSBM5nE7ZRtS8KQIdh4o-nyYRmHjA~OQ70gowGpmsRqXHQxOpPro~C7w3gSe2N0zhqSHKstwoFJD-NmsBQ5opyOiKccATpWEQdAwmoICD6rw7TGG4XYXCtyTD2xxLffER0SEsJ-BJesKKhdm-qyEMAOQq00jatoEs9jBYoujFLFQMUgaDejRJdEHWkiGT~x4auosHGYavmrcm-0mdX0CWYgfjwVb~PORhBqHJ1G5IgRPjoZLuxiJAAAA +nm.i2p=UhUVbM972VwQgqS28SkGPtghCb~IpdpeMW7O9E7I3HtlB2I3XGbMeUAoya5RHsoG3TYxf~P6lA5IM5Z~mDZlcbZ~AG7255FE6Z3Jl9kfMArneou6AYaCfNBqNTRS-P7yX9s0Kss-vM63yBulxhS7CqmBKTZsXR27PNjJS0PMYsWBzciuy8UqUkE0YEhSLWSUYAXLP9FKs065CNjxsLumkkoF~MNUaBNEmbCrjpv5Ih9vrwz4XAJSE~S61qSMj6O-nvEPDVhTJJ5ymeoZnYMpIRt4r7FkCTH8vYSkXZkhqXUkLC11WPC33lw2wzh-irmIb5GQeab9o0-DuNQcvUnbK13Jxkq5XiilfK6kgKiPcEniqxMb-4paZAl8dj9Zp01LvhfjlS0c459Jv-gr7ZkjkX7hhTaEVvqwPyFgoVKnxQCitoZrK98WRKJwu7EQb-Kin2vzUsYfpGGI0aT68~gdr23oom2FsoZ~owVuur1h0bJr9mnCaMf6jaioQE7wezxgAAAA +mp3.tc.i2p=LRCWTiJouI6ohqtaVBNHWZe2ymjhtyj3z9KdeI2G2D9l0cYFsG0CRUVT5VPYOg~WykALRVBiL-2U24fbiQ28hhPdQgBMBDl9aQiZJM2hv~di0uVOdARhRSgCDgRQAWioAfpXeg6pyklzXU3TNLY4c2CRLe~9Y7wuLbK3lONsAApcxxKeHLrfGNkZZwJTKd7PcG78KAHU07E-TVNf4tQrOh8tSrHaMB1r7cQQv1Jl8mNUZWz4fGeNYEZ1wr04w74Em2~Z5K0VZ4mH5DhFGXc9ALYzZf6uOVzZKiUC0eOcdfGNdVUbIog2CYJGH69TgAX69d5vF~kEzvHSzX7RxUTt0y25Rlbi6rHSDF36xOfBrOUVnSPn5X~TdKLygz~zusYpRdGZwlsyOTKVTzJHKlU6Vp5Dofijj1bUwXDQ59XCpFUqEDA17nETiOO0H5jfieYBPS6Ke2cFTAhutSvaw~albCd1eV7RPqeGdw-vFfKoucDIEVUT40B5qalyFRKIxx3lAAAA mesh.firerabbit.i2p=BLt3pciNQcVIU-IGnTfSsrputh~b6drZpc1vH8qeA745XoE~nMCGtw8S7HGYX4uEbwk876nQRPV4qRwGtWWkWBs8BX9qX8NABoXFk4G-6NifB4TxEizC~ZAnXZ2uFs~nrqodhyCR8bBHJL0tzBYK3E36zc~SA-DKqQ9XSHWp7ScW7Z3cdQnYKma~x4u5eZmcS23uie3OIfOCk6pJOabtaE-YWRa1eUizhucI-ysm789GumjTo858vHR1mTQfrsPTqNri3yz0aIe~w06ifciq~UlNjVfx89lLEso1vmg8rfTQ-hwxS-qz-u3K5x5vtqdGp673vCvmEnQpU6GEycmkqoLCho9pNQzGbka-OVHg8fZqlFMeBfj2iLz~zlv170jvX6HTlMCNfBYnFqavs2RQJj7--dJ0g7JHReGMKL~TciQjxljrV5AoN-0afRzTZqtDg13PL4tltJm5U1~f-GcxlsjKLZAlv26LlZXsvTDU5plldsernv3fDcBev9UaKYCwAAAA chess.fillament.i2p=8xvXLwcBYu2MxqMAoG6DIahvkNAwBQs43kdZTg9SlouzC3JSQ25RHvspbrxWoGIVAlB~XCxVmBB6EolYEHoSZv2YCziOj4UVxEbVP7nHkh32-7Uc5T7xlcjk8-rsmZzdgz9NhxKVn2Uhp3xtcdVAiyG4mpXisvK-7NgHY-mXPNvj6goaH58roeDUZ5otJN7nCFmcRAUpaUk-nlQs8YfgSCCEsWKOWhsVnAaHwtqtDlpdTo1YKooACMRSa-DcV5W75Il80JEWBD79qpSAeONGAOMMPT~VEMwNNg001VG-UZbvyspZdxHaETw2yd7N9l3-mwI-sYcveTTnNXLWO8IjdgDLdUIP5qrIW6WS9XZIHRKqT2kpwEw7xsEgCA~qSNiZWeai8n6Zs0rLmdyeZeafVEEW9vr6CKcLZ5W7i1SMDqCKnzSbZdd2Hvpb9aWFf5foEjLt33n8w2KSaCUe4zmN~xuQMq2yiB-vQ9~5fgPmphlMxo3ca5MTCfbhshw5137YAAAA kaji.i2p=i-nivH~36AgabgzvS06oKHR~MLoy6NA0oSv8JuNJDLZB8FXEDzIiyzWISmw9XJm8I7QqZ1yFH8~xe84STCHHvMMIQQrfBmOUODLWbKZor~~KhiHdNLtfVZC5BpnXkBCJkklj~fMYSpWa0C~pVRrZl75RoGtBjDVP9P8hioTv5B6RC86g2ypBH5r093rY0wnzxSL8-ZuV3F~H48VYbqro8cRlbMmjx2oEsSHkDpQyjCMVkIYKaCijkSArqZTG~zX6op6Ng9CJwdrkjKSsbzjV6MLnE4aNv-jm2WaGGD5pR24h7e3ImDOGAr17tXRtmNX5ZEQ1udQp8qIhd8UMUumrnm962r8KJWK~9WNzcVeqDrIxaaxC7vcQmXxoPeEW2efbH0yKhVZ7OFu~I9cAapSe~aNWp9UK4URSpuJvOedt0axp3ORaaM-a5U7noW3Ao-HB83qfFEPU-6uUu16HNiPqCFMJiA0qODTOwHiyyx4HKQvbhjujh4mmknSbsuapdgR1AAAA From 33f4fac48f0d4afc53307c3c3926c2177810f735 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 14 Mar 2009 21:42:50 +0000 Subject: [PATCH 049/688] summary bar help --- apps/routerconsole/jsp/help.jsp | 116 +++++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) diff --git a/apps/routerconsole/jsp/help.jsp b/apps/routerconsole/jsp/help.jsp index d53f93a88..279df95dd 100644 --- a/apps/routerconsole/jsp/help.jsp +++ b/apps/routerconsole/jsp/help.jsp @@ -12,12 +12,126 @@

Help

-Sorry, there's no help text here yet, so check out the +Sorry, there's not much help text here yet, so also check out the FAQ on www.i2p2.i2p or the Deutsch FAQ. +You may also try the +forum +or IRC.
+

Summary Bar Information

+

General

+
    +
  • Ident: +The first four characters (24 bits) of your 44-character (256-bit) Base64 router hash. +The full hash is shown on your router info page. +Never reveal this to anyone, as your router info contains your IP. +
  • Version: +The version of the I2P software you are running. +
  • Now: +The current time (UTC) and the skew, if any. I2P requires your computer's time be accurate. +If the skew is more than a few seconds, please correct the problem by adjusting +your computer's time. +
  • Reachability: +The router's view of whether it can be contacted by other routers. +Further information is on the configuration page. +
+ +

Peers

+
    +
  • Active: +The first number is the number of peers you've sent or received a message from in the last few minutes. +This may range from 8-10 to several hundred, depending on your total bandwidth, +shared bandwidth, and locally-generated traffic. +The second number is the number of peers seen in the last hour or so. +Do not be concerned if these numbers vary widely. +
  • Fast: +This is the number of peers you use for building client tunnels. It is generally in the +range 8-15. Your fast peers are shown on the profiles page. +
  • High Capacity: +This is the number of peers you use for building some of your exploratory tunnels. It is generally in the +range 8-25. The fast peers are included in the high capacity tier. +Your high capacity peers are shown on the profiles page. +
  • Well Integrated: +This is the number of peers you use for network database inquiries. +These are usually the "floodfill" peers. +Your well integrated peers are shown on the bottom of the profiles page. +
  • Known: +This is the total number of routers you know about. +They are listed on the network database page. +This may range from under 100 to 1000 or more. +This number is not the total size of the network; +it may vary widely depending on your total bandwidth, +shared bandwidth, and locally-generated traffic. +I2P does not require a router to know every other router. +
+ +

Bandwidth in/out

+Should be self-explanatory. All values are in bytes per second, not bits per second. +Change your bandwidth limits on the configuration page. + +

Local destinations

+The local applications connecting through your router. +These may be clients started through I2PTunnel +or external programs connecting through SAM, BOB, or directly to I2CP. + +

Tunnels in/out

+The actual tunnels are shown on the the tunnels page. +
    +
  • Exploratory: +Tunnels built by your router and used for communication with the floodfill peers, +building new tunnels, and testing existing tunnels. +
  • Client: +Tunnels built by your router for each client's use. +
  • Participating: +Tunnels built by other routers through your router. +This may vary widely depending on network demand, your +shared bandwidth, and amount of locally-generated traffic. +The recommended method for limiting participating tunnels is +to change your share percentage on the configuration page. +You may also limit the total number by setting router.maxParticipatingTunnels=nnn on +the advanced configuration page. +
+ +

Congestion

+Some basic indications of router overload. +
  • Job lag: +How long jobs are waiting before execution. The job queue is listed on the jobs page. +Unfortunately, there are several other job queues in the router that may be congested, +and their status is not available in the router console. +The job lag should generally be zero. +If it is consistently higher than 500ms, your computer is very slow, or the +router has serious problems. +
  • Message delay: +How long an outbound message waits in the queue. +This should generally be a few hundred milliseconds or less. +If it is consistently higher than 1000ms, your computer is very slow, +or you should adjust your bandwidth limits, or your (bittorrent?) clients +may be sending too much data and should have their transmit bandwidth limit reduced. +
  • Tunnel lag: +This is the round trip time for a tunnel test, which sends a single message +out a client tunnel and in an exploratory tunnel, or vice versa. +It should usually be less than 5 seconds. +If it is consistently higher than that, your computer is very slow, +or you should adjust your bandwidth limits, or there are network problems. +
  • Handle backlog: +This is the number of pending requests from other routers to build a +participating tunnel through your router. +It should usually be close to zero. +If it is consistently high, your computer is too slow, +and you should reduce your share bandwidth limits. +
  • Accepting/Rejecting: +Your routers' status on accepting or rejecting +requests from other routers to build a +participating tunnel through your router. +Your router may accept all requests, accept or reject a percentage of requests, +or reject all requests for a number of reasons, to control +the bandwidth and CPU demands and maintain capacity for +local clients. + +

    Legal stuff

    The I2P router (router.jar) and SDK (i2p.jar) are almost entirely public domain, with a few notable exceptions:
      From 91b3889cbcba549891faeba9cad4bc90250175fe Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 15 Mar 2009 00:16:38 +0000 Subject: [PATCH 050/688] catch a rare AIOOB --- router/java/src/net/i2p/router/tunnel/FragmentHandler.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/router/java/src/net/i2p/router/tunnel/FragmentHandler.java b/router/java/src/net/i2p/router/tunnel/FragmentHandler.java index 5a97956b9..dbe256ebd 100644 --- a/router/java/src/net/i2p/router/tunnel/FragmentHandler.java +++ b/router/java/src/net/i2p/router/tunnel/FragmentHandler.java @@ -74,6 +74,12 @@ public class FragmentHandler { int padding = 0; while (preprocessed[offset] != (byte)0x00) { offset++; // skip the padding + // AIOOBE http://forum.i2p/viewtopic.php?t=3187 + if (offset >= TrivialPreprocessor.PREPROCESSED_SIZE) { + _cache.release(new ByteArray(preprocessed)); + _context.statManager().addRateData("tunnel.corruptMessage", 1, 1); + return; + } padding++; } offset++; // skip the final 0x00, terminating the padding From d0a969ca33d78c1eb2e0794ab5c7e4351c7bb10b Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 16 Mar 2009 19:31:29 +0000 Subject: [PATCH 051/688] fix NPE on delayed open http://forum.i2p/viewtopic.php?t=3189 --- .../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java index b42376dbf..8e5ef9f4d 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java @@ -185,7 +185,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable /** * create the default options (using the default timeout, etc) - * + * unused? */ protected I2PSocketOptions getDefaultOptions() { Properties defaultOpts = getTunnel().getClientOptions(); @@ -210,6 +210,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable defaultOpts.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, ""+DEFAULT_READ_TIMEOUT); if (!defaultOpts.contains("i2p.streaming.inactivityTimeout")) defaultOpts.setProperty("i2p.streaming.inactivityTimeout", ""+DEFAULT_READ_TIMEOUT); + // delayed start + if (sockMgr == null) + sockMgr = getSocketManager(); I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts); if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT)) opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT); From 0da964e47f620311b0721e7976ac69a971100370 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 16 Mar 2009 19:34:59 +0000 Subject: [PATCH 052/688] -9 --- history.txt | 8 ++++++++ router/java/src/net/i2p/router/RouterVersion.java | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/history.txt b/history.txt index 1388353e4..e5bfe6478 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,11 @@ +2009-03-16 zzz + * help.jsp: Add some + * I2PTunnel: Cleanup + * I2PTunnelHTTPClient: Fix NPE on delayed open + * I2PTunnelHTTPServer: Maybe catch an NPE + * SOCKS: Allow .onion addresses for onioncat testing + * Tunnel: Catch a rare AIOOB + 2009-03-09 zzz * Client: - Clean up retry code diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index eeea3faa9..6ade2ee81 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -17,7 +17,7 @@ import net.i2p.CoreVersion; public class RouterVersion { public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 8; + public final static long BUILD = 9; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID); From e5f19c98a8bde3ee448d46f369a35b8bd787f28d Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 24 Mar 2009 15:21:34 +0000 Subject: [PATCH 053/688] change common corrupt errors to warns --- .../net/i2p/router/tunnel/FragmentHandler.java | 4 ++-- .../net/i2p/router/tunnel/FragmentedMessage.java | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/router/java/src/net/i2p/router/tunnel/FragmentHandler.java b/router/java/src/net/i2p/router/tunnel/FragmentHandler.java index dbe256ebd..99b66c0c8 100644 --- a/router/java/src/net/i2p/router/tunnel/FragmentHandler.java +++ b/router/java/src/net/i2p/router/tunnel/FragmentHandler.java @@ -393,8 +393,8 @@ public class FragmentHandler { _log.error("Error receiving fragmented message (corrupt?): " + stringified, ioe); } catch (I2NPMessageException ime) { if (stringified == null) stringified = msg.toString(); - if (_log.shouldLog(Log.ERROR)) - _log.error("Error receiving fragmented message (corrupt?): " + stringified, ime); + if (_log.shouldLog(Log.WARN)) + _log.warn("Error receiving fragmented message (corrupt?): " + stringified, ime); } } diff --git a/router/java/src/net/i2p/router/tunnel/FragmentedMessage.java b/router/java/src/net/i2p/router/tunnel/FragmentedMessage.java index d26c691b7..b0203f540 100644 --- a/router/java/src/net/i2p/router/tunnel/FragmentedMessage.java +++ b/router/java/src/net/i2p/router/tunnel/FragmentedMessage.java @@ -78,13 +78,13 @@ public class FragmentedMessage { return false; } if (length <= 0) { - if (_log.shouldLog(Log.ERROR)) - _log.error("Length is impossible (" + length + ") for messageId " + messageId); + if (_log.shouldLog(Log.WARN)) + _log.warn("Length is impossible (" + length + ") for messageId " + messageId); return false; } if (offset + length > payload.length) { - if (_log.shouldLog(Log.ERROR)) - _log.error("Length is impossible (" + length + "/" + offset + " out of " + payload.length + ") for messageId " + messageId); + if (_log.shouldLog(Log.WARN)) + _log.warn("Length is impossible (" + length + "/" + offset + " out of " + payload.length + ") for messageId " + messageId); return false; } if (_log.shouldLog(Log.DEBUG)) @@ -131,13 +131,13 @@ public class FragmentedMessage { return false; } if (length <= 0) { - if (_log.shouldLog(Log.ERROR)) - _log.error("Length is impossible (" + length + ") for messageId " + messageId); + if (_log.shouldLog(Log.WARN)) + _log.warn("Length is impossible (" + length + ") for messageId " + messageId); return false; } if (offset + length > payload.length) { - if (_log.shouldLog(Log.ERROR)) - _log.error("Length is impossible (" + length + "/" + offset + " out of " + payload.length + ") for messageId " + messageId); + if (_log.shouldLog(Log.WARN)) + _log.warn("Length is impossible (" + length + "/" + offset + " out of " + payload.length + ") for messageId " + messageId); return false; } if (_log.shouldLog(Log.DEBUG)) From 09d700e1d6b6777d5fc2509dcc2b521855cdbe9d Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 24 Mar 2009 18:19:47 +0000 Subject: [PATCH 054/688] fix encrypted leasesets --- core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java b/core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java index 7a8bd200e..e662f6572 100644 --- a/core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java +++ b/core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java @@ -79,7 +79,7 @@ class RequestLeaseSetMessageHandler extends HandlerImpl { leaseSet.setEncryptionKey(li.getPublicKey()); leaseSet.setSigningKey(li.getSigningPublicKey()); - boolean encrypt = Boolean.valueOf(session.getOptions().getProperty("i2cp.encryptLeaseset")).booleanValue(); + boolean encrypt = Boolean.valueOf(session.getOptions().getProperty("i2cp.encryptLeaseSet")).booleanValue(); String sk = session.getOptions().getProperty("i2cp.leaseSetKey"); if (encrypt && sk != null) { SessionKey key = new SessionKey(); From 47edc3c8537f079ed49c97051f8f539fd3b31168 Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 24 Mar 2009 18:21:28 +0000 Subject: [PATCH 055/688] add warnings for some new features --- apps/i2ptunnel/jsp/editClient.jsp | 4 ++-- apps/i2ptunnel/jsp/editServer.jsp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/i2ptunnel/jsp/editClient.jsp b/apps/i2ptunnel/jsp/editClient.jsp index 6dd839d97..915da5db9 100644 --- a/apps/i2ptunnel/jsp/editClient.jsp +++ b/apps/i2ptunnel/jsp/editClient.jsp @@ -318,7 +318,7 @@
      @@ -353,7 +353,7 @@
      diff --git a/apps/i2ptunnel/jsp/editServer.jsp b/apps/i2ptunnel/jsp/editServer.jsp index b3917cced..59a7b9cf7 100644 --- a/apps/i2ptunnel/jsp/editServer.jsp +++ b/apps/i2ptunnel/jsp/editServer.jsp @@ -286,7 +286,7 @@
      From e9063a22d565963db8435666cf6ebbaa7396eb9c Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 24 Mar 2009 18:58:08 +0000 Subject: [PATCH 056/688] add anchors --- apps/routerconsole/jsp/configstats.jsp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/routerconsole/jsp/configstats.jsp b/apps/routerconsole/jsp/configstats.jsp index 651636176..c7ec3090c 100644 --- a/apps/routerconsole/jsp/configstats.jsp +++ b/apps/routerconsole/jsp/configstats.jsp @@ -88,6 +88,7 @@ function toggleAll(category)
  • LogGraph
    + checked="true" <% } %>/> From 6c365bef85b63c746cd09149c05a7d0510f8189c Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 24 Mar 2009 19:52:06 +0000 Subject: [PATCH 057/688] add links to enable graphing --- apps/routerconsole/jsp/help.jsp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/apps/routerconsole/jsp/help.jsp b/apps/routerconsole/jsp/help.jsp index 279df95dd..68f1b6245 100644 --- a/apps/routerconsole/jsp/help.jsp +++ b/apps/routerconsole/jsp/help.jsp @@ -22,6 +22,10 @@ or IRC.

    Summary Bar Information

    +Many of the stats on the summary bar may be +configured to be +graphed for further analysis. +

    General

    • Ident: @@ -47,13 +51,16 @@ This may range from 8-10 to several hundred, depending on your total bandwidth, shared bandwidth, and locally-generated traffic. The second number is the number of peers seen in the last hour or so. Do not be concerned if these numbers vary widely. +Enable graphing
    • Fast: This is the number of peers you use for building client tunnels. It is generally in the range 8-15. Your fast peers are shown on the profiles page. +Enable graphing
    • High Capacity: This is the number of peers you use for building some of your exploratory tunnels. It is generally in the range 8-25. The fast peers are included in the high capacity tier. Your high capacity peers are shown on the profiles page. +Enable graphing
    • Well Integrated: This is the number of peers you use for network database inquiries. These are usually the "floodfill" peers. @@ -71,6 +78,7 @@ I2P does not require a router to know every other router.

      Bandwidth in/out

      Should be self-explanatory. All values are in bytes per second, not bits per second. Change your bandwidth limits on the configuration page. +Bandwidth is graphed by default.

      Local destinations

      The local applications connecting through your router. @@ -93,10 +101,12 @@ The recommended method for limiting participating tunnels is to change your share percentage on the configuration page. You may also limit the total number by setting router.maxParticipatingTunnels=nnn on the advanced configuration page. +Enable graphing

    Congestion

    Some basic indications of router overload. +
    • Job lag: How long jobs are waiting before execution. The job queue is listed on the jobs page. Unfortunately, there are several other job queues in the router that may be congested, @@ -104,18 +114,21 @@ and their status is not available in the router console. The job lag should generally be zero. If it is consistently higher than 500ms, your computer is very slow, or the router has serious problems. +Enable graphing
    • Message delay: How long an outbound message waits in the queue. This should generally be a few hundred milliseconds or less. If it is consistently higher than 1000ms, your computer is very slow, or you should adjust your bandwidth limits, or your (bittorrent?) clients may be sending too much data and should have their transmit bandwidth limit reduced. +Enable graphing (transport.sendProcessingTime)
    • Tunnel lag: This is the round trip time for a tunnel test, which sends a single message out a client tunnel and in an exploratory tunnel, or vice versa. It should usually be less than 5 seconds. If it is consistently higher than that, your computer is very slow, or you should adjust your bandwidth limits, or there are network problems. +Enable graphing (tunnel.testSuccessTime)
    • Handle backlog: This is the number of pending requests from other routers to build a participating tunnel through your router. From bb51bf49b03d35836630923ddee0c1e69c1059d4 Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 24 Mar 2009 20:24:20 +0000 Subject: [PATCH 058/688] - Suppress log error on manual stop - Prevent NPE when closing a delayed-open tunnel --- .../i2p/i2ptunnel/I2PTunnelClientBase.java | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java index 94bc959c0..b6eb39224 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java @@ -375,7 +375,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna // This will build a new socket manager and a new dest if the session is closed. sockMgr = getSocketManager(); if (oldSockMgr != sockMgr) { - _log.error("Built a new destination on resume"); + _log.warn("Built a new destination on resume"); } } } // else the old socket manager will reconnect the old session if necessary @@ -431,8 +431,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna _context.statManager().addRateData("i2ptunnel.client.manageTime", total, total); } } catch (IOException ex) { - _log.error("Error listening for connections on " + localPort, ex); - notifyEvent("openBaseClientResult", "error"); + if (open) { + _log.error("Error listening for connections on " + localPort, ex); + notifyEvent("openBaseClientResult", "error"); + } synchronized (sockLock) { mySockets.clear(); } @@ -513,20 +515,23 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna // might risk to create an orphan socket. Would be better // to return with an error in that situation quickly. synchronized (sockLock) { - mySockets.retainAll(sockMgr.listSockets()); - if (!forced && mySockets.size() != 0) { - l.log("There are still active connections!"); - _log.debug("can't close: there are still active connections!"); - for (Iterator it = mySockets.iterator(); it.hasNext();) { - l.log("->" + it.next()); + if (sockMgr != null) { + mySockets.retainAll(sockMgr.listSockets()); + if (!forced && mySockets.size() != 0) { + l.log("There are still active connections!"); + _log.debug("can't close: there are still active connections!"); + for (Iterator it = mySockets.iterator(); it.hasNext();) { + l.log("->" + it.next()); + } + return false; + } + I2PSession session = sockMgr.getSession(); + if (session != null) { + getTunnel().removeSession(session); } - return false; - } - I2PSession session = sockMgr.getSession(); - if (session != null) { - getTunnel().removeSession(session); } l.log("Closing client " + toString()); + open = false; try { if (ss != null) ss.close(); } catch (IOException ex) { @@ -534,7 +539,6 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna return false; } l.log("Client closed."); - open = false; } synchronized (_waitingSockets) { _waitingSockets.notifyAll(); } From 41718b47c16c0d51769a49645e850c75aa252b50 Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 24 Mar 2009 21:29:15 +0000 Subject: [PATCH 059/688] increase default bw to 64/32 --- .../i2p/router/transport/FIFOBandwidthRefiller.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/router/java/src/net/i2p/router/transport/FIFOBandwidthRefiller.java b/router/java/src/net/i2p/router/transport/FIFOBandwidthRefiller.java index d4bdfea85..693df45b1 100644 --- a/router/java/src/net/i2p/router/transport/FIFOBandwidthRefiller.java +++ b/router/java/src/net/i2p/router/transport/FIFOBandwidthRefiller.java @@ -33,11 +33,11 @@ public class FIFOBandwidthRefiller implements Runnable { public static final String PROP_OUTBOUND_BANDWIDTH_PEAK = "i2np.bandwidth.outboundBurstKBytes"; //public static final String PROP_REPLENISH_FREQUENCY = "i2np.bandwidth.replenishFrequencyMs"; - // no longer allow unlimited bandwidth - the user must specify a value, and if they do not, it is 32/16KBps - public static final int DEFAULT_INBOUND_BANDWIDTH = 48; - public static final int DEFAULT_OUTBOUND_BANDWIDTH = 24; - public static final int DEFAULT_INBOUND_BURST_BANDWIDTH = 64; - public static final int DEFAULT_OUTBOUND_BURST_BANDWIDTH = 32; + // no longer allow unlimited bandwidth - the user must specify a value, else use defaults below (KBps) + public static final int DEFAULT_INBOUND_BANDWIDTH = 64; + public static final int DEFAULT_OUTBOUND_BANDWIDTH = 32; + public static final int DEFAULT_INBOUND_BURST_BANDWIDTH = 80; + public static final int DEFAULT_OUTBOUND_BURST_BANDWIDTH = 40; public static final int DEFAULT_BURST_SECONDS = 60; From 2695461bd48a0da1ca056e9eda4dc803201cb0b1 Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 24 Mar 2009 21:31:55 +0000 Subject: [PATCH 060/688] -10 --- history.txt | 10 ++++++++++ router/java/src/net/i2p/router/RouterVersion.java | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/history.txt b/history.txt index e5bfe6478..d3fcf2f4c 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,13 @@ +2009-03-24 zzz + * I2PTunnel: + - Add some warnings about new features + - Fix encrypted leasesets broken in about -4 + - Suppress log error on manual stop + - Fix NPE on close of a tunnel not open yet + * Transport: + - Increase default bw to 64/32, burst 80/40 + * Tunnels: Change some fragmentation errors to warns + 2009-03-16 zzz * help.jsp: Add some * I2PTunnel: Cleanup diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 6ade2ee81..c1d6bdf7c 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -17,7 +17,7 @@ import net.i2p.CoreVersion; public class RouterVersion { public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 9; + public final static long BUILD = 10; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID); From 5414d41de4d199e44da68a3b5b4376d792fc47ff Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 25 Mar 2009 23:19:37 +0000 Subject: [PATCH 061/688] I2PSnark: Use new BW Limits message, remove router.jar dependencies --- apps/i2psnark/java/build.xml | 3 +- .../java/src/org/klomp/snark/BWLimits.java | 44 +++++++++++++++++++ .../java/src/org/klomp/snark/Snark.java | 5 +-- .../src/org/klomp/snark/SnarkManager.java | 23 +++++----- core/java/src/net/i2p/client/I2PSession.java | 5 +++ .../src/net/i2p/client/I2PSessionImpl.java | 4 ++ .../src/net/i2p/client/I2PSimpleSession.java | 2 +- .../client/ClientMessageEventListener.java | 6 +-- 8 files changed, 72 insertions(+), 20 deletions(-) create mode 100644 apps/i2psnark/java/src/org/klomp/snark/BWLimits.java diff --git a/apps/i2psnark/java/build.xml b/apps/i2psnark/java/build.xml index 02eee0aab..9689fda06 100644 --- a/apps/i2psnark/java/build.xml +++ b/apps/i2psnark/java/build.xml @@ -18,7 +18,6 @@ - @@ -32,7 +31,7 @@ srcdir="./src" debug="true" deprecation="on" source="1.5" target="1.5" destdir="./build/obj" - classpath="../../../core/java/build/i2p.jar:../../../router/java/build/router.jar:../../jetty/jettylib/org.mortbay.jetty.jar:../../jetty/jettylib/javax.servlet.jar:../../ministreaming/java/build/mstreaming.jar" /> + classpath="../../../core/java/build/i2p.jar:../../jetty/jettylib/org.mortbay.jetty.jar:../../jetty/jettylib/javax.servlet.jar:../../ministreaming/java/build/mstreaming.jar" /> diff --git a/apps/i2psnark/java/src/org/klomp/snark/BWLimits.java b/apps/i2psnark/java/src/org/klomp/snark/BWLimits.java new file mode 100644 index 000000000..eb157cb5e --- /dev/null +++ b/apps/i2psnark/java/src/org/klomp/snark/BWLimits.java @@ -0,0 +1,44 @@ +/* + * Released into the public domain + * with no warranty of any kind, either expressed or implied. + */ +package org.klomp.snark; + +import java.util.Arrays; +import java.util.Properties; + +import net.i2p.I2PAppContext; +import net.i2p.client.I2PSessionException; +import net.i2p.client.I2PClient; +import net.i2p.client.I2PSession; +import net.i2p.client.I2PSimpleClient; + +/** + * Connect via I2CP and ask the router the bandwidth limits. + * + * The call is blocking and returns null on failure. + * Timeout is set to 5 seconds in I2PSimpleSession but it should be much faster. + * + * @author zzz + */ +class BWLimits { + + public static int[] getBWLimits(String host, int port) { + int[] rv = null; + try { + I2PClient client = new I2PSimpleClient(); + Properties opts = new Properties(); + opts.put(I2PClient.PROP_TCP_HOST, host); + opts.put(I2PClient.PROP_TCP_PORT, "" + port); + I2PSession session = client.createSession(null, opts); + session.connect(); + rv = session.bandwidthLimits(); + session.destroySession(); + } catch (I2PSessionException ise) {} + return rv; + } + + public static void main(String args[]) { + System.out.println(Arrays.toString(getBWLimits("127.0.0.1", 7654))); + } +} diff --git a/apps/i2psnark/java/src/org/klomp/snark/Snark.java b/apps/i2psnark/java/src/org/klomp/snark/Snark.java index e124955cf..53c42dfad 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Snark.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Snark.java @@ -36,7 +36,6 @@ import java.util.Timer; import java.util.TimerTask; import net.i2p.I2PAppContext; -import net.i2p.router.client.ClientManagerFacadeImpl; import net.i2p.client.streaming.I2PServerSocket; import net.i2p.data.Destination; import net.i2p.util.I2PThread; @@ -261,9 +260,9 @@ public class Snark public Snark(I2PAppContext ctx, Properties opts, String torrent, StorageListener slistener, boolean start, String rootDir) { this(new I2PSnarkUtil(ctx), torrent, null, -1, slistener, null, null, null, null, false, rootDir); - String host = opts.getProperty(ClientManagerFacadeImpl.PROP_CLIENT_HOST); + String host = opts.getProperty("i2cp.hostname"); int port = 0; - String s = opts.getProperty(ClientManagerFacadeImpl.PROP_CLIENT_PORT); + String s = opts.getProperty("i2cp.port"); if (s != null) { try { port = Integer.parseInt(s); diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index 54367af1a..01a93a58c 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -18,7 +18,6 @@ import java.util.TreeMap; import net.i2p.I2PAppContext; import net.i2p.data.Base64; import net.i2p.data.DataHelper; -import net.i2p.router.RouterContext; import net.i2p.util.I2PAppThread; import net.i2p.util.Log; @@ -147,22 +146,21 @@ public class SnarkManager implements Snark.CompleteListener { _config.setProperty(PROP_EEP_PORT, "4444"); if (!_config.containsKey(PROP_UPLOADERS_TOTAL)) _config.setProperty(PROP_UPLOADERS_TOTAL, "" + Snark.MAX_TOTAL_UPLOADERS); - if (!_config.containsKey(PROP_UPBW_MAX)) { - try { - if (_context instanceof RouterContext) - _config.setProperty(PROP_UPBW_MAX, "" + (((RouterContext)_context).bandwidthLimiter().getOutboundKBytesPerSecond() / 2)); - else - _config.setProperty(PROP_UPBW_MAX, "" + DEFAULT_MAX_UP_BW); - } catch (NoClassDefFoundError ncdfe) { - _config.setProperty(PROP_UPBW_MAX, "" + DEFAULT_MAX_UP_BW); - } - } if (!_config.containsKey(PROP_DIR)) _config.setProperty(PROP_DIR, "i2psnark"); if (!_config.containsKey(PROP_AUTO_START)) _config.setProperty(PROP_AUTO_START, DEFAULT_AUTO_START); updateConfig(); } + + /** call from DirMonitor since loadConfig() is called before router I2CP is up */ + private void getBWLimit() { + if (!_config.containsKey(PROP_UPBW_MAX)) { + int[] limits = BWLimits.getBWLimits(_util.getI2CPHost(), _util.getI2CPPort()); + if (limits != null && limits[1] > 0) + _util.setMaxUpBW(limits[1]); + } + } private void updateConfig() { String i2cpHost = _config.getProperty(PROP_I2CP_HOST); @@ -619,6 +617,9 @@ public class SnarkManager implements Snark.CompleteListener { _messages.remove(0); } + // here because we need to delay until I2CP is up + // although the user will see the default until then + getBWLimit(); while (true) { File dir = getDataDir(); _log.debug("Directory Monitor loop over " + dir.getAbsolutePath()); diff --git a/core/java/src/net/i2p/client/I2PSession.java b/core/java/src/net/i2p/client/I2PSession.java index 1776af5c0..1998dad55 100644 --- a/core/java/src/net/i2p/client/I2PSession.java +++ b/core/java/src/net/i2p/client/I2PSession.java @@ -143,6 +143,11 @@ public interface I2PSession { */ public Destination lookupDest(Hash h) throws I2PSessionException; + /** + * Get the current bandwidth limits + */ + public int[] bandwidthLimits() throws I2PSessionException; + /** See I2PSessionMuxedImpl for details */ public void addSessionListener(I2PSessionListener lsnr, int proto, int port); /** See I2PSessionMuxedImpl for details */ diff --git a/core/java/src/net/i2p/client/I2PSessionImpl.java b/core/java/src/net/i2p/client/I2PSessionImpl.java index 0e13f2c56..5b7603fdd 100644 --- a/core/java/src/net/i2p/client/I2PSessionImpl.java +++ b/core/java/src/net/i2p/client/I2PSessionImpl.java @@ -656,6 +656,10 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa return null; } + public int[] bandwidthLimits() throws I2PSessionException { + return null; + } + protected void updateActivity() { _lastActivity = _context.clock().now(); if (_isReduced) { diff --git a/core/java/src/net/i2p/client/I2PSimpleSession.java b/core/java/src/net/i2p/client/I2PSimpleSession.java index ae588e05f..b417bd7f7 100644 --- a/core/java/src/net/i2p/client/I2PSimpleSession.java +++ b/core/java/src/net/i2p/client/I2PSimpleSession.java @@ -130,7 +130,7 @@ class I2PSimpleSession extends I2PSessionImpl2 { return null; _bwReceivedLock = new Object(); sendMessage(new GetBandwidthLimitsMessage()); - for (int i = 0; i < 5 && !_destReceived; i++) { + for (int i = 0; i < 5 && !_bwReceived; i++) { try { synchronized (_bwReceivedLock) { _bwReceivedLock.wait(1000); diff --git a/router/java/src/net/i2p/router/client/ClientMessageEventListener.java b/router/java/src/net/i2p/router/client/ClientMessageEventListener.java index cf49ebcd4..3b4b1a6be 100644 --- a/router/java/src/net/i2p/router/client/ClientMessageEventListener.java +++ b/router/java/src/net/i2p/router/client/ClientMessageEventListener.java @@ -280,15 +280,15 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi } /** - * Divide router limit by 2 for overhead. + * Divide router limit by 1.75 for overhead. * This could someday give a different answer to each client. * But it's not enforced anywhere. */ private void handleGetBWLimits(I2CPMessageReader reader, GetBandwidthLimitsMessage message) { if (_log.shouldLog(Log.INFO)) _log.info("Got BW Limits request"); - int in = _context.bandwidthLimiter().getInboundKBytesPerSecond() / 2; - int out = _context.bandwidthLimiter().getOutboundKBytesPerSecond() / 2; + int in = _context.bandwidthLimiter().getInboundKBytesPerSecond() * 4 / 7; + int out = _context.bandwidthLimiter().getOutboundKBytesPerSecond() * 4 / 7; BandwidthLimitsMessage msg = new BandwidthLimitsMessage(in, out); try { _runner.doSend(msg); From 29df534161b1f914e9abcf7f6012232282314835 Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 26 Mar 2009 00:02:29 +0000 Subject: [PATCH 062/688] update license splash text --- installer/resources/readme.license.txt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/installer/resources/readme.license.txt b/installer/resources/readme.license.txt index d707e557c..9e40f595c 100644 --- a/installer/resources/readme.license.txt +++ b/installer/resources/readme.license.txt @@ -16,12 +16,13 @@ following non-public domain code: * Bouncycastle's hash routines (MIT license) * Cryptix's AES routines (Cryptix license) * Adam Buckley's SNTP routines (BSD) +* FSF's PRNG and GMP (LGPL) Also included in this distribution are a bunch of third party client applications, all with their own dependencies. Please see our license policy page for details: - http://www.i2p.net/licenses + http://www.i2p2.de/licenses One of the bundled client apps (routerconsole) requires us to say: @@ -29,8 +30,11 @@ requires us to say: the Apache Software Foundation (http://www.apache.org/) -Another (I2PTunnel) is GPL licensed. +I2PTunnel, I2PSnark, SusiDNS, and SusiMail +are GPL licensed. + +For more information see LICENSE.txt +in the install directory. For source, please see: - http://www.i2p.net/download -or http://www.i2p.net/cvs \ No newline at end of file + http://www.i2p2.de/monotone From 6a6cd14398d790d32ad8b56c53f2495c38b298f1 Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 26 Mar 2009 18:28:27 +0000 Subject: [PATCH 063/688] checklist update --- checklist.txt | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/checklist.txt b/checklist.txt index b649bef59..fcbe65028 100644 --- a/checklist.txt +++ b/checklist.txt @@ -15,45 +15,45 @@ Change revision in: core/java/src/net/i2p/CoreVersion.java Review the complete diff from the last release: - mtn diff -r t:i2p-0.6.(xx-1) > out.diff + mtn diff -r t:i2p-0.7.(xx-1) > out.diff vi out.diff Build and tag: ant pkg mtn ci - mtn tag h: i2p-0.6.xx + mtn tag h: i2p-0.7.xx Sync with mtn.i2p2.i2p Create a signed update file with: export I2P=~/i2p - java -cp $I2P/lib/i2p.jar net.i2p.crypto.TrustedUpdate sign i2pupdate.zip i2pupdate.sud /path/to/private.key 0.6.xx + java -cp $I2P/lib/i2p.jar net.i2p.crypto.TrustedUpdate sign i2pupdate.zip i2pupdate.sud /path/to/private.key 0.7.xx Verify signed update file with: java -cp $I2P/lib/i2p.jar net.i2p.crypto.TrustedUpdate showversion i2pupdate.sud java -cp $I2P/lib/i2p.jar net.i2p.crypto.TrustedUpdate verifysig i2pupdate.sud Make the source tarball: - Start with a clean checkout mtn -d i2p.mtn co --branch=i2p.i2p i2p-0.6.xx + Start with a clean checkout mtn -d i2p.mtn co --branch=i2p.i2p i2p-0.7.xx Double-check trust list - tar cjf i2psource-0.6.xx.tar.bz2 --exclude i2p-0.6.xx/_MTN i2p-0.6.xx - mv i2p-0.6.xx.tar.bz2 i2p.i2p + tar cjf i2psource-0.7.xx.tar.bz2 --exclude i2p-0.7.xx/_MTN i2p-0.7.xx + mv i2p-0.7.xx.tar.bz2 i2p.i2p Until the build script gets this ability, you need to rename some files: - mv i2pinstall.exe i2pinstall-0.6.xx.exe - mv i2p.tar.bz2 i2pheadless-0.6.xx.tar.bz2 - mv i2pupdate.zip i2pupdate-0.6.xx.zip + mv i2pinstall.exe i2pinstall-0.7.xx.exe + mv i2p.tar.bz2 i2pheadless-0.7.xx.tar.bz2 + mv i2pupdate.zip i2pupdate-0.7.xx.zip you probably don't need to rename i2pupdate.sud Generate hashes: - sha1sum i2p*0.6.xx.* + sha1sum i2p*0.7.xx.* sha1sum i2pupdate.sud now GPG-sign an announcement with the hashes Generate PGP signatures: - gpg -b i2pinstall-0.6.xx.exe - gpg -b i2pheadless-0.6.xx.tar.bz2 - gpg -b i2psource-0.6.xx.tar.bz2 - gpg -b i2pupdate-0.6.xx.zip + gpg -b i2pinstall-0.7.xx.exe + gpg -b i2pheadless-0.7.xx.tar.bz2 + gpg -b i2psource-0.7.xx.tar.bz2 + gpg -b i2pupdate-0.7.xx.zip gpg -b i2pupdate.sud Distribute files to download locations and to www.i2p2.i2p From 0343e8ffcd7ba0465aabdd3e1ca20472ac3d7676 Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 26 Mar 2009 18:51:43 +0000 Subject: [PATCH 064/688] readme_fr - thanks Narya and Mathiasdm --- build.xml | 3 ++- readme.html | 2 +- readme_de.html | 2 +- readme_fr.html | 37 +++++++++++++++++++++++++++++++++++++ readme_nl.html | 2 +- readme_sv.html | 4 ++-- 6 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 readme_fr.html diff --git a/build.xml b/build.xml index a9f58ef20..ef84b2aab 100644 --- a/build.xml +++ b/build.xml @@ -276,6 +276,7 @@ + - + diff --git a/readme.html b/readme.html index 9ba805a72..0e4891b5d 100644 --- a/readme.html +++ b/readme.html @@ -1,4 +1,4 @@ -

      English | Deutsch | Nederlands | Svenska

      +

      English | Deutsch | Français | Nederlands | Svenska

      If you've just started I2P, the Active: numbers on the left should start to grow over the next few minutes and you'll see a "shared clients" local destination listed on the left (if not, see below). Once those show up, diff --git a/readme_de.html b/readme_de.html index a6d233d6a..1534585da 100644 --- a/readme_de.html +++ b/readme_de.html @@ -1,4 +1,4 @@ -

      English | Deutsch | Nederlands | Svenska

      +

      English | Deutsch | Français | Nederlands | Svenska

      Wenn Du gerade I2P gestartet hast, sollten die "Active:" Zahlen links in den nächsten paar Minuten anwachsen und Du siehst dann dort ein "shared clients" lokales Ziel gelistet (falls nicht, siehe Unten). Sobald das erscheint, kannst Du:

      • "Eepsites" besuchen - In I2P sind anonym gehostete Websites - diff --git a/readme_fr.html b/readme_fr.html new file mode 100644 index 000000000..5e619cff2 --- /dev/null +++ b/readme_fr.html @@ -0,0 +1,37 @@ +

        Deutsch | English | Français | Nederlands | Svenska

        +

        Si vous venez juste de lancer I2P, les chiffres sur la gauche à coté de Active devraient commencer à augmenter dans les prochaines minutes et vous verrez un "Shared client" en destination locale listés sur la gauche (si non, voir plus bas). Une fois qu'ils apparaissent, vous pouvez:

        +
          +
        • parcourir les "eepsites" - sur I2P il y a des sites web anonymes hébergés - dites à votre navigateur d'utiliser le HTTP proxy a l'adresse localhost port 4444, ensuite vous pouvez naviguer sur les eepsites. + + Il y a bien plus d'eepsites - suivez juste les liens au départ de ceux sur lesquels vous êtes, mettez-les dans vos favoris et visitez-les souvent!
        • +
        • Parcourez le web - Il y a pour l'instant un outproxy HTTP sur I2P attaché à votre propre proxy HTTP sur le port 4444 - vous devez simplement configurer le proxy de votre navigateur pour l'utiliser (comme expliqué ci-dessus) et aller sur n'importe quel URL normale - vos requêtes seront relayées par le réseau i2p.
        • +
        • Transfer de fichiers - Il y a un port intégré de Snark le client BitTorrent.
        • +
        • Utiliser le service de mail anonyme - Postman a créé un sytème de mails compatible avec un client de messagerie normal (POP3 / SMTP) qui permet d'envoyer des emails autant au sein d'i2p que vers et à partir de l'internet normal! Créez-vous un compte à hq.postman.i2p. + Nous fournissons dans la version de base de i2p susimail, + un client web pop3/smtp orienté sur l'anonymat qui est configuré pour accéder aux services email de postman.
        • +
        • Chatter de manière anonyme - Activez votre client IRC et connectez-le sur le serveur localhost port 6668. Ceci pointe vers l'un des deux serveur IRC anonyme, mais ni vous ni eux ne savent qui est l'autre
        • +
        • Créez-vous un blog anonyme - Renseignez-vous chez Syndie
        • +
        • Et bien d'autres
        • +
        + +

        Vous voulez votre propre eepsite?

        + +

        Nous fournissons de base quelques logiciels pour vous permettre de créer votre propre eepsite - une instance +Jetty, qui écoute sur +http://localhost:7658/. Placer simplement vos fichiers dans le répertoire eepsite/docroot/ (ou placez n'importe quel fichier JSP/Servlet standard .war) dans eepsite/webapps, ou script CGI standard dans eepsite/cgi-bin) et ils apparaitront. Après avoir démarré un tunnel pour votre eepsite (le tunnel doit pointer sur l'adresse locale du eepsite), votre eepsite sera visible pour les autes. Des instructions plus détaillées pour créer un eepsite se trouvent sur Votre eepsite temporaire. +

        + +

        Dépannage

        + +

        Soyez patient - i2p peut s'avérer lent à démarrer la première fois car il recherche des pairs. Si, après 30 minutes, votre Actives: connecté/récent compte moins de 10 pairs connectés, vous devez ouvrir le port 8887 sur votre pare-feu pour avoir une meilleure connection. Si vous ne pouvez accéder à aucun eepsite (même www.i2p2.i2p), soyez sûr que votre navigateur utilise bien le proxy localhost sur le port 4444. Vous pouvez aussi faire part de votre démarche sur le site web I2P, poster des message sur le forum de discussion, ou passer par #i2p ou #i2p-chat sur IRC sur le serveur irc.freenode.net, irc.postman.i2p ou irc.freshcoffee.i2p (ils sont liés).

        + +

        Comme vous pouvez le remarquer, il suffit d'éditer la page "docs/readme.html" pour changer la page d'acceuil

        diff --git a/readme_nl.html b/readme_nl.html index 3203c79b5..427f75f91 100644 --- a/readme_nl.html +++ b/readme_nl.html @@ -1,4 +1,4 @@ -

        English | Deutsch | Nederlands | Svenska

        +

        English | Deutsch | Français | Nederlands | Svenska

        Als je net I2P opgestart hebt, zullen de 'Active:' (Actieve) getallen aan de linkerkant in de komende minuten stijgen, en je zal een "Shared clients" (Gedeelde clients) lokale bestemming zien staan aan de linkerkant (indien niet, zie hieronder). Eenmaal je deze bestemming ziet, kan je:

        • surfen naar "eepsites" - op I2P zijn er anonieme websites - stel je browser in om de HTTP proxy op localhost, poort 4444 te gebruiken, en surf vervolgens naar een eepsite - diff --git a/readme_sv.html b/readme_sv.html index e368fbc53..b1f59fa04 100644 --- a/readme_sv.html +++ b/readme_sv.html @@ -1,5 +1,5 @@

          English -| Deutsch | Deutsch | Français | Nederlands | Svenska

          Om du just har startat I2P kommer de "Aktiva: #/#" börja öka inom några få minuter och du kommer se en destination kallad "delade @@ -98,4 +98,4 @@ href="irc://irc.freenode.net/#i2p">irc.freenode.net, irc.postman.i2p eller irc.freshcoffee.i2p (de är alla sammankopplade).

          Du kan förändra denhär sidan genom att ändra i filen -"docs/readme_sv.html"

          \ No newline at end of file +"docs/readme_sv.html"

          From fe0d0d6737a8a5f3ae8342a1cac73ec9122b34cf Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 27 Mar 2009 18:09:46 +0000 Subject: [PATCH 065/688] -11, catch rare AIOOB --- history.txt | 5 +++++ router/java/src/net/i2p/data/i2np/TunnelGatewayMessage.java | 5 +++++ router/java/src/net/i2p/router/RouterVersion.java | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/history.txt b/history.txt index d3fcf2f4c..1495d99de 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,8 @@ +2009-03-27 zzz + * Add readme_fr.html + * License splash update + * Catch rare TunnelGatewayMessage AIOOB, root cause unknown + 2009-03-24 zzz * I2PTunnel: - Add some warnings about new features diff --git a/router/java/src/net/i2p/data/i2np/TunnelGatewayMessage.java b/router/java/src/net/i2p/data/i2np/TunnelGatewayMessage.java index f611c3213..8fc7c9fd9 100644 --- a/router/java/src/net/i2p/data/i2np/TunnelGatewayMessage.java +++ b/router/java/src/net/i2p/data/i2np/TunnelGatewayMessage.java @@ -75,6 +75,11 @@ public class TunnelGatewayMessage extends I2NPMessageImpl { } DataHelper.toLong(out, curIndex, 2, _msgData.length); curIndex += 2; + // where is this coming from? + if (curIndex + _msgData.length > out.length) { + _log.log(Log.ERROR, "output buffer too small idx: " + curIndex + " len: " + _msgData.length + " outlen: " + out.length); + throw new I2NPMessageException("Too much data to write out (id=" + _tunnelId + " data=" + _msg + ")"); + } System.arraycopy(_msgData, 0, out, curIndex, _msgData.length); curIndex += _msgData.length; return curIndex; diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index c1d6bdf7c..510adb6de 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -17,7 +17,7 @@ import net.i2p.CoreVersion; public class RouterVersion { public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 10; + public final static long BUILD = 11; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID); From 4a9543be78e3ada39c9572680068133096b9f4e5 Mon Sep 17 00:00:00 2001 From: complication Date: Sun, 29 Mar 2009 19:47:46 +0000 Subject: [PATCH 066/688] * Update versions, package release --- core/java/src/net/i2p/CoreVersion.java | 2 +- history.txt | 5 +++ initialNews.xml | 4 +- installer/install.xml | 2 +- news.xml | 42 +++++++++---------- .../src/net/i2p/router/RouterVersion.java | 2 +- 6 files changed, 29 insertions(+), 28 deletions(-) diff --git a/core/java/src/net/i2p/CoreVersion.java b/core/java/src/net/i2p/CoreVersion.java index c24542b00..6c924fe10 100644 --- a/core/java/src/net/i2p/CoreVersion.java +++ b/core/java/src/net/i2p/CoreVersion.java @@ -15,7 +15,7 @@ package net.i2p; */ public class CoreVersion { public final static String ID = "$Revision: 1.72 $ $Date: 2008-08-24 12:00:00 $"; - public final static String VERSION = "0.7"; + public final static String VERSION = "0.7.1"; public static void main(String args[]) { System.out.println("I2P Core version: " + VERSION); diff --git a/history.txt b/history.txt index 1495d99de..e76d41055 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,8 @@ +* 2009-03-29 0.7.1 released + +2009-03-29 Complication + * Update versions, package release + 2009-03-27 zzz * Add readme_fr.html * License splash update diff --git a/initialNews.xml b/initialNews.xml index 6a9c4b4ca..0306dcd7b 100644 --- a/initialNews.xml +++ b/initialNews.xml @@ -1,5 +1,5 @@ - - +

          Congratulations on getting I2P installed!

            diff --git a/installer/install.xml b/installer/install.xml index 8c7ff1bf4..125e92231 100644 --- a/installer/install.xml +++ b/installer/install.xml @@ -4,7 +4,7 @@ i2p - 0.7 + 0.7.1 diff --git a/news.xml b/news.xml index 63adee7fa..b85e0f130 100644 --- a/news.xml +++ b/news.xml @@ -1,5 +1,5 @@ - - +

            • -2009-01-24: 0.7 Released +2009-03-29: 0.7.1 Released

            -The 0.7 release adds stability and flexibility to I2PSnark, -which can hopefully be used to distribute I2P updates in future. +The 0.7.1 release optimizes I2P towards better performance +and introduces new features.

            -The I2P router gets fixes and optimizations to various -transport-level and streaming issues, network exploration, -NetDB performance and the UDP introducer system. -Among other features, the new release offers -better connection limiting, higher tolerance to "out of memory" exceptions -in helper applications, and an experimental new address system -using Base32 hashes of destination keys (".b32.i2p" URLs). +Multiple bugs are fixed, replacements to the SimpleTimer class +should waste less time on object locking. Some old components +are dropped and several classes refactored to avoid repeating code.

            -Both the BOB and SAM protocols are improved upon, -more old components dropped, Router Console features added -and a possible latency measurement attack mitigated. -From this release onwards, block lists for misbehaving peers -are activated by default. +Support for encrypted LeaseSets (for creation of links over I2P +which an adversary cannot obstruct by attacking its gateways) +becomes more complete. New tunnel types like IRC server tunnels +and new options like delayed start and idling of tunnels +also gain support, along with improved usability of the I2P +Socks proxy mechanism.

            -It seems worthwhile to remind that already since -the last release, I2P requires Java 1.5 or higher. -If you are uncertain about your Java version, you can verify -by opening a terminal window or command prompt, -and entering the command "java -version". -If you have an older Java installed, please update it first!

            +Work continues on streamlining and expanding the Router Console, +on the BOB protocol, on I2P ports for Debian and Slackware Linux, +on the I2PSnark client, on TCP connection properties +and multiple other fronts. Updating is highly recommended. +

            diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 510adb6de..450adcf28 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -17,7 +17,7 @@ import net.i2p.CoreVersion; public class RouterVersion { public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 11; + public final static long BUILD = 0; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID); From 0c98d1843acf6d458bd69adfd59f34652dba39f7 Mon Sep 17 00:00:00 2001 From: sponge Date: Mon, 30 Mar 2009 05:31:40 +0000 Subject: [PATCH 067/688] Sponge fixes and additions patch: * 3 New jbigi build scripts and old ones fixed to work properly. * Some trivial BOB work. --- apps/BOB/bob.config | 2 +- apps/BOB/nbproject/build-impl.xml | 2 +- apps/BOB/nbproject/genfiles.properties | 4 +- apps/BOB/nbproject/project.properties | 33 +++--- apps/BOB/src/net/i2p/BOB/TCPio.java | 3 + core/c/jbigi/build-all.sh | 5 +- core/c/jbigi/build.sh | 2 +- core/c/jbigi/build_jbigi.sh | 6 +- core/c/jbigi/mbuild-all.sh | 140 +++++++++++++++++++++++++ core/c/jbigi/mbuild_jbigi.sh | 47 +++++++++ core/c/mbuild.sh | 28 +++++ 11 files changed, 245 insertions(+), 27 deletions(-) create mode 100755 core/c/jbigi/mbuild-all.sh create mode 100755 core/c/jbigi/mbuild_jbigi.sh create mode 100755 core/c/mbuild.sh diff --git a/apps/BOB/bob.config b/apps/BOB/bob.config index f9c28d382..810d65b29 100644 --- a/apps/BOB/bob.config +++ b/apps/BOB/bob.config @@ -7,7 +7,7 @@ i2cp.tcp.port=7654 BOB.host=localhost inbound.lengthVariance=0 i2cp.messageReliability=BestEffort -BOB.port=45067 +BOB.port=45678 outbound.length=1 inbound.length=1 outbound.lengthVariance=0 diff --git a/apps/BOB/nbproject/build-impl.xml b/apps/BOB/nbproject/build-impl.xml index 9feea8e03..d8ed4de11 100644 --- a/apps/BOB/nbproject/build-impl.xml +++ b/apps/BOB/nbproject/build-impl.xml @@ -152,7 +152,7 @@ is divided into following sections: - + diff --git a/apps/BOB/nbproject/genfiles.properties b/apps/BOB/nbproject/genfiles.properties index dfc3734b8..ca344b0d3 100644 --- a/apps/BOB/nbproject/genfiles.properties +++ b/apps/BOB/nbproject/genfiles.properties @@ -4,5 +4,5 @@ build.xml.stylesheet.CRC32=958a1d3e # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. nbproject/build-impl.xml.data.CRC32=209349b6 -nbproject/build-impl.xml.script.CRC32=75fac64c -nbproject/build-impl.xml.stylesheet.CRC32=e55b27f5 +nbproject/build-impl.xml.script.CRC32=c51e188e +nbproject/build-impl.xml.stylesheet.CRC32=65b8de21 diff --git a/apps/BOB/nbproject/project.properties b/apps/BOB/nbproject/project.properties index 76e318ff0..7a94ff6dd 100644 --- a/apps/BOB/nbproject/project.properties +++ b/apps/BOB/nbproject/project.properties @@ -24,18 +24,22 @@ dist.dir=dist dist.jar=${dist.dir}/BOB.jar dist.javadoc.dir=${dist.dir}/javadoc excludes= +file.reference.build-javadoc=../../../i2p.i2p/build/javadoc file.reference.core.jar=../i2p.i2p/core/dist/core.jar file.reference.i2p.jar=../../bob/i2p/i2p.i2p/build/i2p.jar -file.reference.i2p.jar-1=../../core/java/build/i2p.jar +file.reference.i2p.jar-1=../../build/i2p.jar file.reference.i2p.jar-2=../i2p.i2p/core/java/build/i2p.jar -file.reference.i2ptunnel.jar=../i2ptunnel/java/build/i2ptunnel.jar +file.reference.i2p.jar-3=../../../i2p.i2p/build/i2p.jar +file.reference.i2ptunnel.jar=../../../i2p.i2p/build/i2ptunnel.jar file.reference.java-src=../i2p.i2p/core/java/src/ file.reference.jbigi.jar=../../bob/i2p/i2p.i2p/build/jbigi.jar file.reference.mstreaming.jar=../../bob/i2p/i2p.i2p/build/mstreaming.jar -file.reference.mstreaming.jar-1=../ministreaming/java/build/mstreaming.jar +file.reference.mstreaming.jar-1=../../build/mstreaming.jar +file.reference.mstreaming.jar-2=../../../i2p.i2p/build/mstreaming.jar file.reference.NetBeansProjects-i2p.i2p=../i2p.i2p/ +file.reference.router.jar=../../build/router.jar file.reference.streaming.jar=../../bob/i2p/i2p.i2p/build/streaming.jar -file.reference.streaming.jar-1=../streaming/java/build/streaming.jar +file.reference.streaming.jar-1=../../../i2p.i2p/build/streaming.jar file.reference.wrapper-freebsd=../../installer/lib/wrapper/freebsd/ file.reference.wrapper-linux=../../installer/lib/wrapper/linux/ file.reference.wrapper-linux64=../../installer/lib/wrapper/linux64/ @@ -43,24 +47,17 @@ file.reference.wrapper-macosx=../../installer/lib/wrapper/macosx/ file.reference.wrapper-solaris=../../installer/lib/wrapper/solaris/ file.reference.wrapper-win32=../../installer/lib/wrapper/win32/ file.reference.wrapper.jar=../../installer/lib/wrapper/linux/wrapper.jar -file.reference.wrapper.jar-1=../../installer/lib/wrapper/freebsd/wrapper.jar -file.reference.wrapper.jar-2=../../installer/lib/wrapper/linux64/wrapper.jar -file.reference.wrapper.jar-3=../../installer/lib/wrapper/macosx/wrapper.jar -file.reference.wrapper.jar-4=../../installer/lib/wrapper/solaris/wrapper.jar -file.reference.wrapper.jar-5=../../installer/lib/wrapper/win32/wrapper.jar includes=** jar.compress=false javac.classpath=\ - ${file.reference.i2p.jar-1}:\ - ${file.reference.i2ptunnel.jar}:\ - ${file.reference.mstreaming.jar-1}:\ - ${file.reference.streaming.jar-1}:\ - ${file.reference.wrapper.jar-1}:\ ${file.reference.wrapper.jar}:\ - ${file.reference.wrapper.jar-2}:\ - ${file.reference.wrapper.jar-3}:\ - ${file.reference.wrapper.jar-4}:\ - ${file.reference.wrapper.jar-5} + ${file.reference.streaming.jar-1}:\ + ${file.reference.i2ptunnel.jar}:\ + ${file.reference.i2p.jar-1}:\ + ${file.reference.router.jar}:\ + ${file.reference.mstreaming.jar-1}:\ + ${file.reference.mstreaming.jar-2}:\ + ${file.reference.i2p.jar-3} # Space-separated list of extra javac options javac.compilerargs= javac.deprecation=false diff --git a/apps/BOB/src/net/i2p/BOB/TCPio.java b/apps/BOB/src/net/i2p/BOB/TCPio.java index 41bb7cbe4..c9f4ab64c 100644 --- a/apps/BOB/src/net/i2p/BOB/TCPio.java +++ b/apps/BOB/src/net/i2p/BOB/TCPio.java @@ -76,6 +76,9 @@ public class TCPio implements Runnable { * * --Sponge * + * Tested with 128 bytes, and there was no performance gain. + * + * --Sponge */ int b; diff --git a/core/c/jbigi/build-all.sh b/core/c/jbigi/build-all.sh index 65456ee50..861a1e0a4 100755 --- a/core/c/jbigi/build-all.sh +++ b/core/c/jbigi/build-all.sh @@ -12,7 +12,7 @@ FreeBSD*) exit;; esac -VER=4.2.2 +VER=4.2.4 echo "Extracting GMP Version $VER ..." tar -xjf gmp-$VER.tar.bz2 echo "Building..." @@ -21,7 +21,8 @@ mkdir lib mkdir lib/net mkdir lib/net/i2p mkdir lib/net/i2p/util -for x in none pentium pentiummmx pentium2 pentium3 pentium4 k6 k62 k63 athlon + +for x in none pentium pentiummmx pentium2 pentium3 pentium4 k6 k62 k63 athlon geode pentiumm core2 do mkdir bin/$x cd bin/$x diff --git a/core/c/jbigi/build.sh b/core/c/jbigi/build.sh index 39969b13c..7ef900070 100755 --- a/core/c/jbigi/build.sh +++ b/core/c/jbigi/build.sh @@ -15,7 +15,7 @@ mkdir -p lib/ mkdir -p bin/local -VER=4.2.2 +VER=4.2.4 if [ "$1" != "dynamic" -a ! -d gmp-$VER ] then diff --git a/core/c/jbigi/build_jbigi.sh b/core/c/jbigi/build_jbigi.sh index 859eda329..8ff3219b8 100755 --- a/core/c/jbigi/build_jbigi.sh +++ b/core/c/jbigi/build_jbigi.sh @@ -1,6 +1,7 @@ #!/bin/sh # When executed in Mingw: Produces an jbigi.dll -# When executed in Linux: Produces an libjbigi.so +# When executed in Linux/FreeBSD: Produces an libjbigi.so +# Darwin produces libjbigi.jnilib, right? CC="gcc" @@ -32,7 +33,8 @@ esac #To link dynamically to GMP (use libgmp.so or gmp.lib), uncomment the first line below #To link statically to GMP, uncomment the second line below -if test $1 = "dynamic" +# Bug!!! Quote *BOTH* or neither! --Sponge +if test "$1" = "dynamic" then echo "Building jbigi lib that is dynamically linked to GMP" LIBPATH="-L.libs" diff --git a/core/c/jbigi/mbuild-all.sh b/core/c/jbigi/mbuild-all.sh new file mode 100755 index 000000000..a97139dfc --- /dev/null +++ b/core/c/jbigi/mbuild-all.sh @@ -0,0 +1,140 @@ +#/bin/bash + +# TO-DO: Darwin. + +# Note: You will have to add the CPU ID for the platform in the CPU ID code +# for a new CPU. Just adding them here won't let I2P use the code! + +# +# If you know of other platforms i2p on linux works on, +# please add them here. +# Do NOT add any X86 platforms, do that below in the x86 platform list. +# +MISC_LINUX_PLATFORMS="hppa2.0 alphaev56 armv5tel mips64el itanium itanium2 ultrasparc2 ultrasparc2i alphaev6 powerpc970 powerpc7455 powerpc7447 atom" + +# +# If you know of other platforms i2p on FREEBSD works on, +# please add them here. +# Do NOT add any X86 platforms, do that below in the x86 platform list. +# +MISC_FREEBSD_PLATFORMS="atom alphaev56 ultrasparc2i" + +# +# MINGW/Windows?? +# +MISC_MINGW_PLATFORMS="" + +# +# Are there any other X86 platforms that work on i2p? Add them here. +# +# Oddly athlon64 builds.... I wonder what others can :-) +# +X86_PLATFORMS="pentium pentiummmx pentium2 pentium3 pentium4 k6 k62 k63 athlon pentiumm core2 athlon64 geode" + + +# +# You should not need to edit anything below this comment. +# + +MINGW_PLATFORMS="${X86_PLATFORMS} ${MISC_MINGW_PLATFORMS}" +LINUX_PLATFORMS="${X86_PLATFORMS} ${MISC_LINUX_PLATFORMS}" +FREEBSD_PLATFORMS="${X86_PLATFORMS} ${MISC_FREEBSD_PLATFORMS}" + +VER=$(echo gmp-*.tar.bz2 | sed -re "s/(.*-)(.*)(.*.tar.bz2)$/\2/" | tail --lines=1) +if [ "$VER" == "" ] ; then + echo "ERROR! Can't find gmp source tarball." + exit 1 +fi + + +case `uname -sr` in +MINGW*) + PLATFORM_LIST="${MINGW_PLATFORMS}" + NAME="jbigi" + TYPE="dll" + TARGET="-windows-" + echo "Building windows .dlls for all architectures";; +Linux*) + PLATFORM_LIST="${LINUX_PLATFORMS}" + NAME="libjbigi" + TYPE="so" + TARGET="-linux-" + echo "Building linux .sos for all architectures";; +FreeBSD*) + PLATFORM_LIST="${FREEBSD_PLATFORMS}" + NAME="libjbigi" + TYPE="so" + TARGET="-freebsd-" + echo "Building freebsd .sos for all architectures";; +*) + echo "Unsupported build environment" + exit;; +esac + +function make_static { + echo "Attempting .${4} creation for ${3}${5}${2}" + ../../mbuild_jbigi.sh static || return 1 + cp ${3}.${4} ../../lib/net/i2p/util/${3}${5}${2}.${4} + return 0 +} + +function make_file { + # Nonfatal bail out on Failed build. + echo "Attempting build for ${3}${5}${2}" + make && return 0 + cd .. + rm -R "$2" + echo -e "\n\nFAILED! ${3}${5}${2} not made.\a" + sleep 10 + return 1 +} + +function configure_file { + echo -e "\n\n\nAttempting configure for ${3}${5}${2}\n\n\n" + sleep 10 + # Nonfatal bail out on unsupported platform. + ../../gmp-${1}/configure --build=${2} && return 0 + cd .. + rm -R "$2" + echo -e "\n\nSorry, ${3}${5}${2} is not supported on your build environment.\a" + sleep 10 + return 1 +} + +function build_file { + configure_file "$1" "$2" "$3" "$4" "$5" && make_file "$1" "$2" "$3" "$4" "$5" && make_static "$1" "$2" "$3" "$4" "$5" && return 0 + echo -e "\n\n\nError building static!\n\n\a" + sleep 10 + return 1 +} + +echo "Extracting GMP Version $VER ..." +tar -xf gmp-$VER.tar.bz2 || ( echo "Error in tarball file!" ; exit 1 ) + +if [ ! -d bin ]; then + mkdir bin +fi +if [ ! -d lib/net/i2p/util ]; then + mkdir -p lib/net/i2p/util +fi + +# Don't touch this one. +NO_PLATFORM=none + +for x in $NO_PLATFORM $PLATFORM_LIST +do + ( + if [ ! -d bin/$x ]; then + mkdir bin/$x + cd bin/$x + else + cd bin/$x + rm -Rf * + fi + + build_file "$VER" "$x" "$NAME" "$TYPE" "$TARGET" + ) +done + +echo "Success!" +exit 0 diff --git a/core/c/jbigi/mbuild_jbigi.sh b/core/c/jbigi/mbuild_jbigi.sh new file mode 100755 index 000000000..1e262a603 --- /dev/null +++ b/core/c/jbigi/mbuild_jbigi.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# When executed in Mingw: Produces an jbigi.dll +# When executed in Linux/FreeBSD: Produces an libjbigi.so +# What does Darwin produce? libjbigi.jnilib? +CC="gcc" + +case `uname -sr` in +MINGW*) + JAVA_HOME="c:/software/j2sdk1.4.2_05" + COMPILEFLAGS="-Wall" + INCLUDES="-I. -I../../jbigi/include -I$JAVA_HOME/include/win32/ -I$JAVA_HOME/include/" + LINKFLAGS="-shared -Wl,--kill-at" + LIBFILE="jbigi.dll";; +CYGWIN*) + JAVA_HOME="c:/software/j2sdk1.4.2_05" + COMPILEFLAGS="-Wall -mno-cygwin" + INCLUDES="-I. -I../../jbigi/include -I$JAVA_HOME/include/win32/ -I$JAVA_HOME/include/" + LINKFLAGS="-shared -Wl,--kill-at" + LIBFILE="jbigi.dll";; +Darwin*) + JAVA_HOME="/Library/Java/Home" + COMPILEFLAGS="-Wall" + INCLUDES="-I. -I../../jbigi/include -I$JAVA_HOME/include" + LINKFLAGS="-dynamiclib -framework JavaVM" + LIBFILE="libjbigi.jnilib";; +*) + COMPILEFLAGS="-fPIC -Wall" + INCLUDES="-I. -I../../jbigi/include -I$JAVA_HOME/include -I$JAVA_HOME/include/linux" + LINKFLAGS="-shared -Wl,-soname,libjbigi.so" + LIBFILE="libjbigi.so";; +esac + +if [ "$1" = "dynamic" ] ; then + echo "Building a jbigi lib that is dynamically linked to GMP" + LIBPATH="-L.libs" + INCLUDELIBS="-lgmp" +else + echo "Building a jbigi lib that is statically linked to GMP" + STATICLIBS=".libs/libgmp.a" +fi + +echo "Compiling C code..." +rm -f jbigi.o $LIBFILE +$CC -c $COMPILEFLAGS $INCLUDES ../../jbigi/src/jbigi.c || exit 1 +$CC $LINKFLAGS $INCLUDES $INCLUDELIBS -o $LIBFILE jbigi.o $STATICLIBS || exit 1 + +exit 0 diff --git a/core/c/mbuild.sh b/core/c/mbuild.sh new file mode 100755 index 000000000..4591d5b7c --- /dev/null +++ b/core/c/mbuild.sh @@ -0,0 +1,28 @@ +#/bin/bash + +JBIGI=../../../installer/lib/jbigi/jbigi.jar + +if [ -f jbigi.jarx ] ; then +JBIGI=../jbigi.jar +fi + +(cd jcpuid ; sh build.sh ; cd ..) +(cd jbigi ; sh mbuild-all.sh ; cd ..) + +mkdir t + +( + cd t + jar xf ../../../installer/lib/jbigi/jbigi.jar +) + +cp jbigi/lib/net/i2p/util/* t/ +cp jcpuid/lib/freenet/support/CPUInformation/* t/ + +( + cd t + jar cf ../jbigi.jar . +) + +rm -R t +echo "jbigi.jar Refreshed." From e692e18d44339b111014493b496dc092550c2550 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 30 Mar 2009 16:05:48 +0000 Subject: [PATCH 068/688] Peer Selection: - Limit peers to a max % of all tunnels with router.maxTunnelPercentage=nn, default 33 - Add chart to tunnels.jsp to see results --- .../i2p/router/DummyTunnelManagerFacade.java | 2 + .../net/i2p/router/TunnelManagerFacade.java | 4 + .../tunnel/pool/TunnelPeerSelector.java | 1 + .../i2p/router/tunnel/pool/TunnelPool.java | 4 +- .../router/tunnel/pool/TunnelPoolManager.java | 133 ++++++++++++++++++ 5 files changed, 142 insertions(+), 2 deletions(-) diff --git a/router/java/src/net/i2p/router/DummyTunnelManagerFacade.java b/router/java/src/net/i2p/router/DummyTunnelManagerFacade.java index f7f204857..aec0390fd 100644 --- a/router/java/src/net/i2p/router/DummyTunnelManagerFacade.java +++ b/router/java/src/net/i2p/router/DummyTunnelManagerFacade.java @@ -10,6 +10,7 @@ package net.i2p.router; import java.io.IOException; import java.io.Writer; +import java.util.Set; import net.i2p.data.Destination; import net.i2p.data.Hash; @@ -44,6 +45,7 @@ class DummyTunnelManagerFacade implements TunnelManagerFacade { public void setInboundSettings(Hash client, TunnelPoolSettings settings) {} public void setOutboundSettings(Hash client, TunnelPoolSettings settings) {} public int getInboundBuildQueueSize() { return 0; } + public Set selectPeersInTooManyTunnels() { return null; } public void renderStatusHTML(Writer out) throws IOException {} public void restart() {} diff --git a/router/java/src/net/i2p/router/TunnelManagerFacade.java b/router/java/src/net/i2p/router/TunnelManagerFacade.java index a6c1c9614..da0482e6f 100644 --- a/router/java/src/net/i2p/router/TunnelManagerFacade.java +++ b/router/java/src/net/i2p/router/TunnelManagerFacade.java @@ -10,6 +10,7 @@ package net.i2p.router; import java.io.IOException; import java.io.Writer; +import java.util.Set; import net.i2p.data.Destination; import net.i2p.data.Hash; @@ -62,6 +63,9 @@ public interface TunnelManagerFacade extends Service { /** count how many inbound tunnel requests we have received but not yet processed */ public int getInboundBuildQueueSize(); + /** @return Set of peers that should not be allowed to be in another tunnel */ + public Set selectPeersInTooManyTunnels(); + /** * the client connected (or updated their settings), so make sure we have * the tunnels for them, and whenever necessary, ask them to authorize diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPeerSelector.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPeerSelector.java index 1e0247c02..92e454517 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPeerSelector.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPeerSelector.java @@ -176,6 +176,7 @@ public abstract class TunnelPeerSelector { Set peers = new HashSet(1); peers.addAll(ctx.profileOrganizer().selectPeersRecentlyRejecting()); + peers.addAll(ctx.tunnelManager().selectPeersInTooManyTunnels()); // if (false && filterUnreachable(ctx, isInbound, isExploratory)) { if (filterUnreachable(ctx, isInbound, isExploratory)) { List caps = ctx.peerManager().getPeersByCapability(Router.CAPABILITY_UNREACHABLE); 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 06a9b4999..699a8be9f 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java @@ -28,7 +28,7 @@ public class TunnelPool { private RouterContext _context; private Log _log; private TunnelPoolSettings _settings; - private ArrayList _tunnels; + private ArrayList _tunnels; private TunnelPeerSelector _peerSelector; private TunnelPoolManager _manager; private boolean _alive; @@ -227,7 +227,7 @@ public class TunnelPool { * * @return list of TunnelInfo objects */ - public List listTunnels() { + public List listTunnels() { synchronized (_tunnels) { return new ArrayList(_tunnels); } diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java index 58637ce63..acbb9345d 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java @@ -6,9 +6,12 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import net.i2p.data.DataHelper; import net.i2p.data.Destination; @@ -506,6 +509,7 @@ public class TunnelPoolManager implements TunnelManagerFacade { out.write("
    \n"); out.write("Inactive participating tunnels: " + inactive + "
    \n"); out.write("Lifetime bandwidth usage: " + DataHelper.formatSize(processed*1024) + "B
    \n"); + renderPeers(out); } class TunnelComparator implements Comparator { @@ -579,6 +583,135 @@ public class TunnelPoolManager implements TunnelManagerFacade { DataHelper.formatSize(processedOut*1024) + "B out
    "); } + private void renderPeers(Writer out) throws IOException { + // count up the peers in the local pools + HashCounter lc = new HashCounter(); + int tunnelCount = countTunnelsPerPeer(lc); + + // count up the peers in the participating tunnels + HashCounter pc = new HashCounter(); + int partCount = countParticipatingPerPeer(pc); + + Set peers = new HashSet(lc.hashes()); + peers.addAll(pc.hashes()); + List peerList = new ArrayList(peers); + Collections.sort(peerList, new HashComparator()); + + out.write("

    Tunnel Counts By Peer:

    \n"); + out.write("\n"); + for (Hash h : peerList) { + out.write("
    PeerExpl. + Client% of totalPart. from + to% of total
    "); + out.write(netDbLink(h)); + out.write("" + lc.count(h)); + out.write(""); + if (tunnelCount > 0) + out.write("" + (lc.count(h) * 100 / tunnelCount)); + else + out.write('0'); + out.write("" + pc.count(h)); + out.write(""); + if (partCount > 0) + out.write("" + (pc.count(h) * 100 / partCount)); + else + out.write('0'); + out.write('\n'); + } + out.write("
    Tunnels" + tunnelCount); + out.write(" " + partCount); + out.write(" 
    \n"); + } + + /** @return total number of non-fallback expl. + client tunnels */ + private int countTunnelsPerPeer(HashCounter lc) { + List pools = new ArrayList(); + listPools(pools); + int tunnelCount = 0; + for (TunnelPool tp : pools) { + for (TunnelInfo info : tp.listTunnels()) { + if (info.getLength() > 1) { + tunnelCount++; + for (int j = 0; j < info.getLength(); j++) { + Hash peer = info.getPeer(j); + if (!_context.routerHash().equals(peer)) + lc.increment(peer); + } + } + } + } + return tunnelCount; + } + + private static final int DEFAULT_MAX_PCT_TUNNELS = 33; + /** + * For reliability reasons, don't allow a peer in more than x% of + * client and exploratory tunnels. + * + * This also will prevent a single huge-capacity (or malicious) peer from + * taking all the tunnels in the network (although it would be nice to limit + * the % of total network tunnels to 10% or so, but that appears to be + * too low to set as a default here... much lower than 33% will push client + * tunnels out of the fast tier into high cap or beyond...) + * + * Possible improvement - restrict based on count per IP, or IP block, + * to slightly increase costs of collusion + * + * @return Set of peers that should not be allowed in another tunnel + */ + public Set selectPeersInTooManyTunnels() { + HashCounter lc = new HashCounter(); + int tunnelCount = countTunnelsPerPeer(lc); + Set rv = new HashSet(); + if (tunnelCount >= 4 && _context.router().getUptime() > 10*60*1000) { + int max = _context.getProperty("router.maxTunnelPercentage", DEFAULT_MAX_PCT_TUNNELS); + for (Hash h : lc.hashes()) { + if (lc.count(h) > 0 && (lc.count(h) + 1) * 100 / (tunnelCount + 1) > max) + rv.add(h); + } + } + return rv; + } + + /** @return total number of part. tunnels */ + private int countParticipatingPerPeer(HashCounter pc) { + List participating = _context.tunnelDispatcher().listParticipatingTunnels(); + for (HopConfig cfg : participating) { + Hash from = cfg.getReceiveFrom(); + if (from != null) + pc.increment(from); + Hash to = cfg.getSendTo(); + if (to != null) + pc.increment(to); + } + return participating.size(); + } + + class HashComparator implements Comparator { + public int compare(Object l, Object r) { + return ((Hash)l).toBase64().compareTo(((Hash)r).toBase64()); + } + } + + private static class HashCounter { + private ConcurrentHashMap _map; + public HashCounter() { + _map = new ConcurrentHashMap(); + } + public void increment(Hash h) { + Integer i = _map.putIfAbsent(h, Integer.valueOf(1)); + if (i != null) + _map.put(h, Integer.valueOf(i.intValue() + 1)); + } + public int count(Hash h) { + Integer i = _map.get(h); + if (i != null) + return i.intValue(); + return 0; + } + public Set hashes() { + return _map.keySet(); + } + } + private String getCapacity(Hash peer) { RouterInfo info = _context.netDb().lookupRouterInfoLocally(peer); if (info != null) { From 58fc3a501dfa9e7e0b82e7cd34da2f6d1e33a15c Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 30 Mar 2009 16:13:58 +0000 Subject: [PATCH 069/688] -1 --- history.txt | 10 ++++++++++ router/java/src/net/i2p/router/RouterVersion.java | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/history.txt b/history.txt index e76d41055..f027db23a 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,13 @@ +2009-03-30 zzz + * I2CP: + - Implement BandwidthLimitsMessage + - Have i2psnark use new message, remove + build dependency on router + * Peer Selection: + - Limit peers to a max % of all tunnels with + router.maxTunnelPercentage=nn, default 33 + - Add chart to tunnels.jsp to see results + * 2009-03-29 0.7.1 released 2009-03-29 Complication diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 450adcf28..1d4797d6f 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -17,7 +17,7 @@ import net.i2p.CoreVersion; public class RouterVersion { public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 0; + public final static long BUILD = 1; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID); From f81a24a0cc1856ee18f76db8ba6d2cf68c6c8639 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 1 Apr 2009 04:54:49 +0000 Subject: [PATCH 070/688] I2PTunnel: Fix tunnel close http://forum.i2p/viewtopic.php?t=3231 broken in 0.7-8 --- .../java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java | 1 + history.txt | 4 ++++ router/java/src/net/i2p/router/RouterVersion.java | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java index b6eb39224..f5f0df6b9 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java @@ -278,6 +278,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna } } sockManager.setName("Client"); + tunnel.addSession(sockManager.getSession()); return sockManager; } diff --git a/history.txt b/history.txt index f027db23a..9ac00e87b 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,7 @@ +2009-04-01 zzz + * I2PTunnel: Fix tunnel close + http://forum.i2p/viewtopic.php?t=3231 + 2009-03-30 zzz * I2CP: - Implement BandwidthLimitsMessage diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 1d4797d6f..a727710d6 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -17,7 +17,7 @@ import net.i2p.CoreVersion; public class RouterVersion { public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 1; + public final static long BUILD = 2; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID); From 0b89171abd51681c18a8bf62cc8d47e8ba042a4a Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 1 Apr 2009 20:17:25 +0000 Subject: [PATCH 071/688] StatisticsManager - effective in 0.7.2: - Spoof uptime to 90m for all - Change tunnel stats from 10m to 60m --- .../src/net/i2p/router/StatisticsManager.java | 76 +++++-------------- .../i2p/router/tunnel/pool/BuildExecutor.java | 12 +-- 2 files changed, 27 insertions(+), 61 deletions(-) diff --git a/router/java/src/net/i2p/router/StatisticsManager.java b/router/java/src/net/i2p/router/StatisticsManager.java index d959c25d3..4a286fe3d 100644 --- a/router/java/src/net/i2p/router/StatisticsManager.java +++ b/router/java/src/net/i2p/router/StatisticsManager.java @@ -29,12 +29,9 @@ public class StatisticsManager implements Service { private Log _log; private RouterContext _context; private boolean _includePeerRankings; - private int _publishedStats; public final static String PROP_PUBLISH_RANKINGS = "router.publishPeerRankings"; public final static String DEFAULT_PROP_PUBLISH_RANKINGS = "true"; - public final static String PROP_MAX_PUBLISHED_PEERS = "router.publishPeerMax"; - public final static int DEFAULT_MAX_PUBLISHED_PEERS = 10; private final DecimalFormat _fmt; private final DecimalFormat _pct; @@ -52,45 +49,12 @@ public class StatisticsManager implements Service { startup(); } public void startup() { - String val = _context.router().getConfigSetting(PROP_PUBLISH_RANKINGS); - try { - if (val == null) { - if (_log.shouldLog(Log.INFO)) - _log.info("Peer publishing setting " + PROP_PUBLISH_RANKINGS - + " not set - using default " + DEFAULT_PROP_PUBLISH_RANKINGS); - val = DEFAULT_PROP_PUBLISH_RANKINGS; - } else { - if (_log.shouldLog(Log.INFO)) - _log.info("Peer publishing setting " + PROP_PUBLISH_RANKINGS - + " set to " + val); - } - boolean v = Boolean.TRUE.toString().equalsIgnoreCase(val); - _includePeerRankings = v; - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Setting includePeerRankings = " + v); - } catch (Throwable t) { - if (_log.shouldLog(Log.ERROR)) - _log.error("Error determining whether to publish rankings [" - + PROP_PUBLISH_RANKINGS + "=" + val - + "], so we're defaulting to FALSE"); - _includePeerRankings = false; - } - val = _context.router().getConfigSetting(PROP_MAX_PUBLISHED_PEERS); - if (val == null) { - _publishedStats = DEFAULT_MAX_PUBLISHED_PEERS; - } else { - try { - int num = Integer.parseInt(val); - _publishedStats = num; - } catch (NumberFormatException nfe) { - if (_log.shouldLog(Log.ERROR)) - _log.error("Invalid max number of peers to publish [" + val - + "], defaulting to " + DEFAULT_MAX_PUBLISHED_PEERS, nfe); - _publishedStats = DEFAULT_MAX_PUBLISHED_PEERS; - } - } + String val = _context.getProperty(PROP_PUBLISH_RANKINGS, DEFAULT_PROP_PUBLISH_RANKINGS); + _includePeerRankings = Boolean.valueOf(val); } + static final boolean CommentOutIn072 = RouterVersion.VERSION.equals("0.7.1"); + /** Retrieve a snapshot of the statistics that should be published */ public Properties publishStatistics() { Properties stats = new Properties(); @@ -124,9 +88,6 @@ public class StatisticsManager implements Service { ***/ if (_includePeerRankings) { - if (false) - stats.putAll(_context.profileManager().summarizePeers(_publishedStats)); - long publishedUptime = _context.router().getUptime(); // Don't publish these for first hour if (publishedUptime > 60*60*1000) @@ -172,12 +133,16 @@ public class StatisticsManager implements Service { //includeRate("stream.con.sendDuplicateSize", stats, new long[] { 60*60*1000 }); //includeRate("stream.con.receiveDuplicateSize", stats, new long[] { 60*60*1000 }); - // Round smaller uptimes to 1 hour, to frustrate uptime tracking - // Round 2nd hour to 90m since peers use 2h minimum to route - if (publishedUptime < 60*60*1000) publishedUptime = 60*60*1000; - else if (publishedUptime < 2*60*60*1000) publishedUptime = 90*60*1000; - - stats.setProperty("stat_uptime", DataHelper.formatDuration(publishedUptime)); + if (CommentOutIn072) { + // Round smaller uptimes to 1 hour, to frustrate uptime tracking + // Round 2nd hour to 90m since peers use 2h minimum to route + if (publishedUptime < 60*60*1000) publishedUptime = 60*60*1000; + else if (publishedUptime < 2*60*60*1000) publishedUptime = 90*60*1000; + stats.setProperty("stat_uptime", DataHelper.formatDuration(publishedUptime)); + } else { + // So that we will still get build requests + stats.setProperty("stat_uptime", "90m"); + } //stats.setProperty("stat__rateKey", "avg;maxAvg;pctLifetime;[sat;satLim;maxSat;maxSatLim;][num;lifetimeFreq;maxFreq]"); //includeRate("tunnel.decryptRequestTime", stats, new long[] { 60*1000, 10*60*1000 }); @@ -185,12 +150,13 @@ public class StatisticsManager implements Service { //includeRate("udp.packetVerifyTime", stats, new long[] { 60*1000 }); //includeRate("tunnel.buildRequestTime", stats, new long[] { 10*60*1000 }); - includeRate("tunnel.buildClientExpire", stats, new long[] { 10*60*1000 }); - includeRate("tunnel.buildClientReject", stats, new long[] { 10*60*1000 }); - includeRate("tunnel.buildClientSuccess", stats, new long[] { 10*60*1000 }); - includeRate("tunnel.buildExploratoryExpire", stats, new long[] { 10*60*1000 }); - includeRate("tunnel.buildExploratoryReject", stats, new long[] { 10*60*1000 }); - includeRate("tunnel.buildExploratorySuccess", stats, new long[] { 10*60*1000 }); + long rate = CommentOutIn072 ? 10*60*1000 : 60*60*1000; + includeRate("tunnel.buildClientExpire", stats, new long[] { rate }); + includeRate("tunnel.buildClientReject", stats, new long[] { rate }); + includeRate("tunnel.buildClientSuccess", stats, new long[] { rate }); + includeRate("tunnel.buildExploratoryExpire", stats, new long[] { rate }); + includeRate("tunnel.buildExploratoryReject", stats, new long[] { rate }); + includeRate("tunnel.buildExploratorySuccess", stats, new long[] { rate }); //includeRate("tunnel.rejectTimeout", stats, new long[] { 10*60*1000 }); //includeRate("tunnel.rejectOverloaded", stats, new long[] { 10*60*1000 }); //includeRate("tunnel.acceptLoad", stats, new long[] { 10*60*1000 }); diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java b/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java index 3a84f4810..06a666354 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java +++ b/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java @@ -37,12 +37,12 @@ class BuildExecutor implements Runnable { _currentlyBuilding = new ArrayList(10); _context.statManager().createRateStat("tunnel.concurrentBuilds", "How many builds are going at once", "Tunnels", new long[] { 60*1000, 5*60*1000, 60*60*1000 }); _context.statManager().createRateStat("tunnel.concurrentBuildsLagged", "How many builds are going at once when we reject further builds, due to job lag (period is lag)", "Tunnels", new long[] { 60*1000, 5*60*1000, 60*60*1000 }); - _context.statManager().createRateStat("tunnel.buildExploratoryExpire", "How often an exploratory tunnel times out during creation", "Tunnels", new long[] { 60*1000, 10*60*1000 }); - _context.statManager().createRateStat("tunnel.buildClientExpire", "How often a client tunnel times out during creation", "Tunnels", new long[] { 60*1000, 10*60*1000 }); - _context.statManager().createRateStat("tunnel.buildExploratorySuccess", "Response time for success", "Tunnels", new long[] { 60*1000, 10*60*1000 }); - _context.statManager().createRateStat("tunnel.buildClientSuccess", "Response time for success", "Tunnels", new long[] { 60*1000, 10*60*1000 }); - _context.statManager().createRateStat("tunnel.buildExploratoryReject", "Response time for rejection", "Tunnels", new long[] { 60*1000, 10*60*1000 }); - _context.statManager().createRateStat("tunnel.buildClientReject", "Response time for rejection", "Tunnels", new long[] { 60*1000, 10*60*1000 }); + _context.statManager().createRateStat("tunnel.buildExploratoryExpire", "How often an exploratory tunnel times out during creation", "Tunnels", new long[] { 10*60*1000, 60*60*1000 }); + _context.statManager().createRateStat("tunnel.buildClientExpire", "How often a client tunnel times out during creation", "Tunnels", new long[] { 10*60*1000, 60*60*1000 }); + _context.statManager().createRateStat("tunnel.buildExploratorySuccess", "Response time for success", "Tunnels", new long[] { 10*60*1000, 60*60*1000 }); + _context.statManager().createRateStat("tunnel.buildClientSuccess", "Response time for success", "Tunnels", new long[] { 10*60*1000, 60*60*1000 }); + _context.statManager().createRateStat("tunnel.buildExploratoryReject", "Response time for rejection", "Tunnels", new long[] { 10*60*1000, 60*60*1000 }); + _context.statManager().createRateStat("tunnel.buildClientReject", "Response time for rejection", "Tunnels", new long[] { 10*60*1000, 60*60*1000 }); _context.statManager().createRateStat("tunnel.buildRequestTime", "How long it takes to build a tunnel request", "Tunnels", new long[] { 60*1000, 10*60*1000 }); _context.statManager().createRateStat("tunnel.buildRequestZeroHopTime", "How long it takes to build a zero hop tunnel", "Tunnels", new long[] { 60*1000, 10*60*1000 }); _context.statManager().createRateStat("tunnel.pendingRemaining", "How many inbound requests are pending after a pass (period is how long the pass takes)?", "Tunnels", new long[] { 60*1000, 10*60*1000 }); From a4d16af95d36d3df3f8a1a22fb66660f721bfc96 Mon Sep 17 00:00:00 2001 From: mkvore-commit Date: Thu, 2 Apr 2009 08:22:31 +0000 Subject: [PATCH 072/688] SAM version 3 : - Raw and Datagram sessions implemented - option "SILENT=true" added to the stream protocol - java 6 warnings removed ministreaming : - java 6 warnings removed ministreaming and streaming : - added functions : I2PServerSocket.waitIncoming(long timeout) I2PServerSocket.accept(boolean block) --- .../i2p/client/streaming/I2PServerSocket.java | 30 + .../client/streaming/I2PServerSocketImpl.java | 107 ++- .../i2p/client/streaming/I2PSocketImpl.java | 1 - .../streaming/I2PSocketManagerFactory.java | 1 - .../streaming/I2PSocketManagerImpl.java | 19 +- .../net/i2p/client/streaming/TestSwarm.java | 14 + apps/sam/Demos/datagramTests/README.txt | 15 + apps/sam/Demos/datagramTests/samForward.py | 35 + apps/sam/Demos/datagramTests/samIn.py | 29 + apps/sam/Demos/datagramTests/samOut.py | 31 + apps/sam/Demos/rawTests/README.txt | 15 + apps/sam/Demos/rawTests/samForward.py | 36 + apps/sam/Demos/rawTests/samIn.py | 31 + apps/sam/Demos/rawTests/samOut.py | 31 + apps/sam/Demos/streamTests/README.txt | 24 + apps/sam/Demos/streamTests/samForward.py | 58 ++ apps/sam/Demos/streamTests/samIn.py | 89 +++ apps/sam/Demos/streamTests/samOut.py | 52 ++ .../sam/Demos/streamTests/samOutWithNaming.py | 51 ++ apps/sam/Demos/streamTests/server.py | 41 + apps/sam/doc/protocol-v3.txt | 17 + apps/sam/java/src/net/i2p/sam/SAMBridge.java | 36 +- .../src/net/i2p/sam/SAMDatagramSession.java | 9 +- .../java/src/net/i2p/sam/SAMException.java | 6 +- apps/sam/java/src/net/i2p/sam/SAMHandler.java | 46 +- .../src/net/i2p/sam/SAMHandlerFactory.java | 21 +- .../i2p/sam/SAMInvalidDirectionException.java | 3 +- .../src/net/i2p/sam/SAMMessageSession.java | 3 +- .../java/src/net/i2p/sam/SAMRawSession.java | 2 +- .../src/net/i2p/sam/SAMStreamReceiver.java | 3 +- .../src/net/i2p/sam/SAMStreamSession.java | 27 +- apps/sam/java/src/net/i2p/sam/SAMUtils.java | 18 + .../java/src/net/i2p/sam/SAMv1Handler.java | 103 ++- .../java/src/net/i2p/sam/SAMv2Handler.java | 6 +- .../src/net/i2p/sam/SAMv2StreamSession.java | 18 +- .../src/net/i2p/sam/SAMv3DatagramSession.java | 90 +++ .../java/src/net/i2p/sam/SAMv3Handler.java | 748 ++++++++++++++++++ .../java/src/net/i2p/sam/SAMv3RawSession.java | 88 +++ .../src/net/i2p/sam/SAMv3StreamSession.java | 389 +++++++++ .../net/i2p/sam/client/SAMEventHandler.java | 6 +- .../src/net/i2p/sam/client/SAMStreamSend.java | 11 +- .../src/net/i2p/sam/client/SAMStreamSink.java | 11 +- .../client/streaming/ConnectionHandler.java | 46 +- .../client/streaming/I2PServerSocketFull.java | 44 ++ 44 files changed, 2278 insertions(+), 183 deletions(-) create mode 100644 apps/sam/Demos/datagramTests/README.txt create mode 100755 apps/sam/Demos/datagramTests/samForward.py create mode 100755 apps/sam/Demos/datagramTests/samIn.py create mode 100755 apps/sam/Demos/datagramTests/samOut.py create mode 100644 apps/sam/Demos/rawTests/README.txt create mode 100755 apps/sam/Demos/rawTests/samForward.py create mode 100755 apps/sam/Demos/rawTests/samIn.py create mode 100755 apps/sam/Demos/rawTests/samOut.py create mode 100644 apps/sam/Demos/streamTests/README.txt create mode 100755 apps/sam/Demos/streamTests/samForward.py create mode 100755 apps/sam/Demos/streamTests/samIn.py create mode 100755 apps/sam/Demos/streamTests/samOut.py create mode 100755 apps/sam/Demos/streamTests/samOutWithNaming.py create mode 100755 apps/sam/Demos/streamTests/server.py create mode 100644 apps/sam/doc/protocol-v3.txt create mode 100644 apps/sam/java/src/net/i2p/sam/SAMv3DatagramSession.java create mode 100644 apps/sam/java/src/net/i2p/sam/SAMv3Handler.java create mode 100644 apps/sam/java/src/net/i2p/sam/SAMv3RawSession.java create mode 100644 apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocket.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocket.java index d0028fdb8..e7db251fa 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocket.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocket.java @@ -30,6 +30,36 @@ public interface I2PServerSocket { */ public I2PSocket accept() throws I2PException, ConnectException, SocketTimeoutException; + /** + * accept(true) has the same behaviour as accept(). + * accept(false) does not wait for a socket connecting. If a socket is + * available in the queue, it is accepted. Else, null is returned. + * + * @param true if the call should block until a socket is available + * + * @return a connected I2PSocket, or null + * + * @throws I2PException if there is a problem with reading a new socket + * from the data available (aka the I2PSession closed, etc) + * @throws ConnectException if the I2PServerSocket is closed + * @throws SocketTimeoutException + */ + public I2PSocket accept(boolean blocking) throws I2PException, ConnectException, SocketTimeoutException; + + /** + * Waits until there is a socket waiting for acception or the timeout is + * reached. + * + * @param timeoutMs timeout in ms. A negative value waits forever. + * + * @return true if a socket is available, false if not + * + * @throws I2PException if there is a problem with reading a new socket + * from the data available (aka the I2PSession closed, etc) + * @throws ConnectException if the I2PServerSocket is closed + */ + public boolean waitIncoming(long timeoutMs) throws I2PException, ConnectException, InterruptedException; + /** * Set Sock Option accept timeout * @param x timeout in ms diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocketImpl.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocketImpl.java index 93db8595b..a2c075e3e 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocketImpl.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocketImpl.java @@ -20,7 +20,7 @@ class I2PServerSocketImpl implements I2PServerSocket { private final static Log _log = new Log(I2PServerSocketImpl.class); private I2PSocketManager mgr; /** list of sockets waiting for the client to accept them */ - private List pendingSockets = Collections.synchronizedList(new ArrayList(4)); + private List pendingSockets = Collections.synchronizedList(new ArrayList(4)); /** have we been closed */ private volatile boolean closing = false; @@ -49,7 +49,90 @@ class I2PServerSocketImpl implements I2PServerSocket { this.mgr = mgr; } + + + + + /** + * Waits until there is a socket waiting for acception or the timeout is + * reached. + * + * @param timeoutMs timeout in ms. A negative value waits forever. + * + * @return true if a socket is available, false if not + * + * @throws I2PException if there is a problem with reading a new socket + * from the data available (aka the I2PSession closed, etc) + * @throws ConnectException if the I2PServerSocket is closed + */ + public boolean waitIncoming(long timeoutMs) throws I2PException, ConnectException { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("waitIncoming() called, pending: " + pendingSockets.size()); + + boolean isTimed = (timeoutMs>=0); + if (isTimed) { + Clock clock = I2PAppContext.getGlobalContext().clock(); + long now = clock.now(); + long end = now + timeoutMs; + while (pendingSockets.size() <= 0 && now0); + } + + + /** + * accept(true) has the same behaviour as accept(). + * accept(false) does not wait for a socket connecting. If a socket is + * available in the queue, it is accepted. Else, null is returned. + * + * @param true if the call should block until a socket is available + * + * @return a connected I2PSocket, or null + * + * @throws I2PException if there is a problem with reading a new socket + * from the data available (aka the I2PSession closed, etc) + * @throws ConnectException if the I2PServerSocket is closed + */ + + public I2PSocket accept(boolean blocking) throws I2PException, ConnectException { + I2PSocket ret = null; + + if (blocking) { + ret = accept(); + } else { + synchronized (pendingSockets) { + if (pendingSockets.size() > 0) { + ret = (I2PSocket)pendingSockets.remove(0); + } + } + if (ret != null) { + synchronized (socketAcceptedLock) { + socketAcceptedLock.notifyAll(); + } + } + } + return ret; + } + + /** * Waits for the next socket connecting. If a remote user tried to make a * connection and the local application wasn't .accept()ing new connections, * they should get refused (if .accept() doesnt occur in some small period - @@ -68,24 +151,10 @@ class I2PServerSocketImpl implements I2PServerSocket { I2PSocket ret = null; while ( (ret == null) && (!closing) ){ - while (pendingSockets.size() <= 0) { - if (closing) throw new ConnectException("I2PServerSocket closed"); - try { - synchronized(socketAddedLock) { - socketAddedLock.wait(); - } - } catch (InterruptedException ie) {} - } - synchronized (pendingSockets) { - if (pendingSockets.size() > 0) { - ret = (I2PSocket)pendingSockets.remove(0); - } - } - if (ret != null) { - synchronized (socketAcceptedLock) { - socketAcceptedLock.notifyAll(); - } - } + + this.waitIncoming(-1); + + ret = accept(false); } if (_log.shouldLog(Log.DEBUG)) diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketImpl.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketImpl.java index dc7a48fd4..b37bf1085 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketImpl.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketImpl.java @@ -350,7 +350,6 @@ class I2PSocketImpl implements I2PSocket { read = bc.startToByteArray(len); bc.notifyAll(); } - boolean timedOut = false; while ( (read.length == 0) && (!inStreamClosed) ) { synchronized (flagLock) { diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java index 33477a4a8..2cf23f134 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java @@ -13,7 +13,6 @@ import net.i2p.client.I2PClient; import net.i2p.client.I2PClientFactory; import net.i2p.client.I2PSession; import net.i2p.client.I2PSessionException; -import net.i2p.data.Destination; import net.i2p.util.Log; /** diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerImpl.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerImpl.java index 406f71847..a272e99df 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerImpl.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerImpl.java @@ -43,12 +43,12 @@ class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener { private I2PSession _session; private I2PServerSocketImpl _serverSocket = null; private Object lock = new Object(); // for locking socket lists - private HashMap _outSockets; - private HashMap _inSockets; + private HashMap _outSockets; + private HashMap _inSockets; private I2PSocketOptions _defaultOptions; private long _acceptTimeout; private String _name; - private List _listeners; + private List _listeners; private static int __managerId = 0; public static final short ACK = 0x51; @@ -76,10 +76,10 @@ class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener { _name = name; _context = context; _log = _context.logManager().getLog(I2PSocketManager.class); - _inSockets = new HashMap(16); - _outSockets = new HashMap(16); + _inSockets = new HashMap(16); + _outSockets = new HashMap(16); _acceptTimeout = ACCEPT_TIMEOUT_DEFAULT; - _listeners = new ArrayList(1); + _listeners = new ArrayList(1); setSession(session); setDefaultOptions(buildOptions(opts)); _context.statManager().createRateStat("streaming.lifetime", "How long before the socket is closed?", "streaming", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 }); @@ -113,9 +113,9 @@ class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener { public void disconnected(I2PSession session) { _log.info(getName() + ": Disconnected from the session"); destroySocketManager(); - List listeners = null; + List listeners = null; synchronized (_listeners) { - listeners = new ArrayList(_listeners); + listeners = new ArrayList(_listeners); _listeners.clear(); } for (int i = 0; i < listeners.size(); i++) { @@ -130,7 +130,6 @@ class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener { public void messageAvailable(I2PSession session, int msgId, long size) { try { - I2PSocketImpl s; byte msg[] = session.receiveMessage(msgId); if (msg.length == 1 && msg[0] == -1) { _log.debug(getName() + ": Ping received"); @@ -660,7 +659,7 @@ class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener { * */ public Set listSockets() { - Set sockets = new HashSet(8); + Set sockets = new HashSet(8); synchronized (lock) { sockets.addAll(_inSockets.values()); sockets.addAll(_outSockets.values()); diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/TestSwarm.java b/apps/ministreaming/java/src/net/i2p/client/streaming/TestSwarm.java index 1c3ef54e9..a29b9f6f1 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/TestSwarm.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/TestSwarm.java @@ -28,6 +28,10 @@ public class TestSwarm { private String _conOptions; // unused? used elsewhere? private boolean _dead; // unused? used elsewhere? + public void antiCompilationWarnings() { + _log.debug(""+_conOptions+_dead); + } + public static void main(String args[]) { if (args.length < 1) { System.err.println("Usage: TestSwarm myDestFile [peerDestFile ]*"); @@ -131,6 +135,14 @@ public class TestSwarm { _context.statManager().createRateStat("swarm." + _connectionId + ".lifetime", "How long we talk to a peer", "swarm", new long[] { 5*60*1000 }); } + public void antiCompilationWarnings() { + _log.debug(""+this._lastReceived+this._lastReceivedOn+this._started); + } + public void antiCompilationWarnings(long x, long y) { + if (false) + _log.debug(""+x+y); + } + public Flooder(I2PSocket socket) { _socket = socket; _remoteDestination = socket.getPeerDestination(); @@ -154,6 +166,8 @@ public class TestSwarm { _context.random().nextBytes(data); long value = 0; long lastSend = _context.clock().now(); + this.antiCompilationWarnings(value, lastSend); + if (_socket == null) { try { _socket = _manager.connect(_remoteDestination); diff --git a/apps/sam/Demos/datagramTests/README.txt b/apps/sam/Demos/datagramTests/README.txt new file mode 100644 index 000000000..8e79434f9 --- /dev/null +++ b/apps/sam/Demos/datagramTests/README.txt @@ -0,0 +1,15 @@ +# test example + +#in a first terminal, launch : + ./samIn.py inTest + +#in a second terminal, launch : + ./samForward.py 25000 forward + +#in a third terminal, launch : +l=0 +while [ $l -lt 1000 ] +do + l=$((l+1)) + ./samOut.py forward this is message n. $l +done diff --git a/apps/sam/Demos/datagramTests/samForward.py b/apps/sam/Demos/datagramTests/samForward.py new file mode 100755 index 000000000..56590e7ef --- /dev/null +++ b/apps/sam/Demos/datagramTests/samForward.py @@ -0,0 +1,35 @@ +#!/usr/bin/python + +import socket +import sys + +# create a forward style SAM datagram session +# that forwards messages on specified port (default port : 25000) +# creates a standard datagram server that listens on this port forever +# usage : ./samForward.py [port [SAM session name]] + +if len(sys.argv)>=2 : + port = eval(sys.argv[1]) +else : + port = 25000 + +if len(sys.argv)==3 : + name = sys.argv[2] +else : + name = "essaiSamForward" + +sess = socket.socket( + socket.AF_INET, socket.SOCK_STREAM) +sess.connect(("127.0.0.1",7656)); +sess.send("HELLO VERSION MIN=3.0 MAX=3.0\n") +sys.stdout.write(sess.recv(1000)) +sess.send("SESSION CREATE STYLE=DATAGRAM PORT="+str(port)+" ID="+name+" DESTINATION=EYUpJFeW9tiubXR0aOjvCJ~ndj3xN0Wn-ljuGdbpOEttPg7nj0VCTOQDJ~FAolzn9FIDdmR3VjM0OFFDT46Q5HN4vShXFE2VNC8e3~GjzxJfaJhijRC2R9oIOzsNlzKtInD2o9lh0PxPioNMCigwmgWuqlQHs4tjWeaYRAtooHxbrtuoCIhIdGfyVV-nAcPiyYbouKq3leETXE~4kBXm-LfWfyPtrv6OuDk3GBVVcthv19GYBmnl2YI8HpJjc-G-TvNkgYishjzIJyEW-Xrpy43R4ZBXlyQqnheGLlbOEY8NLDbyNHLRMMOGbcr~67SVE3Iw3RqQ3Dhrkq2FCaQwcDucfIUCCbOfCZgu0hlnCkS42xsUvegQeiwMxbdI~h9v7vcR3yFFOrHX6WQvIZSbFLKNGArGJcfmOJVLqw1wTC4AgYXjk3csVDPd-QWbMXOuodyBgrg27Ds2BBYTsVXWskoo6ASsMIQZ6jMfL7PkY9dPLCRParIyzb9aPmf~MntNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHNqwgkhJnBW4ymaRsdVmITAha-ff0UiALfKSlznqp5HcSewgMHbzQ0I01TQytFnW\n") +sys.stdout.write(sess.recv(10000)) + +s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +s.bind(("", port)) +print "waiting on port:", port +while 1: + data, addr = s.recvfrom(40000) + print data, " received from ", addr, "length=", len(data) + diff --git a/apps/sam/Demos/datagramTests/samIn.py b/apps/sam/Demos/datagramTests/samIn.py new file mode 100755 index 000000000..c2c0589f3 --- /dev/null +++ b/apps/sam/Demos/datagramTests/samIn.py @@ -0,0 +1,29 @@ +#!/usr/bin/python + + +# create a SAM datagram session that writes incoming messages on its master session stream +# and a listen forever +# usage : ./samIn.py [session name] + +import socket +import sys + +if len(sys.argv)==2 : + name = sys.argv[1] +else : + name = "datagramSamIn" + + +sess = socket.socket( + socket.AF_INET, socket.SOCK_STREAM) +sess.connect(("127.0.0.1",7656)); +sess.send("HELLO VERSION MIN=3.0 MAX=3.0\n") +sys.stdout.write(sess.recv(1000)) +sess.send("SESSION CREATE STYLE=DATAGRAM ID="+name+" DESTINATION=tYhjbFlFL38WFuO5eCzTvE0UBr4RfaqWMKlekGeMoB-Ouz7nYaWfiS-9j3jMiZT7FH~pwdmoSREOs2ZbXK84sR59P~pPfeCMxnJrk57f3U9uKzXkesjkKWYco3YAGs-G8sw8Fu2FBx0Do57yBdA9~j8Zq6pMjmgPBXCLuXG3vo0Z8zUWCjApJyFY6OXYopHck9Fz9vKy7YhC6zXFHfEuNHVkAooduiLd~aCoGij0TW3lH2rTVU-lx-DUdi6edxQ5-RvDNkXfikvytoCpRkivbNVytjCJLk~7RNU4FpBD20wTZWNJmEG3OY3cjNjawJVFdNjtgczh9K7gZ7ad-NjVjZVhXEj1lU8mk~vAH-2QE5om8dstWUwWoNDwmVDlvIJNKzQmahG~VrpFexFHXO0n3fKIXcSgWGOHDExM8w9neCt7AxUjxPDtXXuYNW~bRwcfiL-C9~z4K9rmwiTPZX0lmsToSXTF28l7WAoj~TMT9kZAjQeFRRWU5oW5oxVuonVvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABngJSS8xMyF4t82otZmCDhrKjbm-QLMtOLoumwR28ebDHEd4clF6O7aRa3d3yRH7p\n") +sys.stdout.write(sess.recv(1000)) +while 1 : + chunk = sess.recv(10000) + sys.stdout.write(chunk+'\n') + if not chunk : break +print + diff --git a/apps/sam/Demos/datagramTests/samOut.py b/apps/sam/Demos/datagramTests/samOut.py new file mode 100755 index 000000000..bb9be4db2 --- /dev/null +++ b/apps/sam/Demos/datagramTests/samOut.py @@ -0,0 +1,31 @@ +#!/usr/bin/python + +# sends a message to datagram destinations opened by samForward.py and samIn.py, using specified sending session name +# at least samForward.py should be running for results to be seen +# usage : ./samOut.py [ sendingSessionName [ message ... ] ] +# sendingSessionName : default = datagramSamForward +# message : default = "this is nice message" + +import socket +import sys +import time + +if len(sys.argv)>=2 : + name = sys.argv[1] +else : + name = "datagramSamForward" + +if len(sys.argv)>2 : + message = ''.join([s+' ' for s in sys.argv[2:]]).strip() +else : + message = "This is a nice message" + + +# client.py +port = 7655 +host = "localhost" +s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +s.bind(("", 0)) +s.sendto(name+" tYhjbFlFL38WFuO5eCzTvE0UBr4RfaqWMKlekGeMoB-Ouz7nYaWfiS-9j3jMiZT7FH~pwdmoSREOs2ZbXK84sR59P~pPfeCMxnJrk57f3U9uKzXkesjkKWYco3YAGs-G8sw8Fu2FBx0Do57yBdA9~j8Zq6pMjmgPBXCLuXG3vo0Z8zUWCjApJyFY6OXYopHck9Fz9vKy7YhC6zXFHfEuNHVkAooduiLd~aCoGij0TW3lH2rTVU-lx-DUdi6edxQ5-RvDNkXfikvytoCpRkivbNVytjCJLk~7RNU4FpBD20wTZWNJmEG3OY3cjNjawJVFdNjtgczh9K7gZ7ad-NjVjZVhXEj1lU8mk~vAH-2QE5om8dstWUwWoNDwmVDlvIJNKzQmahG~VrpFexFHXO0n3fKIXcSgWGOHDExM8w9neCt7AxUjxPDtXXuYNW~bRwcfiL-C9~z4K9rmwiTPZX0lmsToSXTF28l7WAoj~TMT9kZAjQeFRRWU5oW5oxVuonVvAAAA\n"+message, (host, port)) +s.sendto(name+" EYUpJFeW9tiubXR0aOjvCJ~ndj3xN0Wn-ljuGdbpOEttPg7nj0VCTOQDJ~FAolzn9FIDdmR3VjM0OFFDT46Q5HN4vShXFE2VNC8e3~GjzxJfaJhijRC2R9oIOzsNlzKtInD2o9lh0PxPioNMCigwmgWuqlQHs4tjWeaYRAtooHxbrtuoCIhIdGfyVV-nAcPiyYbouKq3leETXE~4kBXm-LfWfyPtrv6OuDk3GBVVcthv19GYBmnl2YI8HpJjc-G-TvNkgYishjzIJyEW-Xrpy43R4ZBXlyQqnheGLlbOEY8NLDbyNHLRMMOGbcr~67SVE3Iw3RqQ3Dhrkq2FCaQwcDucfIUCCbOfCZgu0hlnCkS42xsUvegQeiwMxbdI~h9v7vcR3yFFOrHX6WQvIZSbFLKNGArGJcfmOJVLqw1wTC4AgYXjk3csVDPd-QWbMXOuodyBgrg27Ds2BBYTsVXWskoo6ASsMIQZ6jMfL7PkY9dPLCRParIyzb9aPmf~MntNAAAA\n"+message, (host, port)) + diff --git a/apps/sam/Demos/rawTests/README.txt b/apps/sam/Demos/rawTests/README.txt new file mode 100644 index 000000000..8e79434f9 --- /dev/null +++ b/apps/sam/Demos/rawTests/README.txt @@ -0,0 +1,15 @@ +# test example + +#in a first terminal, launch : + ./samIn.py inTest + +#in a second terminal, launch : + ./samForward.py 25000 forward + +#in a third terminal, launch : +l=0 +while [ $l -lt 1000 ] +do + l=$((l+1)) + ./samOut.py forward this is message n. $l +done diff --git a/apps/sam/Demos/rawTests/samForward.py b/apps/sam/Demos/rawTests/samForward.py new file mode 100755 index 000000000..6d55da5f7 --- /dev/null +++ b/apps/sam/Demos/rawTests/samForward.py @@ -0,0 +1,36 @@ +#!/usr/bin/python + +import socket +import sys + +# create a forward style SAM raw datagram session +# that forwards messages on specified port (default port : 25000) +# creates a standard datagram server that listens on this port forever +# usage : ./samForward.py [port [SAM session name]] + +if len(sys.argv)>=2 : + port = eval(sys.argv[1]) +else : + port = 25000 + +if len(sys.argv)==3 : + name = sys.argv[2] +else : + name = "essaiSamForward" + +sess = socket.socket( + socket.AF_INET, socket.SOCK_STREAM) +sess.connect(("127.0.0.1",7656)); +sess.send("HELLO VERSION MIN=3.0 MAX=3.0\n") +sys.stdout.write(sess.recv(1000)) +sess.send("SESSION CREATE STYLE=RAW PORT="+str(port)+" ID="+name+" DESTINATION=EYUpJFeW9tiubXR0aOjvCJ~ndj3xN0Wn-ljuGdbpOEttPg7nj0VCTOQDJ~FAolzn9FIDdmR3VjM0OFFDT46Q5HN4vShXFE2VNC8e3~GjzxJfaJhijRC2R9oIOzsNlzKtInD2o9lh0PxPioNMCigwmgWuqlQHs4tjWeaYRAtooHxbrtuoCIhIdGfyVV-nAcPiyYbouKq3leETXE~4kBXm-LfWfyPtrv6OuDk3GBVVcthv19GYBmnl2YI8HpJjc-G-TvNkgYishjzIJyEW-Xrpy43R4ZBXlyQqnheGLlbOEY8NLDbyNHLRMMOGbcr~67SVE3Iw3RqQ3Dhrkq2FCaQwcDucfIUCCbOfCZgu0hlnCkS42xsUvegQeiwMxbdI~h9v7vcR3yFFOrHX6WQvIZSbFLKNGArGJcfmOJVLqw1wTC4AgYXjk3csVDPd-QWbMXOuodyBgrg27Ds2BBYTsVXWskoo6ASsMIQZ6jMfL7PkY9dPLCRParIyzb9aPmf~MntNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHNqwgkhJnBW4ymaRsdVmITAha-ff0UiALfKSlznqp5HcSewgMHbzQ0I01TQytFnW\n") +sys.stdout.write(sess.recv(10000)) + +# listening server +s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +s.bind(("", port)) +print "waiting on port:", port +while 1: + data, addr = s.recvfrom(40000) + print data, " received from ", addr, "length=", len(data) + diff --git a/apps/sam/Demos/rawTests/samIn.py b/apps/sam/Demos/rawTests/samIn.py new file mode 100755 index 000000000..0f89deb17 --- /dev/null +++ b/apps/sam/Demos/rawTests/samIn.py @@ -0,0 +1,31 @@ +#!/usr/bin/python + + +# create a SAM datagram session that writes incoming messages on its master session stream +# and a listen forever +# usage : ./samIn.py [session name] + +import socket +import sys + +if len(sys.argv)==2 : + name = sys.argv[1] +else : + name = "datagramSamIn" + + +sess = socket.socket( + socket.AF_INET, socket.SOCK_STREAM) +sess.connect(("127.0.0.1",7656)); +sess.send("HELLO VERSION MIN=3.0 MAX=3.0\n") +sys.stdout.write(sess.recv(1000)) +sess.send("SESSION CREATE STYLE=RAW ID="+name+" DESTINATION=tYhjbFlFL38WFuO5eCzTvE0UBr4RfaqWMKlekGeMoB-Ouz7nYaWfiS-9j3jMiZT7FH~pwdmoSREOs2ZbXK84sR59P~pPfeCMxnJrk57f3U9uKzXkesjkKWYco3YAGs-G8sw8Fu2FBx0Do57yBdA9~j8Zq6pMjmgPBXCLuXG3vo0Z8zUWCjApJyFY6OXYopHck9Fz9vKy7YhC6zXFHfEuNHVkAooduiLd~aCoGij0TW3lH2rTVU-lx-DUdi6edxQ5-RvDNkXfikvytoCpRkivbNVytjCJLk~7RNU4FpBD20wTZWNJmEG3OY3cjNjawJVFdNjtgczh9K7gZ7ad-NjVjZVhXEj1lU8mk~vAH-2QE5om8dstWUwWoNDwmVDlvIJNKzQmahG~VrpFexFHXO0n3fKIXcSgWGOHDExM8w9neCt7AxUjxPDtXXuYNW~bRwcfiL-C9~z4K9rmwiTPZX0lmsToSXTF28l7WAoj~TMT9kZAjQeFRRWU5oW5oxVuonVvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABngJSS8xMyF4t82otZmCDhrKjbm-QLMtOLoumwR28ebDHEd4clF6O7aRa3d3yRH7p\n") +sys.stdout.write(sess.recv(1000)) + +# listen incoming messages +while 1 : + chunk = sess.recv(10000) + sys.stdout.write(chunk+'\n') + if not chunk : break +print + diff --git a/apps/sam/Demos/rawTests/samOut.py b/apps/sam/Demos/rawTests/samOut.py new file mode 100755 index 000000000..bb9be4db2 --- /dev/null +++ b/apps/sam/Demos/rawTests/samOut.py @@ -0,0 +1,31 @@ +#!/usr/bin/python + +# sends a message to datagram destinations opened by samForward.py and samIn.py, using specified sending session name +# at least samForward.py should be running for results to be seen +# usage : ./samOut.py [ sendingSessionName [ message ... ] ] +# sendingSessionName : default = datagramSamForward +# message : default = "this is nice message" + +import socket +import sys +import time + +if len(sys.argv)>=2 : + name = sys.argv[1] +else : + name = "datagramSamForward" + +if len(sys.argv)>2 : + message = ''.join([s+' ' for s in sys.argv[2:]]).strip() +else : + message = "This is a nice message" + + +# client.py +port = 7655 +host = "localhost" +s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +s.bind(("", 0)) +s.sendto(name+" tYhjbFlFL38WFuO5eCzTvE0UBr4RfaqWMKlekGeMoB-Ouz7nYaWfiS-9j3jMiZT7FH~pwdmoSREOs2ZbXK84sR59P~pPfeCMxnJrk57f3U9uKzXkesjkKWYco3YAGs-G8sw8Fu2FBx0Do57yBdA9~j8Zq6pMjmgPBXCLuXG3vo0Z8zUWCjApJyFY6OXYopHck9Fz9vKy7YhC6zXFHfEuNHVkAooduiLd~aCoGij0TW3lH2rTVU-lx-DUdi6edxQ5-RvDNkXfikvytoCpRkivbNVytjCJLk~7RNU4FpBD20wTZWNJmEG3OY3cjNjawJVFdNjtgczh9K7gZ7ad-NjVjZVhXEj1lU8mk~vAH-2QE5om8dstWUwWoNDwmVDlvIJNKzQmahG~VrpFexFHXO0n3fKIXcSgWGOHDExM8w9neCt7AxUjxPDtXXuYNW~bRwcfiL-C9~z4K9rmwiTPZX0lmsToSXTF28l7WAoj~TMT9kZAjQeFRRWU5oW5oxVuonVvAAAA\n"+message, (host, port)) +s.sendto(name+" EYUpJFeW9tiubXR0aOjvCJ~ndj3xN0Wn-ljuGdbpOEttPg7nj0VCTOQDJ~FAolzn9FIDdmR3VjM0OFFDT46Q5HN4vShXFE2VNC8e3~GjzxJfaJhijRC2R9oIOzsNlzKtInD2o9lh0PxPioNMCigwmgWuqlQHs4tjWeaYRAtooHxbrtuoCIhIdGfyVV-nAcPiyYbouKq3leETXE~4kBXm-LfWfyPtrv6OuDk3GBVVcthv19GYBmnl2YI8HpJjc-G-TvNkgYishjzIJyEW-Xrpy43R4ZBXlyQqnheGLlbOEY8NLDbyNHLRMMOGbcr~67SVE3Iw3RqQ3Dhrkq2FCaQwcDucfIUCCbOfCZgu0hlnCkS42xsUvegQeiwMxbdI~h9v7vcR3yFFOrHX6WQvIZSbFLKNGArGJcfmOJVLqw1wTC4AgYXjk3csVDPd-QWbMXOuodyBgrg27Ds2BBYTsVXWskoo6ASsMIQZ6jMfL7PkY9dPLCRParIyzb9aPmf~MntNAAAA\n"+message, (host, port)) + diff --git a/apps/sam/Demos/streamTests/README.txt b/apps/sam/Demos/streamTests/README.txt new file mode 100644 index 000000000..6ce7ae57b --- /dev/null +++ b/apps/sam/Demos/streamTests/README.txt @@ -0,0 +1,24 @@ +# test example + +#in a first terminal, launch : + ./samIn.py inTest + +#in a second terminal, launch : + ./samOut.py + +#and again + ./samOut.py + +########## + +# test example n°2 + +#in a first terminal, launch : + ./samForward.py inTest + +#in a second terminal, launch : + ./server.py + +#in a third terminal, launch : + ./samOut.py + diff --git a/apps/sam/Demos/streamTests/samForward.py b/apps/sam/Demos/streamTests/samForward.py new file mode 100755 index 000000000..c12753405 --- /dev/null +++ b/apps/sam/Demos/streamTests/samForward.py @@ -0,0 +1,58 @@ +#!/usr/bin/python + +import socket +import sys + +# create a master SAM stream session that opens a destination in I2P world +# then open another session that tells SAM to forward incoming connections +# to the specified address +# +# usage : +# ./samForward.py [ silent [ port [ sessionName [ host ] ] ] ] +# +# silent : should the first line of incoming socket contain the peer destination (true or false) +# port : port to which connections are forwarded (default : 25000) +# sessionName : session id (default : "forward") +# host : host to which connections are forwarded (default : this host) + +if len(sys.argv)>=2 : + silent = " SILENT="+sys.argv[1] +else : silent = " SILENT=false" + +if len(sys.argv)>=3 : + port = " PORT="+sys.argv[2] +else : port = " PORT=25000" + +if len(sys.argv)>=4 : + name = " ID="+sys.argv[3] +else : name = " ID=forward" + +if len(sys.argv)>=5 : + host = " HOST="+sys.argv[4] +else : host = "" + + + + +sess = socket.socket( + socket.AF_INET, socket.SOCK_STREAM) +sess.connect(("127.0.0.1",7656)); +sess.send("HELLO VERSION MIN=3.0 MAX=3.0\n") +sys.stdout.write(sess.recv(1000)) +sess.send("SESSION CREATE STYLE=STREAM"+name+" DESTINATION=tYhjbFlFL38WFuO5eCzTvE0UBr4RfaqWMKlekGeMoB-Ouz7nYaWfiS-9j3jMiZT7FH~pwdmoSREOs2ZbXK84sR59P~pPfeCMxnJrk57f3U9uKzXkesjkKWYco3YAGs-G8sw8Fu2FBx0Do57yBdA9~j8Zq6pMjmgPBXCLuXG3vo0Z8zUWCjApJyFY6OXYopHck9Fz9vKy7YhC6zXFHfEuNHVkAooduiLd~aCoGij0TW3lH2rTVU-lx-DUdi6edxQ5-RvDNkXfikvytoCpRkivbNVytjCJLk~7RNU4FpBD20wTZWNJmEG3OY3cjNjawJVFdNjtgczh9K7gZ7ad-NjVjZVhXEj1lU8mk~vAH-2QE5om8dstWUwWoNDwmVDlvIJNKzQmahG~VrpFexFHXO0n3fKIXcSgWGOHDExM8w9neCt7AxUjxPDtXXuYNW~bRwcfiL-C9~z4K9rmwiTPZX0lmsToSXTF28l7WAoj~TMT9kZAjQeFRRWU5oW5oxVuonVvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABngJSS8xMyF4t82otZmCDhrKjbm-QLMtOLoumwR28ebDHEd4clF6O7aRa3d3yRH7p\n") +sys.stdout.write(sess.recv(1000)) + +sock = socket.socket( + socket.AF_INET, socket.SOCK_STREAM) +sock.connect(("127.0.0.1",7656)); +sock.send("HELLO VERSION MIN=3.0 MAX=3.0\n") +sys.stdout.write(sock.recv(1000)) +sock.send("STREAM FORWARD" + name + host + port + silent + "\n") +sys.stdout.write(sock.recv(1000)) + +l=0 +while 1 : + chunk = sock.recv(100) + sys.stdout.write(chunk) + if not chunk : break + diff --git a/apps/sam/Demos/streamTests/samIn.py b/apps/sam/Demos/streamTests/samIn.py new file mode 100755 index 000000000..2edfd65c6 --- /dev/null +++ b/apps/sam/Demos/streamTests/samIn.py @@ -0,0 +1,89 @@ +#!/usr/bin/python + + +# create an stream session +# then an "accept" stream connected to this session +# then another "accept" stream from the same session +# then listen from the first stream and then listen from the second +# usage : ./samIn.py [ silent [ name ] ] +# name : the session id ( defaults to InTest ) +# silent : true or false : tells wether we want to receive the incoming stream destination +# as first line + +import socket +import sys +import time + +if len(sys.argv)>=2 : + silent = " SILENT="+sys.argv[1] +else : silent = " SILENT=false" + +if len(sys.argv)>=3 : + name = sys.argv[2] +else : name = "inTest" + + + + +sess = socket.socket( + socket.AF_INET, socket.SOCK_STREAM) +sess.connect(("127.0.0.1",7656)); +sess.send("HELLO VERSION MIN=3.0 MAX=3.0\n") +sys.stdout.write(sess.recv(1000)) +sess.send("SESSION CREATE STYLE=STREAM ID="+name+" DESTINATION=tYhjbFlFL38WFuO5eCzTvE0UBr4RfaqWMKlekGeMoB-Ouz7nYaWfiS-9j3jMiZT7FH~pwdmoSREOs2ZbXK84sR59P~pPfeCMxnJrk57f3U9uKzXkesjkKWYco3YAGs-G8sw8Fu2FBx0Do57yBdA9~j8Zq6pMjmgPBXCLuXG3vo0Z8zUWCjApJyFY6OXYopHck9Fz9vKy7YhC6zXFHfEuNHVkAooduiLd~aCoGij0TW3lH2rTVU-lx-DUdi6edxQ5-RvDNkXfikvytoCpRkivbNVytjCJLk~7RNU4FpBD20wTZWNJmEG3OY3cjNjawJVFdNjtgczh9K7gZ7ad-NjVjZVhXEj1lU8mk~vAH-2QE5om8dstWUwWoNDwmVDlvIJNKzQmahG~VrpFexFHXO0n3fKIXcSgWGOHDExM8w9neCt7AxUjxPDtXXuYNW~bRwcfiL-C9~z4K9rmwiTPZX0lmsToSXTF28l7WAoj~TMT9kZAjQeFRRWU5oW5oxVuonVvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABngJSS8xMyF4t82otZmCDhrKjbm-QLMtOLoumwR28ebDHEd4clF6O7aRa3d3yRH7p\n") +sys.stdout.write(sess.recv(1000)) + + + + + +def accept() : + sock = socket.socket( + socket.AF_INET, socket.SOCK_STREAM) + sock.connect(("127.0.0.1",7656)); + sock.send("HELLO VERSION MIN=3.0 MAX=3.0\n") + sys.stdout.write(sock.recv(1000)) + sock.send("STREAM ACCEPT ID=" + name + silent+"\n") + print "STREAM ACCEPT ID="+name+silent+"\n" + return sock + +def echo( sock, lines ) : + l = 0 + while lines==-1 or l=2 : + silent = " SILENT="+sys.argv[1] +else : silent = " SILENT=false" + +if len(sys.argv)>=3 : + name = " ID="+sys.argv[2] +else : name = " ID=testOutStream" + +sess = socket.socket( + socket.AF_INET, socket.SOCK_STREAM) +sess.connect(("127.0.0.1",7656)); +sess.send("HELLO VERSION MIN=3.0 MAX=3.0\n") +sys.stdout.write(sess.recv(1000)) +sess.send("SESSION CREATE STYLE=STREAM"+name+" DESTINATION=EYUpJFeW9tiubXR0aOjvCJ~ndj3xN0Wn-ljuGdbpOEttPg7nj0VCTOQDJ~FAolzn9FIDdmR3VjM0OFFDT46Q5HN4vShXFE2VNC8e3~GjzxJfaJhijRC2R9oIOzsNlzKtInD2o9lh0PxPioNMCigwmgWuqlQHs4tjWeaYRAtooHxbrtuoCIhIdGfyVV-nAcPiyYbouKq3leETXE~4kBXm-LfWfyPtrv6OuDk3GBVVcthv19GYBmnl2YI8HpJjc-G-TvNkgYishjzIJyEW-Xrpy43R4ZBXlyQqnheGLlbOEY8NLDbyNHLRMMOGbcr~67SVE3Iw3RqQ3Dhrkq2FCaQwcDucfIUCCbOfCZgu0hlnCkS42xsUvegQeiwMxbdI~h9v7vcR3yFFOrHX6WQvIZSbFLKNGArGJcfmOJVLqw1wTC4AgYXjk3csVDPd-QWbMXOuodyBgrg27Ds2BBYTsVXWskoo6ASsMIQZ6jMfL7PkY9dPLCRParIyzb9aPmf~MntNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHNqwgkhJnBW4ymaRsdVmITAha-ff0UiALfKSlznqp5HcSewgMHbzQ0I01TQytFnW\n") +sys.stdout.write(sess.recv(1000)) + +sock = socket.socket( + socket.AF_INET, socket.SOCK_STREAM) +sock.connect(("127.0.0.1",7656)); +sock.send("HELLO VERSION MIN=3.0 MAX=3.0\n") +sys.stdout.write(sock.recv(1000)) +sock.send("STREAM CONNECT"+name+" DESTINATION=tYhjbFlFL38WFuO5eCzTvE0UBr4RfaqWMKlekGeMoB-Ouz7nYaWfiS-9j3jMiZT7FH~pwdmoSREOs2ZbXK84sR59P~pPfeCMxnJrk57f3U9uKzXkesjkKWYco3YAGs-G8sw8Fu2FBx0Do57yBdA9~j8Zq6pMjmgPBXCLuXG3vo0Z8zUWCjApJyFY6OXYopHck9Fz9vKy7YhC6zXFHfEuNHVkAooduiLd~aCoGij0TW3lH2rTVU-lx-DUdi6edxQ5-RvDNkXfikvytoCpRkivbNVytjCJLk~7RNU4FpBD20wTZWNJmEG3OY3cjNjawJVFdNjtgczh9K7gZ7ad-NjVjZVhXEj1lU8mk~vAH-2QE5om8dstWUwWoNDwmVDlvIJNKzQmahG~VrpFexFHXO0n3fKIXcSgWGOHDExM8w9neCt7AxUjxPDtXXuYNW~bRwcfiL-C9~z4K9rmwiTPZX0lmsToSXTF28l7WAoj~TMT9kZAjQeFRRWU5oW5oxVuonVvAAAA"+silent+"\n") + +# wait for acknowledgement before sending data, if we asked for it +if (silent==" SILENT=false") : + sys.stdout.write(sock.recv(1000)) + +for i in range(1,11) : + sock.send(str(i)+'\n') + buf=sock.recv(1000) + sys.stdout.write(str(i)+' '+buf) + if not buf : break + +print + + diff --git a/apps/sam/Demos/streamTests/samOutWithNaming.py b/apps/sam/Demos/streamTests/samOutWithNaming.py new file mode 100755 index 000000000..6aa6476bc --- /dev/null +++ b/apps/sam/Demos/streamTests/samOutWithNaming.py @@ -0,0 +1,51 @@ +#!/usr/bin/python + + +# open a I2P stream destination +# then open another stream that connects to the destination created by samForward.py or samIn.py +# then send bytes through the stream +# usage : +# ./samOut.py [ silent [ sessionName ] ] +# +# silent : should the first incoming after the connection request contain the connection status message (true or false) +# sessionName : session id (default : "forward") + +import socket +import sys +import time + +if len(sys.argv)>=2 : + silent = " SILENT="+sys.argv[1] +else : silent = " SILENT=false" + +if len(sys.argv)>=3 : + name = " ID="+sys.argv[2] +else : name = " ID=testOutStream" + +sess = socket.socket( + socket.AF_INET, socket.SOCK_STREAM) +sess.connect(("127.0.0.1",7656)); +sess.send("HELLO VERSION MIN=3.0 MAX=3.0\n") +sys.stdout.write(sess.recv(1000)) +sess.send("SESSION CREATE STYLE=STREAM"+name+" DESTINATION=EYUpJFeW9tiubXR0aOjvCJ~ndj3xN0Wn-ljuGdbpOEttPg7nj0VCTOQDJ~FAolzn9FIDdmR3VjM0OFFDT46Q5HN4vShXFE2VNC8e3~GjzxJfaJhijRC2R9oIOzsNlzKtInD2o9lh0PxPioNMCigwmgWuqlQHs4tjWeaYRAtooHxbrtuoCIhIdGfyVV-nAcPiyYbouKq3leETXE~4kBXm-LfWfyPtrv6OuDk3GBVVcthv19GYBmnl2YI8HpJjc-G-TvNkgYishjzIJyEW-Xrpy43R4ZBXlyQqnheGLlbOEY8NLDbyNHLRMMOGbcr~67SVE3Iw3RqQ3Dhrkq2FCaQwcDucfIUCCbOfCZgu0hlnCkS42xsUvegQeiwMxbdI~h9v7vcR3yFFOrHX6WQvIZSbFLKNGArGJcfmOJVLqw1wTC4AgYXjk3csVDPd-QWbMXOuodyBgrg27Ds2BBYTsVXWskoo6ASsMIQZ6jMfL7PkY9dPLCRParIyzb9aPmf~MntNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHNqwgkhJnBW4ymaRsdVmITAha-ff0UiALfKSlznqp5HcSewgMHbzQ0I01TQytFnW\n") +sys.stdout.write(sess.recv(1000)) + +sock = socket.socket( + socket.AF_INET, socket.SOCK_STREAM) +sock.connect(("127.0.0.1",7656)); +sock.send("HELLO VERSION MIN=3.0 MAX=3.0\n") +sys.stdout.write(sock.recv(1000)) +sock.send("STREAM CONNECT"+name+" DESTINATION=http://amiga.i2p"+silent+"\n") + +# wait for acknowledgement before sending data, if we asked for it +if (silent==" SILENT=false") : + sys.stdout.write(sock.recv(1000)) + +while (1) : + buf=sock.recv(1000) + sys.stdout.write(buf) + if not buf : break + +print + + diff --git a/apps/sam/Demos/streamTests/server.py b/apps/sam/Demos/streamTests/server.py new file mode 100755 index 000000000..829a37e32 --- /dev/null +++ b/apps/sam/Demos/streamTests/server.py @@ -0,0 +1,41 @@ +#!/usr/bin/python + + +# echo server +# accepts a socket on specified port, writes on stdout and send back incoming data + +import socket +import sys + +if len(sys.argv)>=2 : + port = eval(sys.argv[1]) +else : port = 25000 + +#create an INET, STREAMing socket +serversocket = socket.socket( + socket.AF_INET, socket.SOCK_STREAM) +#bind the socket to a public host, +# and a well-known port +serversocket.bind(("0.0.0.0", port)) + #become a server socket +serversocket.listen(1) + + + #accept connections from outside +(clientsocket, address) = serversocket.accept() + #now do something with the clientsocket + #in this case, we'll pretend this is a threaded server + +i = 0 +while 1 : + chunk = clientsocket.recv(1024) + i = i + 1 + sys.stdout.write(str(i)+' '+chunk) + if not chunk: break + clientsocket.send(str(i)+' '+chunk) + + +clientsocket.close() + +print + diff --git a/apps/sam/doc/protocol-v3.txt b/apps/sam/doc/protocol-v3.txt new file mode 100644 index 000000000..9aacfa124 --- /dev/null +++ b/apps/sam/doc/protocol-v3.txt @@ -0,0 +1,17 @@ +telnet localhost 7656 +HELLO VERSION MIN=3.0 MAX=3.0 +SESSION CREATE STYLE=STREAM ID=essaiSamIn DESTINATION=tYhjbFlFL38WFuO5eCzTvE0UBr4RfaqWMKlekGeMoB-Ouz7nYaWfiS-9j3jMiZT7FH~pwdmoSREOs2ZbXK84sR59P~pPfeCMxnJrk57f3U9uKzXkesjkKWYco3YAGs-G8sw8Fu2FBx0Do57yBdA9~j8Zq6pMjmgPBXCLuXG3vo0Z8zUWCjApJyFY6OXYopHck9Fz9vKy7YhC6zXFHfEuNHVkAooduiLd~aCoGij0TW3lH2rTVU-lx-DUdi6edxQ5-RvDNkXfikvytoCpRkivbNVytjCJLk~7RNU4FpBD20wTZWNJmEG3OY3cjNjawJVFdNjtgczh9K7gZ7ad-NjVjZVhXEj1lU8mk~vAH-2QE5om8dstWUwWoNDwmVDlvIJNKzQmahG~VrpFexFHXO0n3fKIXcSgWGOHDExM8w9neCt7AxUjxPDtXXuYNW~bRwcfiL-C9~z4K9rmwiTPZX0lmsToSXTF28l7WAoj~TMT9kZAjQeFRRWU5oW5oxVuonVvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABngJSS8xMyF4t82otZmCDhrKjbm-QLMtOLoumwR28ebDHEd4clF6O7aRa3d3yRH7p + +telnet localhost 7656 +HELLO VERSION MIN=3.0 MAX=3.0 +STREAM ACCEPT ID=essaiSamIn + + +telnet localhost 7656 +HELLO VERSION MIN=3.0 MAX=3.0 +SESSION CREATE STYLE=STREAM ID=essaiSamOut DESTINATION=EYUpJFeW9tiubXR0aOjvCJ~ndj3xN0Wn-ljuGdbpOEttPg7nj0VCTOQDJ~FAolzn9FIDdmR3VjM0OFFDT46Q5HN4vShXFE2VNC8e3~GjzxJfaJhijRC2R9oIOzsNlzKtInD2o9lh0PxPioNMCigwmgWuqlQHs4tjWeaYRAtooHxbrtuoCIhIdGfyVV-nAcPiyYbouKq3leETXE~4kBXm-LfWfyPtrv6OuDk3GBVVcthv19GYBmnl2YI8HpJjc-G-TvNkgYishjzIJyEW-Xrpy43R4ZBXlyQqnheGLlbOEY8NLDbyNHLRMMOGbcr~67SVE3Iw3RqQ3Dhrkq2FCaQwcDucfIUCCbOfCZgu0hlnCkS42xsUvegQeiwMxbdI~h9v7vcR3yFFOrHX6WQvIZSbFLKNGArGJcfmOJVLqw1wTC4AgYXjk3csVDPd-QWbMXOuodyBgrg27Ds2BBYTsVXWskoo6ASsMIQZ6jMfL7PkY9dPLCRParIyzb9aPmf~MntNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHNqwgkhJnBW4ymaRsdVmITAha-ff0UiALfKSlznqp5HcSewgMHbzQ0I01TQytFnW + +telnet localhost 7656 +HELLO VERSION MIN=3.0 MAX=3.0 +STREAM CONNECT ID=essaiSamOut DESTINATION=tYhjbFlFL38WFuO5eCzTvE0UBr4RfaqWMKlekGeMoB-Ouz7nYaWfiS-9j3jMiZT7FH~pwdmoSREOs2ZbXK84sR59P~pPfeCMxnJrk57f3U9uKzXkesjkKWYco3YAGs-G8sw8Fu2FBx0Do57yBdA9~j8Zq6pMjmgPBXCLuXG3vo0Z8zUWCjApJyFY6OXYopHck9Fz9vKy7YhC6zXFHfEuNHVkAooduiLd~aCoGij0TW3lH2rTVU-lx-DUdi6edxQ5-RvDNkXfikvytoCpRkivbNVytjCJLk~7RNU4FpBD20wTZWNJmEG3OY3cjNjawJVFdNjtgczh9K7gZ7ad-NjVjZVhXEj1lU8mk~vAH-2QE5om8dstWUwWoNDwmVDlvIJNKzQmahG~VrpFexFHXO0n3fKIXcSgWGOHDExM8w9neCt7AxUjxPDtXXuYNW~bRwcfiL-C9~z4K9rmwiTPZX0lmsToSXTF28l7WAoj~TMT9kZAjQeFRRWU5oW5oxVuonVvAAAA + diff --git a/apps/sam/java/src/net/i2p/sam/SAMBridge.java b/apps/sam/java/src/net/i2p/sam/SAMBridge.java index ee7e33bb4..95ece882f 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMBridge.java +++ b/apps/sam/java/src/net/i2p/sam/SAMBridge.java @@ -14,9 +14,10 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.Socket; +import java.net.InetSocketAddress; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -34,7 +35,7 @@ import net.i2p.util.Log; */ public class SAMBridge implements Runnable { private final static Log _log = new Log(SAMBridge.class); - private ServerSocket serverSocket; + private ServerSocketChannel serverSocket; private Properties i2cpProps; /** * filename in which the name to private key mapping should @@ -45,12 +46,17 @@ public class SAMBridge implements Runnable { * app designated destination name to the base64 of the I2P formatted * destination keys (Destination+PrivateKey+SigningPrivateKey) */ - private Map nameToPrivKeys; + private Map nameToPrivKeys; private boolean acceptConnections = true; private static final int SAM_LISTENPORT = 7656; public static final String DEFAULT_SAM_KEYFILE = "sam.keys"; + public static final String PROP_DATAGRAM_HOST = "sam.datagram.host"; + public static final String PROP_DATAGRAM_PORT = "sam.datagram.port"; + public static final String DEFAULT_DATAGRAM_HOST = "0.0.0.0"; + public static final String DEFAULT_DATAGRAM_PORT = "7655"; + private SAMBridge() {} @@ -64,16 +70,18 @@ public class SAMBridge implements Runnable { */ public SAMBridge(String listenHost, int listenPort, Properties i2cpProps, String persistFile) { persistFilename = persistFile; - nameToPrivKeys = new HashMap(8); + nameToPrivKeys = new HashMap(8); loadKeys(); try { if ( (listenHost != null) && !("0.0.0.0".equals(listenHost)) ) { - serverSocket = new ServerSocket(listenPort, 0, InetAddress.getByName(listenHost)); + serverSocket = ServerSocketChannel.open(); + serverSocket.socket().bind(new InetSocketAddress(listenHost, listenPort)); if (_log.shouldLog(Log.DEBUG)) _log.debug("SAM bridge listening on " + listenHost + ":" + listenPort); } else { - serverSocket = new ServerSocket(listenPort); + serverSocket = ServerSocketChannel.open(); + serverSocket.socket().bind(new InetSocketAddress(listenPort)); if (_log.shouldLog(Log.DEBUG)) _log.debug("SAM bridge listening on 0.0.0.0:" + listenPort); } @@ -193,12 +201,12 @@ public class SAMBridge implements Runnable { /** * Usage: - *
    SAMBridge [[listenHost ]listenPort[ name=val]*]
    + *
    SAMBridge [ keyfile [listenHost ] listenPort [ name=val ]* ]
    * * name=val options are passed to the I2CP code to build a session, * allowing the bridge to specify an alternate I2CP host and port, tunnel * depth, etc. - * @param args [[listenHost ]listenPort[ name=val]*] + * @param args [ keyfile [ listenHost ] listenPort [ name=val ]* ] */ public static void main(String args[]) { String keyfile = DEFAULT_SAM_KEYFILE; @@ -266,11 +274,11 @@ public class SAMBridge implements Runnable { if (serverSocket == null) return; try { while (acceptConnections) { - Socket s = serverSocket.accept(); + SocketChannel s = serverSocket.accept(); if (_log.shouldLog(Log.DEBUG)) _log.debug("New connection from " - + s.getInetAddress().toString() + ":" - + s.getPort()); + + s.socket().getInetAddress().toString() + ":" + + s.socket().getPort()); try { SAMHandler handler = SAMHandlerFactory.createSAMHandler(s, i2cpProps); @@ -289,7 +297,7 @@ public class SAMBridge implements Runnable { _log.error("SAM error: " + e.getMessage(), e); try { String reply = "HELLO REPLY RESULT=I2P_ERROR MESSAGE=\"" + e.getMessage() + "\"\n"; - s.getOutputStream().write(reply.getBytes("ISO-8859-1")); + s.write(ByteBuffer.wrap(reply.getBytes("ISO-8859-1"))); } catch (IOException ioe) { if (_log.shouldLog(Log.ERROR)) _log.error("SAM Error sending error reply", ioe); diff --git a/apps/sam/java/src/net/i2p/sam/SAMDatagramSession.java b/apps/sam/java/src/net/i2p/sam/SAMDatagramSession.java index a3e20f7df..c8d31b489 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMDatagramSession.java +++ b/apps/sam/java/src/net/i2p/sam/SAMDatagramSession.java @@ -30,7 +30,7 @@ public class SAMDatagramSession extends SAMMessageSession { private final static Log _log = new Log(SAMDatagramSession.class); public static int DGRAM_SIZE_MAX = 31*1024; - private SAMDatagramReceiver recv = null; + protected SAMDatagramReceiver recv = null; private I2PDatagramMaker dgramMaker; private I2PDatagramDissector dgramDissector = new I2PDatagramDissector(); @@ -84,9 +84,10 @@ public class SAMDatagramSession extends SAMMessageSession { public boolean sendBytes(String dest, byte[] data) throws DataFormatException { if (data.length > DGRAM_SIZE_MAX) throw new DataFormatException("Datagram size exceeded (" + data.length + ")"); - - byte[] dgram = dgramMaker.makeI2PDatagram(data); - + byte[] dgram ; + synchronized (dgramMaker) { + dgram = dgramMaker.makeI2PDatagram(data); + } return sendBytesThroughMessageSession(dest, dgram); } diff --git a/apps/sam/java/src/net/i2p/sam/SAMException.java b/apps/sam/java/src/net/i2p/sam/SAMException.java index e51e35ea4..ae965a4c8 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMException.java +++ b/apps/sam/java/src/net/i2p/sam/SAMException.java @@ -15,11 +15,13 @@ package net.i2p.sam; */ public class SAMException extends Exception { + static final long serialVersionUID = 1 ; + public SAMException() { - super(); + super(); } public SAMException(String s) { - super(s); + super(s); } } diff --git a/apps/sam/java/src/net/i2p/sam/SAMHandler.java b/apps/sam/java/src/net/i2p/sam/SAMHandler.java index 64d824a57..d53a5a662 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMHandler.java +++ b/apps/sam/java/src/net/i2p/sam/SAMHandler.java @@ -9,9 +9,8 @@ package net.i2p.sam; */ import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; +import java.nio.channels.SocketChannel; +import java.nio.ByteBuffer; import java.util.Properties; import net.i2p.util.I2PAppThread; @@ -32,8 +31,7 @@ public abstract class SAMHandler implements Runnable { protected SAMBridge bridge = null; private Object socketWLock = new Object(); // Guards writings on socket - private Socket socket = null; - private OutputStream socketOS = null; // Stream associated to socket + protected SocketChannel socket = null; protected int verMajor = 0; protected int verMinor = 0; @@ -53,10 +51,9 @@ public abstract class SAMHandler implements Runnable { * @param i2cpProps properties to configure the I2CP connection (host, port, etc) * @throws IOException */ - protected SAMHandler(Socket s, + protected SAMHandler(SocketChannel s, int verMajor, int verMinor, Properties i2cpProps) throws IOException { socket = s; - socketOS = socket.getOutputStream(); this.verMajor = verMajor; this.verMinor = verMinor; @@ -86,8 +83,8 @@ public abstract class SAMHandler implements Runnable { * @return input stream * @throws IOException */ - protected final InputStream getClientSocketInputStream() throws IOException { - return socket.getInputStream(); + protected final SocketChannel getClientSocket() { + return socket ; } /** @@ -98,13 +95,17 @@ public abstract class SAMHandler implements Runnable { * @param data A byte array to be written * @throws IOException */ - protected final void writeBytes(byte[] data) throws IOException { + protected final void writeBytes(ByteBuffer data) throws IOException { synchronized (socketWLock) { - socketOS.write(data); - socketOS.flush(); + writeBytes(data, socket); } } + static public void writeBytes(ByteBuffer data, SocketChannel out) throws IOException { + while (data.hasRemaining()) out.write(data); + out.socket().getOutputStream().flush(); + } + /** * If you're crazy enough to write to the raw socket, grab the write lock * with getWriteLock(), synchronize against it, and write to the getOut() @@ -112,7 +113,6 @@ public abstract class SAMHandler implements Runnable { * @return socket Write lock object */ protected Object getWriteLock() { return socketWLock; } - protected OutputStream getOut() { return socketOS; } /** * Write a string to the handler's socket. This method must @@ -121,21 +121,25 @@ public abstract class SAMHandler implements Runnable { * * @param str A byte array to be written * - * @return True is the string was successfully written, false otherwise + * @return True if the string was successfully written, false otherwise */ protected final boolean writeString(String str) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Sending the client: [" + str + "]"); - try { - writeBytes(str.getBytes("ISO-8859-1")); + return writeString(str, socket); + } + + public static boolean writeString(String str, SocketChannel out) + { + try { + writeBytes(ByteBuffer.wrap(str.getBytes("ISO-8859-1")), out); } catch (IOException e) { _log.debug("Caught IOException", e); return false; } - - return true; + return true ; } - + /** * Close the socket connected to the SAM client. * @@ -178,8 +182,8 @@ public abstract class SAMHandler implements Runnable { return ("SAM handler (class: " + this.getClass().getName() + "; SAM version: " + verMajor + "." + verMinor + "; client: " - + this.socket.getInetAddress().toString() + ":" - + this.socket.getPort() + ")"); + + this.socket.socket().getInetAddress().toString() + ":" + + this.socket.socket().getPort() + ")"); } public final void run() { diff --git a/apps/sam/java/src/net/i2p/sam/SAMHandlerFactory.java b/apps/sam/java/src/net/i2p/sam/SAMHandlerFactory.java index 21a0e97d2..b5c3d198c 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMHandlerFactory.java +++ b/apps/sam/java/src/net/i2p/sam/SAMHandlerFactory.java @@ -9,9 +9,9 @@ package net.i2p.sam; */ import java.io.IOException; -import java.io.OutputStream; import java.io.UnsupportedEncodingException; -import java.net.Socket; +import java.nio.channels.SocketChannel; +import java.nio.ByteBuffer; import java.util.Properties; import java.util.StringTokenizer; @@ -34,17 +34,17 @@ public class SAMHandlerFactory { * @throws SAMException if the connection handshake (HELLO message) was malformed * @return A SAM protocol handler, or null if the client closed before the handshake */ - public static SAMHandler createSAMHandler(Socket s, Properties i2cpProps) throws SAMException { + public static SAMHandler createSAMHandler(SocketChannel s, Properties i2cpProps) throws SAMException { String line; StringTokenizer tok; try { - line = DataHelper.readLine(s.getInputStream()); + line = DataHelper.readLine(s.socket().getInputStream()); if (line == null) { _log.debug("Connection closed by client"); return null; } - tok = new StringTokenizer(line, " "); + tok = new StringTokenizer(line.trim(), " "); } catch (IOException e) { throw new SAMException("Error reading from socket: " + e.getMessage()); @@ -89,9 +89,8 @@ public class SAMHandlerFactory { // Let's answer positively try { - OutputStream out = s.getOutputStream(); - out.write(("HELLO REPLY RESULT=OK VERSION=" - + ver + "\n").getBytes("ISO-8859-1")); + s.write(ByteBuffer.wrap(("HELLO REPLY RESULT=OK VERSION=" + + ver + "\n").getBytes("ISO-8859-1"))); } catch (UnsupportedEncodingException e) { _log.error("Caught UnsupportedEncodingException (" + e.getMessage() + ")"); @@ -115,6 +114,9 @@ public class SAMHandlerFactory { case 2: handler = new SAMv2Handler(s, verMajor, verMinor, i2cpProps); break; + case 3: + handler = new SAMv3Handler(s, verMajor, verMinor, i2cpProps); + break; default: _log.error("BUG! Trying to initialize the wrong SAM version!"); throw new SAMException("BUG! (in handler instantiation)"); @@ -128,6 +130,7 @@ public class SAMHandlerFactory { /* Return the best version we can use, or null on failure */ private static String chooseBestVersion(String minVer, String maxVer) { + int minMajor = getMajor(minVer), minMinor = getMinor(minVer); int maxMajor = getMajor(maxVer), maxMinor = getMinor(maxVer); @@ -143,6 +146,8 @@ public class SAMHandlerFactory { float fmaxVer = (float) maxMajor + (float) maxMinor / 10 ; + if ( ( fminVer <= 3.0 ) && ( fmaxVer >= 3.0 ) ) return "3.0" ; + if ( ( fminVer <= 2.0 ) && ( fmaxVer >= 2.0 ) ) return "2.0" ; if ( ( fminVer <= 1.0 ) && ( fmaxVer >= 1.0 ) ) return "1.0" ; diff --git a/apps/sam/java/src/net/i2p/sam/SAMInvalidDirectionException.java b/apps/sam/java/src/net/i2p/sam/SAMInvalidDirectionException.java index b52ecda65..cd1c6b1a5 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMInvalidDirectionException.java +++ b/apps/sam/java/src/net/i2p/sam/SAMInvalidDirectionException.java @@ -15,7 +15,8 @@ package net.i2p.sam; * @author human */ public class SAMInvalidDirectionException extends Exception { - + static final long serialVersionUID = 1 ; + public SAMInvalidDirectionException() { super(); } diff --git a/apps/sam/java/src/net/i2p/sam/SAMMessageSession.java b/apps/sam/java/src/net/i2p/sam/SAMMessageSession.java index b29b2f84c..2c8ed2756 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMMessageSession.java +++ b/apps/sam/java/src/net/i2p/sam/SAMMessageSession.java @@ -109,8 +109,7 @@ public abstract class SAMMessageSession { * @throws DataFormatException */ protected boolean sendBytesThroughMessageSession(String dest, byte[] data) throws DataFormatException { - Destination d = new Destination(); - d.fromBase64(dest); + Destination d = SAMUtils.getDest(dest); if (_log.shouldLog(Log.DEBUG)) { _log.debug("Sending " + data.length + " bytes to " + dest); diff --git a/apps/sam/java/src/net/i2p/sam/SAMRawSession.java b/apps/sam/java/src/net/i2p/sam/SAMRawSession.java index 7f56066b1..92bf4960d 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMRawSession.java +++ b/apps/sam/java/src/net/i2p/sam/SAMRawSession.java @@ -26,7 +26,7 @@ public class SAMRawSession extends SAMMessageSession { private final static Log _log = new Log(SAMRawSession.class); public static final int RAW_SIZE_MAX = 32*1024; - private SAMRawReceiver recv = null; + protected SAMRawReceiver recv = null; /** * Create a new SAM RAW session. * diff --git a/apps/sam/java/src/net/i2p/sam/SAMStreamReceiver.java b/apps/sam/java/src/net/i2p/sam/SAMStreamReceiver.java index 6d6d824b5..326c81020 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMStreamReceiver.java +++ b/apps/sam/java/src/net/i2p/sam/SAMStreamReceiver.java @@ -9,6 +9,7 @@ package net.i2p.sam; */ import java.io.IOException; +import java.nio.ByteBuffer; import net.i2p.data.Destination; @@ -60,7 +61,7 @@ public interface SAMStreamReceiver { * @param len Number of bytes in data * @throws IOException */ - public void receiveStreamBytes(int id, byte data[], int len) throws IOException; + public void receiveStreamBytes(int id, ByteBuffer data) throws IOException; /** * Notify that a connection has been closed diff --git a/apps/sam/java/src/net/i2p/sam/SAMStreamSession.java b/apps/sam/java/src/net/i2p/sam/SAMStreamSession.java index 280562e48..aef2802bd 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMStreamSession.java +++ b/apps/sam/java/src/net/i2p/sam/SAMStreamSession.java @@ -13,6 +13,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; import java.net.ConnectException; import java.net.NoRouteToHostException; import java.util.ArrayList; @@ -51,15 +53,15 @@ public class SAMStreamSession { protected SAMStreamReceiver recv = null; - private SAMStreamSessionServer server = null; + protected SAMStreamSessionServer server = null; protected I2PSocketManager socketMgr = null; private Object handlersMapLock = new Object(); /** stream id (Long) to SAMStreamSessionSocketReader */ - private HashMap handlersMap = new HashMap(); + private HashMap handlersMap = new HashMap(); /** stream id (Long) to StreamSender */ - private HashMap sendersMap = new HashMap(); + private HashMap sendersMap = new HashMap(); private Object idLock = new Object(); private int lastNegativeId = 0; @@ -76,6 +78,10 @@ public class SAMStreamSession { public static String PROP_FORCE_FLUSH = "sam.forceFlush"; public static String DEFAULT_FORCE_FLUSH = "false"; + public SAMStreamSession() { + + } + /** * Create a new SAM STREAM session. * @@ -166,7 +172,7 @@ public class SAMStreamSession { } } - private class DisconnectListener implements I2PSocketManager.DisconnectListener { + protected class DisconnectListener implements I2PSocketManager.DisconnectListener { public void sessionDisconnected() { close(); } @@ -572,19 +578,20 @@ public class SAMStreamSession { _log.debug("run() called for socket reader " + id); int read = -1; - byte[] data = new byte[SOCKET_HANDLER_BUF_SIZE]; + ByteBuffer data = ByteBuffer.allocateDirect(SOCKET_HANDLER_BUF_SIZE); try { InputStream in = i2pSocket.getInputStream(); while (stillRunning) { - read = in.read(data); + data.clear(); + read = Channels.newChannel(in).read(data); if (read == -1) { _log.debug("Handler " + id + ": connection closed"); break; } - - recv.receiveStreamBytes(id, data, read); + data.flip(); + recv.receiveStreamBytes(id, data); } } catch (IOException e) { _log.debug("Caught IOException", e); @@ -650,7 +657,7 @@ public class SAMStreamSession { protected class v1StreamSender extends StreamSender { - private List _data; + private List _data; private int _id; private ByteCache _cache; private OutputStream _out = null; @@ -660,7 +667,7 @@ public class SAMStreamSession { public v1StreamSender ( I2PSocket s, int id ) throws IOException { super ( s, id ); - _data = new ArrayList(1); + _data = new ArrayList(1); _id = id; _cache = ByteCache.getInstance(4, 32*1024); _out = s.getOutputStream(); diff --git a/apps/sam/java/src/net/i2p/sam/SAMUtils.java b/apps/sam/java/src/net/i2p/sam/SAMUtils.java index 8bb3fac30..6a6d81f88 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMUtils.java +++ b/apps/sam/java/src/net/i2p/sam/SAMUtils.java @@ -101,6 +101,24 @@ public class SAMUtils { return dest; } + /** + * Resolve the destination from a key or a hostname + * + * @param s Hostname or key to be resolved + * + * @return the Destination for the specified hostname, or null if not found + */ + public static Destination getDest(String s) + { + Destination d = new Destination() ; + try { + d.fromBase64(s); + return d ; + } catch (DataFormatException e) { + return lookupHost(s, null); + } + } + /** * Parse SAM parameters, and put them into a Propetries object * diff --git a/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java b/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java index 93a9a8d66..ac86d6934 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java +++ b/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java @@ -12,12 +12,11 @@ import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.EOFException; import java.io.IOException; -import java.io.InputStream; import java.io.InterruptedIOException; -import java.io.OutputStream; import java.net.ConnectException; import java.net.NoRouteToHostException; -import java.net.Socket; +import java.nio.channels.SocketChannel; +import java.nio.ByteBuffer; import java.util.Properties; import java.util.StringTokenizer; @@ -40,14 +39,14 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag private final static Log _log = new Log(SAMv1Handler.class); - private final static int IN_BUFSIZE = 2048; + protected SAMRawSession rawSession = null; + protected SAMDatagramSession datagramSession = null; + protected SAMStreamSession streamSession = null; + protected SAMDatagramSession getDatagramSession() {return datagramSession ;} + protected SAMRawSession getRawSession() {return rawSession ;} - private SAMRawSession rawSession = null; - private SAMDatagramSession datagramSession = null; - protected SAMStreamSession streamSession = null; - - private long _id; - private static volatile long __id = 0; + protected long _id; + protected static volatile long __id = 0; /** * Create a new SAM version 1 handler. This constructor expects @@ -60,7 +59,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag * @throws SAMException * @throws IOException */ - public SAMv1Handler(Socket s, int verMajor, int verMinor) throws SAMException, IOException { + public SAMv1Handler(SocketChannel s, int verMajor, int verMinor) throws SAMException, IOException { this(s, verMajor, verMinor, new Properties()); } /** @@ -75,7 +74,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag * @throws SAMException * @throws IOException */ - public SAMv1Handler(Socket s, int verMajor, int verMinor, Properties i2cpProps) throws SAMException, IOException { + public SAMv1Handler(SocketChannel s, int verMajor, int verMinor, Properties i2cpProps) throws SAMException, IOException { super(s, verMajor, verMinor, i2cpProps); _id = ++__id; _log.debug("SAM version 1 handler instantiated"); @@ -101,16 +100,13 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag _log.debug("SAM handling started"); try { - InputStream in = getClientSocketInputStream(); - int b = -1; - while (true) { if (shouldStop()) { _log.debug("Stop request found"); break; } - msg = DataHelper.readLine(in); + msg = DataHelper.readLine(getClientSocket().socket().getInputStream()).trim(); if (msg == null) { _log.debug("Connection closed by client"); break; @@ -175,11 +171,11 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag } catch (IOException e) { _log.error("Error closing socket: " + e.getMessage()); } - if (rawSession != null) { - rawSession.close(); + if (getRawSession() != null) { + getRawSession().close(); } - if (datagramSession != null) { - datagramSession.close(); + if (getDatagramSession() != null) { + getDatagramSession().close(); } if (streamSession != null) { streamSession.close(); @@ -188,13 +184,13 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag } /* Parse and execute a SESSION message */ - private boolean execSessionMessage(String opcode, Properties props) { + protected boolean execSessionMessage(String opcode, Properties props) { String dest = "BUG!"; try{ if (opcode.equals("CREATE")) { - if ((rawSession != null) || (datagramSession != null) + if ((getRawSession() != null) || (getDatagramSession() != null) || (streamSession != null)) { _log.debug("Trying to create a session, but one still exists"); return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Session already exists\"\n"); @@ -293,7 +289,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag } /* Parse and execute a DEST message*/ - private boolean execDestMessage(String opcode, Properties props) { + protected boolean execDestMessage(String opcode, Properties props) { if (opcode.equals("GENERATE")) { if (props.size() > 0) { @@ -318,7 +314,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag } /* Parse and execute a NAMING message */ - private boolean execNamingMessage(String opcode, Properties props) { + protected boolean execNamingMessage(String opcode, Properties props) { if (opcode.equals("LOOKUP")) { if (props == null) { _log.debug("No parameters specified in NAMING LOOKUP message"); @@ -333,18 +329,18 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag Destination dest; if (name.equals("ME")) { - if (rawSession != null) { - dest = rawSession.getDestination(); + if (getRawSession() != null) { + dest = getRawSession().getDestination(); } else if (streamSession != null) { dest = streamSession.getDestination(); - } else if (datagramSession != null) { - dest = datagramSession.getDestination(); + } else if (getDatagramSession() != null) { + dest = getDatagramSession().getDestination(); } else { _log.debug("Lookup for SESSION destination, but session is null"); return false; } } else { - dest = SAMUtils.lookupHost(name, null); + dest = SAMUtils.getDest(name); } if (dest == null) { @@ -364,8 +360,8 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag /* Parse and execute a DATAGRAM message */ - private boolean execDatagramMessage(String opcode, Properties props) { - if (datagramSession == null) { + protected boolean execDatagramMessage(String opcode, Properties props) { + if (getDatagramSession() == null) { _log.error("DATAGRAM message received, but no DATAGRAM session exists"); return false; } @@ -403,7 +399,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag } try { - DataInputStream in = new DataInputStream(getClientSocketInputStream()); + DataInputStream in = new DataInputStream(getClientSocket().socket().getInputStream()); byte[] data = new byte[size]; in.readFully(data); @@ -435,8 +431,8 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag } /* Parse and execute a RAW message */ - private boolean execRawMessage(String opcode, Properties props) { - if (rawSession == null) { + protected boolean execRawMessage(String opcode, Properties props) { + if (getRawSession() == null) { _log.error("RAW message received, but no RAW session exists"); return false; } @@ -474,12 +470,12 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag } try { - DataInputStream in = new DataInputStream(getClientSocketInputStream()); + DataInputStream in = new DataInputStream(getClientSocket().socket().getInputStream()); byte[] data = new byte[size]; in.readFully(data); - if (!rawSession.sendBytes(dest, data)) { + if (!getRawSession().sendBytes(dest, data)) { _log.error("RAW SEND failed"); return true; } @@ -567,7 +563,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag } try { - if (!streamSession.sendBytes(id, getClientSocketInputStream(), size)) { // data)) { + if (!streamSession.sendBytes(id, getClientSocket().socket().getInputStream(), size)) { // data)) { if (_log.shouldLog(Log.WARN)) _log.warn("STREAM SEND [" + size + "] failed"); boolean rv = writeString("STREAM CLOSED RESULT=CANT_REACH_PEER ID=" + id + " MESSAGE=\"Send of " + size + " bytes failed\"\n"); @@ -691,7 +687,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag // SAMRawReceiver implementation public void receiveRawBytes(byte data[]) throws IOException { - if (rawSession == null) { + if (getRawSession() == null) { _log.error("BUG! Received raw bytes, but session is null!"); throw new NullPointerException("BUG! RAW session is null!"); } @@ -701,17 +697,18 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag String msgText = "RAW RECEIVED SIZE=" + data.length + "\n"; msg.write(msgText.getBytes("ISO-8859-1")); msg.write(data); + msg.flush(); if (_log.shouldLog(Log.DEBUG)) _log.debug("sending to client: " + msgText); - writeBytes(msg.toByteArray()); + writeBytes(ByteBuffer.wrap(msg.toByteArray())); } public void stopRawReceiving() { _log.debug("stopRawReceiving() invoked"); - if (rawSession == null) { + if (getRawSession() == null) { _log.error("BUG! Got raw receiving stop, but session is null!"); throw new NullPointerException("BUG! RAW session is null!"); } @@ -726,7 +723,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag // SAMDatagramReceiver implementation public void receiveDatagramBytes(Destination sender, byte data[]) throws IOException { - if (datagramSession == null) { + if (getDatagramSession() == null) { _log.error("BUG! Received datagram bytes, but session is null!"); throw new NullPointerException("BUG! DATAGRAM session is null!"); } @@ -740,14 +737,14 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag if (_log.shouldLog(Log.DEBUG)) _log.debug("sending to client: " + msgText); msg.write(data); - - writeBytes(msg.toByteArray()); + msg.flush(); + writeBytes(ByteBuffer.wrap(msg.toByteArray())); } public void stopDatagramReceiving() { _log.debug("stopDatagramReceiving() invoked"); - if (datagramSession == null) { + if (getDatagramSession() == null) { _log.error("BUG! Got datagram receiving stop, but session is null!"); throw new NullPointerException("BUG! DATAGRAM session is null!"); } @@ -830,29 +827,23 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag } } - public void receiveStreamBytes(int id, byte data[], int len) throws IOException { + public void receiveStreamBytes(int id, ByteBuffer data) throws IOException { if (streamSession == null) { _log.error("Received stream bytes, but session is null!"); throw new NullPointerException("BUG! STREAM session is null!"); } - String msgText = "STREAM RECEIVED ID=" + id +" SIZE=" + len + "\n"; + String msgText = "STREAM RECEIVED ID=" + id +" SIZE=" + data.remaining() + "\n"; if (_log.shouldLog(Log.DEBUG)) _log.debug("sending to client: " + msgText); - byte prefix[] = msgText.getBytes("ISO-8859-1"); + ByteBuffer prefix = ByteBuffer.wrap(msgText.getBytes("ISO-8859-1")); - // dont waste so much memory - //ByteArrayOutputStream msg = new ByteArrayOutputStream(); - //msg.write(msgText.getBytes("ISO-8859-1")); - //msg.write(data, 0, len); - // writeBytes(msg.toByteArray()); Object writeLock = getWriteLock(); - OutputStream out = getOut(); synchronized (writeLock) { - out.write(prefix); - out.write(data, 0, len); - out.flush(); + while (prefix.hasRemaining()) socket.write(prefix); + while (data.hasRemaining()) socket.write(data); + socket.socket().getOutputStream().flush(); } } diff --git a/apps/sam/java/src/net/i2p/sam/SAMv2Handler.java b/apps/sam/java/src/net/i2p/sam/SAMv2Handler.java index 75f1bd4b4..0800e17e9 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMv2Handler.java +++ b/apps/sam/java/src/net/i2p/sam/SAMv2Handler.java @@ -9,7 +9,7 @@ package net.i2p.sam; */ import java.io.IOException; -import java.net.Socket; +import java.nio.channels.SocketChannel; import java.util.Properties; import net.i2p.data.DataFormatException; @@ -36,7 +36,7 @@ public class SAMv2Handler extends SAMv1Handler implements SAMRawReceiver, SAMDat * @param verMajor SAM major version to manage (should be 2) * @param verMinor SAM minor version to manage */ - public SAMv2Handler ( Socket s, int verMajor, int verMinor ) throws SAMException, IOException + public SAMv2Handler ( SocketChannel s, int verMajor, int verMinor ) throws SAMException, IOException { this ( s, verMajor, verMinor, new Properties() ); } @@ -52,7 +52,7 @@ public class SAMv2Handler extends SAMv1Handler implements SAMRawReceiver, SAMDat * @param i2cpProps properties to configure the I2CP connection (host, port, etc) */ - public SAMv2Handler ( Socket s, int verMajor, int verMinor, Properties i2cpProps ) throws SAMException, IOException + public SAMv2Handler ( SocketChannel s, int verMajor, int verMinor, Properties i2cpProps ) throws SAMException, IOException { super ( s, verMajor, verMinor, i2cpProps ); } diff --git a/apps/sam/java/src/net/i2p/sam/SAMv2StreamSession.java b/apps/sam/java/src/net/i2p/sam/SAMv2StreamSession.java index de5b7851b..4197597eb 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMv2StreamSession.java +++ b/apps/sam/java/src/net/i2p/sam/SAMv2StreamSession.java @@ -12,6 +12,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.OutputStream; +import java.nio.channels.Channels; +import java.nio.ByteBuffer; import java.net.ConnectException; import java.net.NoRouteToHostException; import java.util.ArrayList; @@ -140,9 +142,6 @@ public class SAMv2StreamSession extends SAMStreamSession public class StreamConnector implements Runnable { - private Object runningLock = new Object(); - private boolean stillRunning = true; - private int id; private Destination dest ; private I2PSocketOptions opts ; @@ -245,7 +244,7 @@ public class SAMv2StreamSession extends SAMStreamSession protected class v2StreamSender extends StreamSender { - private List _data; + private List _data; private int _dataSize; private int _id; private ByteCache _cache; @@ -257,7 +256,7 @@ public class SAMv2StreamSession extends SAMStreamSession public v2StreamSender ( I2PSocket s, int id ) throws IOException { super ( s, id ); - _data = new ArrayList ( 1 ); + _data = new ArrayList ( 1 ); _dataSize = 0; _id = id; _cache = ByteCache.getInstance ( 10, 32 * 1024 ); @@ -511,7 +510,7 @@ public class SAMv2StreamSession extends SAMStreamSession _log.debug ( "run() called for socket reader " + id ); int read = -1; - byte[] data = new byte[SOCKET_HANDLER_BUF_SIZE]; + ByteBuffer data = ByteBuffer.allocateDirect(SOCKET_HANDLER_BUF_SIZE); try { @@ -533,7 +532,8 @@ public class SAMv2StreamSession extends SAMStreamSession break ; } - read = in.read ( data ); + data.clear(); + read = Channels.newChannel(in).read ( data ); if ( read == -1 ) { @@ -542,8 +542,8 @@ public class SAMv2StreamSession extends SAMStreamSession } totalReceived += read ; - - recv.receiveStreamBytes ( id, data, read ); + data.flip(); + recv.receiveStreamBytes ( id, data ); } } catch ( IOException e ) diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3DatagramSession.java b/apps/sam/java/src/net/i2p/sam/SAMv3DatagramSession.java new file mode 100644 index 000000000..dcca03c14 --- /dev/null +++ b/apps/sam/java/src/net/i2p/sam/SAMv3DatagramSession.java @@ -0,0 +1,90 @@ +/** + * @author MKVore + * + */ + +package net.i2p.sam; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.util.Properties; + +import net.i2p.client.I2PSessionException; +import net.i2p.data.DataFormatException; +import net.i2p.data.Destination; +import net.i2p.util.Log; + +import java.net.InetSocketAddress; +import java.net.SocketAddress ; +import java.nio.ByteBuffer; + +public class SAMv3DatagramSession extends SAMDatagramSession implements SAMv3Handler.Session, SAMDatagramReceiver { + + private final static Log _log = new Log ( SAMv3DatagramSession.class ); + + SAMv3Handler handler = null ; + SAMv3Handler.DatagramServer server = null ; + String nick = null ; + SocketAddress clientAddress = null ; + + public String getNick() { return nick; } + + /** + * @param nick nickname of the session + * @param server DatagramServer used for communication with the client + * @throws IOException + * @throws DataFormatException + * @throws I2PSessionException + */ + public SAMv3DatagramSession(String nick) + throws IOException, DataFormatException, I2PSessionException { + + super(SAMv3Handler.sSessionsHash.get(nick).getDest(), + SAMv3Handler.sSessionsHash.get(nick).getProps(), + null + ); + this.nick = nick ; + this.recv = this ; + this.server = SAMv3Handler.DatagramServer.getInstance() ; + + SAMv3Handler.SessionRecord rec = SAMv3Handler.sSessionsHash.get(nick); + if ( rec==null ) throw new InterruptedIOException() ; + + this.handler = rec.getHandler(); + + Properties props = rec.getProps(); + String portStr = props.getProperty("PORT") ; + if ( portStr==null ) { + _log.debug("receiver port not specified. Current socket will be used."); + } + else { + int port = Integer.parseInt(portStr); + + String host = props.getProperty("HOST"); + if ( host==null ) { + _log.debug("no host specified. Take from the client socket"); + + host = rec.getHandler().getClientIP(); + } + + + this.clientAddress = new InetSocketAddress(host,port); + } + } + + public void receiveDatagramBytes(Destination sender, byte[] data) throws IOException { + if (this.clientAddress==null) { + this.handler.receiveDatagramBytes(sender, data); + } else { + String msg = sender.toBase64()+"\n"; + ByteBuffer msgBuf = ByteBuffer.allocate(msg.length()+data.length); + msgBuf.put(msg.getBytes("ISO-8859-1")); + msgBuf.put(data); + msgBuf.flip(); + this.server.send(this.clientAddress, msgBuf); + } + } + + public void stopDatagramReceiving() { + } +} diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java b/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java new file mode 100644 index 000000000..21ff0df68 --- /dev/null +++ b/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java @@ -0,0 +1,748 @@ +package net.i2p.sam; +/* + * free (adj.): unencumbered; not under the control of others + * Written by human in 2004 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.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.net.ConnectException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.NoRouteToHostException; +import java.nio.channels.DatagramChannel; +import java.nio.channels.SocketChannel; +import java.nio.ByteBuffer; +import java.util.Properties; +import java.util.HashMap; +import java.util.StringTokenizer; + +import net.i2p.I2PException; +import net.i2p.client.I2PSessionException; +import net.i2p.data.Base64; +import net.i2p.data.DataFormatException; +import net.i2p.data.DataHelper; +import net.i2p.data.Destination; +import net.i2p.util.Log; +import net.i2p.data.VerifiedDestination; +import net.i2p.util.I2PAppThread; + +/** + * Class able to handle a SAM version 3 client connection. + * + * @author mkvore + */ + +public class SAMv3Handler extends SAMv1Handler +{ + private final static Log _log = new Log ( SAMv3Handler.class ); + + protected SAMv3StreamSession streamSession = null ; + protected SAMv3RawSession rawSession = null ; + protected SAMv3DatagramSession datagramSession = null ; + + protected SAMDatagramSession getDatagramSession() { + return datagramSession ; + } + + protected SAMRawSession getRawSession() { + return rawSession ; + } + + protected Session session = null ; + + interface Session { + String getNick(); + void close(); + boolean sendBytes(String dest, byte[] data) throws DataFormatException; + } + + /** + * Create a new SAM version 3 handler. This constructor expects + * that the SAM HELLO message has been still answered (and + * stripped) from the socket input stream. + * + * @param s Socket attached to a SAM client + * @param verMajor SAM major version to manage (should be 3) + * @param verMinor SAM minor version to manage + */ + public SAMv3Handler ( SocketChannel s, int verMajor, int verMinor ) throws SAMException, IOException + { + this ( s, verMajor, verMinor, new Properties() ); + } + + /** + * Create a new SAM version 3 handler. This constructor expects + * that the SAM HELLO message has been still answered (and + * stripped) from the socket input stream. + * + * @param s Socket attached to a SAM client + * @param verMajor SAM major version to manage (should be 3) + * @param verMinor SAM minor version to manage + * @param i2cpProps properties to configure the I2CP connection (host, port, etc) + */ + + public SAMv3Handler ( SocketChannel s, int verMajor, int verMinor, Properties i2cpProps ) throws SAMException, IOException + { + super ( s, verMajor, verMinor, i2cpProps ); + _log.debug("SAM version 3 handler instantiated"); + } + + public boolean verifVersion() + { + return (verMajor == 3 && verMinor == 0) ; + } + + static public class DatagramServer { + + private static DatagramServer _instance = null ; + private static DatagramChannel server = null ; + + public static DatagramServer getInstance() throws IOException { + return getInstance(new Properties()); + } + + public static DatagramServer getInstance(Properties props) throws IOException { + if (_instance==null) { + _instance = new DatagramServer(props); + } + return _instance ; + } + + public DatagramServer(Properties props) throws IOException { + if (server==null) { + server = DatagramChannel.open(); + } + + String host = props.getProperty(SAMBridge.PROP_DATAGRAM_HOST, SAMBridge.DEFAULT_DATAGRAM_HOST); + String portStr = props.getProperty(SAMBridge.PROP_DATAGRAM_PORT, SAMBridge.DEFAULT_DATAGRAM_PORT); + int port ; + try { + port = Integer.parseInt(portStr); + } catch (NumberFormatException e) { + port = Integer.parseInt(SAMBridge.DEFAULT_DATAGRAM_PORT); + } + + server.socket().bind(new InetSocketAddress(host, port)); + new I2PAppThread(new Listener(server), "DatagramListener").start(); + } + + public void send(SocketAddress addr, ByteBuffer msg) throws IOException { + server.send(msg, addr); + } + + class Listener implements Runnable { + + DatagramChannel server = null; + + public Listener(DatagramChannel server) + { + this.server = server ; + } + public void run() + { + ByteBuffer inBuf = ByteBuffer.allocateDirect(SAMRawSession.RAW_SIZE_MAX+1024); + + while (!Thread.interrupted()) + { + inBuf.clear(); + try { + server.receive(inBuf); + } catch (IOException e) { + break ; + } + inBuf.flip(); + ByteBuffer outBuf = ByteBuffer.wrap(new byte[inBuf.remaining()]); + outBuf.put(inBuf); + outBuf.flip(); + new I2PAppThread(new MessageDispatcher(outBuf.array()), "MessageDispatcher").start(); + } + } + } + } + + public static class MessageDispatcher implements Runnable + { + ByteArrayInputStream is = null ; + + public MessageDispatcher(byte[] buf) + { + this.is = new java.io.ByteArrayInputStream(buf) ; + } + + public void run() { + String header = null ; + String nick ; + String dest ; + + try { + header = DataHelper.readLine(is).trim(); + StringTokenizer tok = new StringTokenizer(header, " "); + if (tok.countTokens() != 2) { + // This is not a correct message, for sure + _log.debug("Error in message format"); + return; + } + nick = tok.nextToken(); + dest = tok.nextToken(); + + byte[] data = new byte[is.available()]; + is.read(data); + SessionRecord rec = sSessionsHash.get(nick); + if (rec!=null) { + rec.getHandler().session.sendBytes(dest,data); + } + } catch (Exception e) {} + } + } + + public class SessionRecord + { + protected String m_dest ; + protected Properties m_props ; + protected ThreadGroup m_threadgroup ; + protected SAMv3Handler m_handler ; + + public SessionRecord( String dest, Properties props, SAMv3Handler handler ) + { + m_dest = new String(dest) ; + m_props = new Properties() ; + m_props.putAll(props); + m_threadgroup = null ; + m_handler = handler ; + } + + public SessionRecord( SessionRecord in ) + { + m_dest = in.getDest(); + m_props = in.getProps(); + m_threadgroup = in.getThreadGroup(); + m_handler = in.getHandler(); + } + + synchronized public String getDest() + { + return new String(m_dest) ; + } + synchronized public Properties getProps() + { + Properties p = new Properties(); + p.putAll(m_props); + return m_props; + } + synchronized public SAMv3Handler getHandler() + { + return m_handler ; + } + synchronized public ThreadGroup getThreadGroup() + { + return m_threadgroup ; + } + synchronized public void createThreadGroup(String name) + { + if (m_threadgroup == null) + m_threadgroup = new ThreadGroup(name); + } + } + + public static class SessionsDB + { + static final long serialVersionUID = 0x1 ; + + HashMap map ; + + public SessionsDB() { + map = new HashMap() ; + } + + synchronized public boolean put( String nick, SessionRecord session ) + { + if ( !map.containsKey(nick) ) { + session.createThreadGroup("SAM session "+nick); + map.put(nick, session) ; + return true ; + } + else + return false ; + } + synchronized public boolean del( String nick ) + { + SessionRecord rec = map.get(nick); + + if ( rec!=null ) { + map.remove(nick); + return true ; + } + else + return false ; + } + synchronized public SessionRecord get(String nick) + { + return map.get(nick); + } + synchronized public boolean containsKey( String nick ) + { + return map.containsKey(nick); + } + } + + public static SessionsDB sSessionsHash = new SessionsDB() ; + + public String getClientIP() + { + return this.socket.socket().getInetAddress().getHostAddress(); + } + + boolean stolenSocket = false ; + + public void stealSocket() + { + stolenSocket = true ; + this.stopHandling(); + } + + public void handle() { + String msg = null; + String domain = null; + String opcode = null; + boolean canContinue = false; + StringTokenizer tok; + Properties props; + + this.thread.setName("SAMv3Handler " + _id); + _log.debug("SAM handling started"); + + try { + InputStream in = getClientSocket().socket().getInputStream(); + + while (true) { + if (shouldStop()) { + _log.debug("Stop request found"); + break; + } + + msg = DataHelper.readLine(in).trim(); + if (msg == null) { + _log.debug("Connection closed by client"); + break; + } + + if (_log.shouldLog(Log.DEBUG)) { + _log.debug("New message received: [" + msg + "]"); + } + + if(msg.equals("")) { + _log.debug("Ignoring newline"); + continue; + } + + tok = new StringTokenizer(msg, " "); + if (tok.countTokens() < 2) { + // This is not a correct message, for sure + _log.debug("Error in message format"); + break; + } + domain = tok.nextToken(); + opcode = tok.nextToken(); + if (_log.shouldLog(Log.DEBUG)) { + _log.debug("Parsing (domain: \"" + domain + + "\"; opcode: \"" + opcode + "\")"); + } + props = SAMUtils.parseParams(tok); + + if (domain.equals("STREAM")) { + canContinue = execStreamMessage(opcode, props); + } else if (domain.equals("SESSION")) { + if (i2cpProps != null) + props.putAll(i2cpProps); // make sure we've got the i2cp settings + canContinue = execSessionMessage(opcode, props); + } else if (domain.equals("DEST")) { + canContinue = execDestMessage(opcode, props); + } else if (domain.equals("NAMING")) { + canContinue = execNamingMessage(opcode, props); + } else { + _log.debug("Unrecognized message domain: \"" + + domain + "\""); + break; + } + + if (!canContinue) { + break; + } + } + } catch (IOException e) { + _log.debug("Caught IOException (" + + e.getMessage() + ") for message [" + msg + "]", e); + } catch (Exception e) { + _log.error("Unexpected exception for message [" + msg + "]", e); + } finally { + _log.debug("Stopping handler"); + + if (!this.stolenSocket) + { + try { + closeClientSocket(); + } catch (IOException e) { + _log.error("Error closing socket: " + e.getMessage()); + } + } + + die(); + } + } + + protected void die() { + SessionRecord rec = null ; + + if (session!=null) { + session.close(); + rec = sSessionsHash.get(session.getNick()); + } + if (rec!=null) { + rec.getThreadGroup().interrupt() ; + while (rec.getThreadGroup().activeCount()>0) + try { + Thread.sleep(1000); + } catch ( InterruptedException e) {} + rec.getThreadGroup().destroy(); + sSessionsHash.del(session.getNick()); + } + } + + /* Parse and execute a SESSION message */ + @Override + protected boolean execSessionMessage(String opcode, Properties props) { + + String dest = "BUG!"; + String nick = null ; + boolean ok = false ; + + try{ + if (opcode.equals("CREATE")) { + if ((this.getRawSession()!= null) || (this.getDatagramSession() != null) + || (streamSession != null)) { + _log.debug("Trying to create a session, but one still exists"); + return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Session already exists\"\n"); + } + if (props == null) { + _log.debug("No parameters specified in SESSION CREATE message"); + return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"No parameters for SESSION CREATE\"\n"); + } + + dest = props.getProperty("DESTINATION"); + if (dest == null) { + _log.debug("SESSION DESTINATION parameter not specified"); + return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"DESTINATION not specified\"\n"); + } + props.remove("DESTINATION"); + + + if (dest.equals("TRANSIENT")) { + _log.debug("TRANSIENT destination requested"); + ByteArrayOutputStream priv = new ByteArrayOutputStream(640); + SAMUtils.genRandomKey(priv, null); + + dest = Base64.encode(priv.toByteArray()); + } else { + _log.debug("Custom destination specified [" + dest + "]"); + } + + boolean good_key = false ; + try { + good_key = (new VerifiedDestination(dest)).verifyCert(true); + } catch (DataFormatException e) { + good_key = false ; + } + if (!good_key) + { + _log.debug("Bad destination key"); + return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"bad destination key\"\n"); + } + + nick = props.getProperty("ID"); + if (nick == null) { + _log.debug("SESSION ID parameter not specified"); + return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"ID not specified\"\n"); + } + props.remove("ID"); + + + String style = props.getProperty("STYLE"); + if (style == null) { + _log.debug("SESSION STYLE parameter not specified"); + return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"No SESSION STYLE specified\"\n"); + } + props.remove("STYLE"); + + + + // Record the session in the database sSessionsHash + Properties allProps = new Properties(); + allProps.putAll(i2cpProps); + allProps.putAll(props); + + if (! sSessionsHash.put( nick, new SessionRecord(dest, allProps, this) ) ) { + _log.debug("SESSION ID parameter already in use"); + String n = nick ; + return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"ID "+n+" already in use\"\n"); + } + + // Create the session + + if (style.equals("RAW")) { + DatagramServer.getInstance(i2cpProps); + rawSession = newSAMRawSession(nick); + this.session = rawSession ; + } else if (style.equals("DATAGRAM")) { + DatagramServer.getInstance(i2cpProps); + datagramSession = newSAMDatagramSession(nick); + this.session = datagramSession ; + } else if (style.equals("STREAM")) { + streamSession = newSAMStreamSession(nick); + this.session = streamSession ; + } else { + _log.debug("Unrecognized SESSION STYLE: \"" + style +"\""); + return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unrecognized SESSION STYLE\"\n"); + } + ok = true ; + return writeString("SESSION STATUS RESULT=OK DESTINATION=" + + dest + "\n"); + } else { + _log.debug("Unrecognized SESSION message opcode: \"" + + opcode + "\""); + return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unrecognized opcode\"\n"); + } + } catch (DataFormatException e) { + _log.debug("Invalid destination specified"); + return writeString("SESSION STATUS RESULT=INVALID_KEY DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n"); + } catch (I2PSessionException e) { + _log.debug("I2P error when instantiating session", e); + return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n"); + } catch (SAMException e) { + _log.error("Unexpected SAM error", e); + return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n"); + } catch (IOException e) { + _log.error("Unexpected IOException", e); + return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n"); + } finally { + // unregister the session if it has not been created + if ( !ok && nick!=null ) { + sSessionsHash.del(nick) ; + session = null ; + } + } + } + + SAMv3StreamSession newSAMStreamSession(String login ) + throws IOException, DataFormatException, SAMException + { + return new SAMv3StreamSession( login ) ; + } + + SAMv3RawSession newSAMRawSession(String login ) + throws IOException, DataFormatException, SAMException, I2PSessionException + { + return new SAMv3RawSession( login ) ; + } + + SAMv3DatagramSession newSAMDatagramSession(String login ) + throws IOException, DataFormatException, SAMException, I2PSessionException + { + return new SAMv3DatagramSession( login ) ; + } + + /* Parse and execute a STREAM message */ + protected boolean execStreamMessage ( String opcode, Properties props ) + { + String nick = null ; + SessionRecord rec = null ; + + if ( session != null ) + { + _log.error ( "STREAM message received, but this session is a master session" ); + writeString("STREAM STATUS RESULT=I2P_ERROR MESSAGE=\"master session cannot be used for streams"); + return false; + } + + nick = props.getProperty("ID"); + if (nick == null) { + _log.debug("SESSION ID parameter not specified"); + writeString("STREAM STATUS RESULT=I2P_ERROR MESSAGE=\"ID not specified\"\n"); + return false ; + } + props.remove("ID"); + + rec = sSessionsHash.get(nick); + + if ( rec==null ) { + _log.debug("STREAM SESSION ID does not exist"); + writeString("STREAM STATUS RESULT=I2P_ERROR MESSAGE=\"STREAM SESSION ID does not exist\"\n"); + return false ; + } + + streamSession = rec.getHandler().streamSession ; + + if (streamSession==null) { + _log.debug("specified ID is not a stream session"); + writeString("STREAM STATUS RESULT=I2P_ERROR MESSAGE=\"specified ID is not a STREAM session\"\n"); + return false ; + } + + if ( opcode.equals ( "CONNECT" ) ) + { + return execStreamConnect ( props ); + } + else if ( opcode.equals ( "ACCEPT" ) ) + { + return execStreamAccept ( props ); + } + else if ( opcode.equals ( "FORWARD") ) + { + return execStreamForwardIncoming( props ); + } + else + { + _log.debug ( "Unrecognized RAW message opcode: \"" + + opcode + "\"" ); + writeString("STREAM STATUS RESULT=I2P_ERROR MESSAGE=\"Unrecognized RAW message opcode: \"" + + opcode + "\"" ); + return false; + } + } + + protected boolean execStreamConnect( Properties props) { + if (props == null) { + _log.debug("No parameters specified in STREAM CONNECT message"); + return false; + } + boolean verbose = props.getProperty("SILENT","false").equals("false"); + + String dest = props.getProperty("DESTINATION"); + if (dest == null) { + _log.debug("Destination not specified in RAW SEND message"); + return false; + } + props.remove("DESTINATION"); + + try { + try { + streamSession.connect( this, dest, props ); + return true ; + } catch (DataFormatException e) { + _log.debug("Invalid destination in STREAM CONNECT message"); + if (verbose) notifyStreamAccept ( "INVALID_KEY" ); + } catch (ConnectException e) { + _log.debug("STREAM CONNECT failed: " + e.getMessage()); + if (verbose) notifyStreamAccept ( "CONNECTION_REFUSED" ); + } catch (NoRouteToHostException e) { + _log.debug("STREAM CONNECT failed: " + e.getMessage()); + if (verbose) notifyStreamAccept ( "CANT_REACH_PEER" ); + } catch (InterruptedIOException e) { + _log.debug("STREAM CONNECT failed: " + e.getMessage()); + if (verbose) notifyStreamAccept ( "TIMEOUT" ); + } catch (I2PException e) { + _log.debug("STREAM CONNECT failed: " + e.getMessage()); + if (verbose) notifyStreamAccept ( "I2P_ERROR" ); + } + } catch (IOException e) { + } + return false ; + } + + protected boolean execStreamForwardIncoming( Properties props ) { + try { + try { + streamSession.startForwardingIncoming(props); + notifyStreamAccept("OK"); + return true ; + } catch (SAMException e) { + _log.debug("Forwarding STREAM connections failed: " + e.getMessage()); + notifyStreamAccept ( "FORWARDER_FAILED" ); + } + } catch (IOException e) { + } + return false ; + } + + protected boolean execStreamAccept( Properties props ) + { + boolean verbose = props.getProperty( "SILENT", "false").equals("false"); + try { + try { + streamSession.accept(this, verbose); + return true ; + } catch (InterruptedIOException e) { + _log.debug("STREAM ACCEPT failed: " + e.getMessage()); + if (verbose) notifyStreamAccept( "TIMEOUT" ); + } catch (I2PException e) { + _log.debug("STREAM ACCEPT failed: " + e.getMessage()); + if (verbose) notifyStreamAccept ( "I2P_ERROR" ); + } catch (SAMException e) { + _log.debug("STREAM ACCEPT failed: " + e.getMessage()); + if (verbose) notifyStreamAccept ( "ALREADY_ACCEPTING" ); + } + } catch (IOException e) { + } + return false ; + } + + + public void notifyStreamAccept(String status) throws IOException + { + if ( streamSession == null ) + { + _log.error ( "BUG! Received stream connection, but session is null!" ); + throw new NullPointerException ( "BUG! STREAM session is null!" ); + } + + if ( !writeString ( "STREAM STATUS RESULT=" + + status + + "\n" ) ) + { + throw new IOException ( "Error notifying connection to SAM client" ); + } + } + + public void notifyStreamOutgoingConnection(String result) throws IOException + { + if ( streamSession == null ) + { + _log.error ( "BUG! Received stream connection, but session is null!" ); + throw new NullPointerException ( "BUG! STREAM session is null!" ); + } + + if ( !writeString ( "STREAM STATUS RESULT=" + + result + + "\n" ) ) + { + throw new IOException ( "Error notifying connection to SAM client" ); + } + } + + public void notifyStreamIncomingConnection(Destination d) throws IOException { + if (streamSession == null) { + _log.error("BUG! Received stream connection, but session is null!"); + throw new NullPointerException("BUG! STREAM session is null!"); + } + + if (!writeString(d.toBase64() + "\n")) { + throw new IOException("Error notifying connection to SAM client"); + } + } + + public static void notifyStreamIncomingConnection(SocketChannel client, Destination d) throws IOException { + if (!writeString(d.toBase64() + "\n", client)) { + throw new IOException("Error notifying connection to SAM client"); + } + } + +} + diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3RawSession.java b/apps/sam/java/src/net/i2p/sam/SAMv3RawSession.java new file mode 100644 index 000000000..1bf7d18a0 --- /dev/null +++ b/apps/sam/java/src/net/i2p/sam/SAMv3RawSession.java @@ -0,0 +1,88 @@ +/** + * + */ +package net.i2p.sam; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.util.Properties; + +import net.i2p.client.I2PSessionException; +import net.i2p.data.DataFormatException; +import net.i2p.util.Log; + +/** + * @author MKVore + * + */ +public class SAMv3RawSession extends SAMRawSession implements SAMv3Handler.Session, SAMRawReceiver { + + String nick = null ; + SAMv3Handler handler = null ; + SAMv3Handler.DatagramServer server ; + private final static Log _log = new Log ( SAMv3DatagramSession.class ); + SocketAddress clientAddress = null ; + + public String getNick() { return nick; } + + /** + * @param nick nickname of the session + * @param server DatagramServer used for communication with the client + * @throws IOException + * @throws DataFormatException + * @throws I2PSessionException + */ + public SAMv3RawSession(String nick) + throws IOException, DataFormatException, I2PSessionException { + + super(SAMv3Handler.sSessionsHash.get(nick).getDest(), + SAMv3Handler.sSessionsHash.get(nick).getProps(), + SAMv3Handler.sSessionsHash.get(nick).getHandler() + ); + this.nick = nick ; + this.recv = this ; + this.server = SAMv3Handler.DatagramServer.getInstance() ; + + SAMv3Handler.SessionRecord rec = SAMv3Handler.sSessionsHash.get(nick); + if ( rec==null ) throw new InterruptedIOException() ; + + this.handler = rec.getHandler(); + + Properties props = rec.getProps(); + + + String portStr = props.getProperty("PORT") ; + if ( portStr==null ) { + _log.debug("receiver port not specified. Current socket will be used."); + } + else { + int port = Integer.parseInt(portStr); + + String host = props.getProperty("HOST"); + if ( host==null ) { + _log.debug("no host specified. Take from the client socket"); + + host = rec.getHandler().getClientIP(); + } + + + this.clientAddress = new InetSocketAddress(host,port); + } + } + + public void receiveRawBytes(byte[] data) throws IOException { + if (this.clientAddress==null) { + this.handler.receiveRawBytes(data); + } else { + ByteBuffer msgBuf = ByteBuffer.allocate(data.length); + msgBuf.put(data); + msgBuf.flip(); + this.server.send(this.clientAddress, msgBuf); + } + } + + public void stopRawReceiving() {} +} diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java b/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java new file mode 100644 index 000000000..b9ea710c6 --- /dev/null +++ b/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java @@ -0,0 +1,389 @@ +package net.i2p.sam; +/* + * free (adj.): unencumbered; not under the control of others + * Written by human in 2004 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.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.ConnectException; +import java.net.NoRouteToHostException; +import java.util.Properties; + +import net.i2p.I2PException; +import net.i2p.client.I2PClient; +import net.i2p.client.streaming.I2PServerSocket; +import net.i2p.client.streaming.I2PSocket; +import net.i2p.client.streaming.I2PSocketManagerFactory; +import net.i2p.client.streaming.I2PSocketOptions; +import net.i2p.data.Base64; +import net.i2p.data.DataFormatException; +import net.i2p.data.Destination; +import net.i2p.util.I2PAppThread; +import net.i2p.util.Log; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.WritableByteChannel; +import java.nio.ByteBuffer ; +import java.nio.channels.SocketChannel; + +/** + * SAMv3 STREAM session class. + * + * @author mkvore + */ + +public class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handler.Session +{ + + private final static Log _log = new Log ( SAMv3StreamSession.class ); + + protected final int BUFFER_SIZE = 1024 ; + + protected Object socketServerLock = new Object(); + protected I2PServerSocket socketServer = null; + + protected String nick ; + + public String getNick() { + return nick ; + } + + /** + * Create a new SAM STREAM session. + * + * @param dest Base64-encoded destination (private key) + * @param dir Session direction ("RECEIVE", "CREATE" or "BOTH") + * @param props Properties to setup the I2P session + * @param recv Object that will receive incoming data + * @throws IOException + * @throws DataFormatException + * @throws SAMException + */ + public SAMv3StreamSession(String login) + throws IOException, DataFormatException, SAMException + { + initSAMStreamSession(login); + } + + public static SAMv3Handler.SessionsDB getDB() + { + return SAMv3Handler.sSessionsHash ; + } + + private void initSAMStreamSession(String login) + throws IOException, DataFormatException, SAMException{ + + SAMv3Handler.SessionRecord rec = getDB().get(login); + String dest = rec.getDest() ; + ByteArrayInputStream ba_dest = new ByteArrayInputStream(Base64.decode(dest)); + + this.recv = rec.getHandler(); + + _log.debug("SAM STREAM session instantiated"); + + Properties allprops = new Properties(); + allprops.putAll(System.getProperties()); + allprops.putAll(rec.getProps()); + + String i2cpHost = allprops.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1"); + int i2cpPort ; + String port = allprops.getProperty(I2PClient.PROP_TCP_PORT, "7654"); + try { + i2cpPort = Integer.parseInt(port); + } catch (NumberFormatException nfe) { + throw new SAMException("Invalid I2CP port specified [" + port + "]"); + } + + _log.debug("Creating I2PSocketManager..."); + socketMgr = I2PSocketManagerFactory.createManager(ba_dest, + i2cpHost, + i2cpPort, + allprops); + if (socketMgr == null) { + throw new SAMException("Error creating I2PSocketManager towards "+i2cpHost+":"+i2cpPort); + } + + socketMgr.addDisconnectListener(new DisconnectListener()); + this.nick = login ; + } + + /** + * Connect the SAM STREAM session to the specified Destination + * + * @param id Unique id for the connection + * @param dest Base64-encoded Destination to connect to + * @param props Options to be used for connection + * + * @return true if successful + * @throws DataFormatException if the destination is not valid + * @throws ConnectException if the destination refuses connections + * @throws NoRouteToHostException if the destination can't be reached + * @throws InterruptedIOException if the connection timeouts + * @throws I2PException if there's another I2P-related error + * @throws IOException + */ + public void connect ( SAMv3Handler handler, String dest, Properties props ) throws I2PException, ConnectException, NoRouteToHostException, DataFormatException, InterruptedIOException, IOException { + + boolean verbose = (props.getProperty("SILENT", "false").equals("false")); + Destination d = new Destination(); + d = SAMUtils.getDest(dest); + + I2PSocketOptions opts = socketMgr.buildOptions(props); + if (props.getProperty(I2PSocketOptions.PROP_CONNECT_TIMEOUT) == null) + opts.setConnectTimeout(60 * 1000); + + _log.debug("Connecting new I2PSocket..."); + + // blocking connection (SAMv3) + + I2PSocket i2ps = socketMgr.connect(d, opts); + + SAMv3Handler.SessionRecord rec = SAMv3Handler.sSessionsHash.get(nick); + + if ( rec==null ) throw new InterruptedIOException() ; + + if (verbose) handler.notifyStreamOutgoingConnection("OK") ; + + handler.stealSocket() ; + + ReadableByteChannel fromClient = handler.getClientSocket(); + ReadableByteChannel fromI2P = Channels.newChannel(i2ps.getInputStream()); + WritableByteChannel toClient = handler.getClientSocket(); + WritableByteChannel toI2P = Channels.newChannel(i2ps.getOutputStream()); + + (new Thread(rec.getThreadGroup(), new I2PAppThread(new Pipe(fromClient,toI2P), "SAMPipeClientToI2P"))).start(); + (new Thread(rec.getThreadGroup(), new I2PAppThread(new Pipe(fromI2P,toClient), "SAMPipeClientToI2P"))).start(); + + } + + /** + * Accept an incoming STREAM + * + * @param id Unique id for the connection + * @param dest Base64-encoded Destination to connect to + * @param props Options to be used for connection + * + * @return true if successful + * @throws DataFormatException if the destination is not valid + * @throws ConnectException if the destination refuses connections + * @throws NoRouteToHostException if the destination can't be reached + * @throws InterruptedIOException if the connection timeouts + * @throws I2PException if there's another I2P-related error + * @throws IOException + */ + public void accept(SAMv3Handler handler, boolean verbose) + throws I2PException, InterruptedIOException, IOException, SAMException { + + synchronized( this.socketServerLock ) + { + if (this.socketServer!=null) { + _log.debug("a socket server is already defined for this destination"); + throw new SAMException("a socket server is already defined for this destination"); + } + this.socketServer = this.socketMgr.getServerSocket(); + } + + I2PSocket i2ps; + i2ps = this.socketServer.accept(); + + synchronized( this.socketServerLock ) + { + this.socketServer = null ; + } + + SAMv3Handler.SessionRecord rec = SAMv3Handler.sSessionsHash.get(nick); + + if ( rec==null ) throw new InterruptedIOException() ; + + if (verbose) + handler.notifyStreamIncomingConnection(i2ps.getPeerDestination()) ; + + handler.stealSocket() ; + ReadableByteChannel fromClient = handler.getClientSocket(); + ReadableByteChannel fromI2P = Channels.newChannel(i2ps.getInputStream()); + WritableByteChannel toClient = handler.getClientSocket(); + WritableByteChannel toI2P = Channels.newChannel(i2ps.getOutputStream()); + + (new Thread(rec.getThreadGroup(), new I2PAppThread(new Pipe(fromClient,toI2P), "SAMPipeClientToI2P"))).start(); + (new Thread(rec.getThreadGroup(), new I2PAppThread(new Pipe(fromI2P,toClient), "SAMPipeClientToI2P"))).start(); + } + + + public void startForwardingIncoming( Properties props ) throws SAMException, InterruptedIOException + { + SAMv3Handler.SessionRecord rec = SAMv3Handler.sSessionsHash.get(nick); + boolean verbose = props.getProperty("SILENT", "false").equals("false"); + + if ( rec==null ) throw new InterruptedIOException() ; + + String portStr = props.getProperty("PORT") ; + if ( portStr==null ) { + _log.debug("receiver port not specified"); + throw new SAMException("receiver port not specified"); + } + int port = Integer.parseInt(portStr); + + String host = props.getProperty("HOST"); + if ( host==null ) { + _log.debug("no host specified. Take from the client socket"); + + host = rec.getHandler().getClientIP(); + } + + + synchronized( this.socketServerLock ) + { + if (this.socketServer!=null) { + _log.debug("a socket server is already defined for this destination"); + throw new SAMException("a socket server is already defined for this destination"); + } + this.socketServer = this.socketMgr.getServerSocket(); + } + + SocketForwarder forwarder = new SocketForwarder(host, port, this, verbose); + (new Thread(rec.getThreadGroup(), new I2PAppThread(forwarder, "SAMStreamForwarder"))).start(); + + } + + public class SocketForwarder extends Thread + { + String host = null ; + int port = 0 ; + SAMv3StreamSession session; + boolean verbose; + + SocketForwarder(String host, int port, SAMv3StreamSession session, boolean verbose) { + this.host = host ; + this.port = port ; + this.session = session ; + this.verbose = verbose ; + } + + public void run() + { + while (session.socketServer!=null) { + + boolean available = false ; + I2PSocket i2ps = null ; + try { + available = session.socketServer.waitIncoming(-1); + } catch (ConnectException e) { + _log.debug("ConnectException"); + break ; + } catch (I2PException e) { + _log.debug("I2PServerSocket has been closed"); + break ; + } catch (InterruptedException e) { + _log.debug("InterruptedException"); + break ; + } + if ( !available ) continue ; + + java.net.InetSocketAddress addr = new java.net.InetSocketAddress(host,port); + + SocketChannel clientServerSock = null ; + try { + clientServerSock = SocketChannel.open(addr) ; + } + catch ( IOException e ) { + continue ; + } + + try { + i2ps = session.socketServer.accept(false); + } catch (Exception e) {} + + if (i2ps==null) { + try { + clientServerSock.close(); + } catch (IOException ee) {} + continue ; + } + try { + if (this.verbose) + SAMv3Handler.notifyStreamIncomingConnection( + clientServerSock, i2ps.getPeerDestination()); + ReadableByteChannel fromClient = clientServerSock ; + ReadableByteChannel fromI2P = Channels.newChannel(i2ps.getInputStream()); + WritableByteChannel toClient = clientServerSock ; + WritableByteChannel toI2P = Channels.newChannel(i2ps.getOutputStream()); + new I2PAppThread(new Pipe(fromClient,toI2P), "SAMPipeClientToI2P").start(); + new I2PAppThread(new Pipe(fromI2P,toClient), "SAMPipeClientToI2P").start(); + + } catch (IOException e) { + try { + clientServerSock.close(); + } catch (IOException ee) {} + try { + i2ps.close(); + } catch (IOException ee) {} + continue ; + } + } + } + } + public class Pipe extends Thread + { + ReadableByteChannel in ; + WritableByteChannel out ; + ByteBuffer buf ; + + public Pipe(ReadableByteChannel in, WritableByteChannel out) + { + this.in = in ; + this.out = out ; + this.buf = ByteBuffer.allocate(BUFFER_SIZE) ; + } + + public void run() + { + try { + while (!Thread.interrupted() && (in.read(buf)>=0 || buf.position() != 0)) { + buf.flip(); + out.write(buf); + buf.compact(); + } + } + catch (IOException e) + { + this.interrupt(); + } + try { + in.close(); + } + catch (IOException e) {} + try { + buf.flip(); + while (buf.hasRemaining()) + out.write(buf); + } + catch (IOException e) {} + try { + out.close(); + } + catch (IOException e) {} + } + } + + + + /** + * Close the stream session + */ + @Override + public void close() { + socketMgr.destroySocketManager(); + } + + public boolean sendBytes(String s, byte[] b) throws DataFormatException + { + throw new DataFormatException(null); + } + +} diff --git a/apps/sam/java/src/net/i2p/sam/client/SAMEventHandler.java b/apps/sam/java/src/net/i2p/sam/client/SAMEventHandler.java index 7df1a2324..9df867aa5 100644 --- a/apps/sam/java/src/net/i2p/sam/client/SAMEventHandler.java +++ b/apps/sam/java/src/net/i2p/sam/client/SAMEventHandler.java @@ -12,17 +12,17 @@ import net.i2p.util.Log; * */ public class SAMEventHandler extends SAMClientEventListenerImpl { - private I2PAppContext _context; + //private I2PAppContext _context; private Log _log; private Boolean _helloOk; private Object _helloLock = new Object(); private Boolean _sessionCreateOk; private Object _sessionCreateLock = new Object(); private Object _namingReplyLock = new Object(); - private Map _namingReplies = new HashMap(); + private Map _namingReplies = new HashMap(); public SAMEventHandler(I2PAppContext ctx) { - _context = ctx; + //_context = ctx; _log = ctx.logManager().getLog(getClass()); } diff --git a/apps/sam/java/src/net/i2p/sam/client/SAMStreamSend.java b/apps/sam/java/src/net/i2p/sam/client/SAMStreamSend.java index 4e9d1133b..80db744a3 100644 --- a/apps/sam/java/src/net/i2p/sam/client/SAMStreamSend.java +++ b/apps/sam/java/src/net/i2p/sam/client/SAMStreamSend.java @@ -31,10 +31,10 @@ public class SAMStreamSend { private OutputStream _samOut; private InputStream _samIn; private SAMReader _reader; - private boolean _dead; + //private boolean _dead; private SAMEventHandler _eventHandler; /** Connection id (Integer) to peer (Flooder) */ - private Map _remotePeers; + private Map _remotePeers; public static void main(String args[]) { if (args.length < 4) { @@ -42,7 +42,7 @@ public class SAMStreamSend { return; } I2PAppContext ctx = new I2PAppContext(); - String files[] = new String[args.length - 3]; + //String files[] = new String[args.length - 3]; SAMStreamSend sender = new SAMStreamSend(ctx, args[0], args[1], args[2], args[3]); sender.startup(); } @@ -50,14 +50,14 @@ public class SAMStreamSend { public SAMStreamSend(I2PAppContext ctx, String samHost, String samPort, String destFile, String dataFile) { _context = ctx; _log = ctx.logManager().getLog(SAMStreamSend.class); - _dead = false; + //_dead = false; _samHost = samHost; _samPort = samPort; _destFile = destFile; _dataFile = dataFile; _conOptions = ""; _eventHandler = new SendEventHandler(_context); - _remotePeers = new HashMap(); + _remotePeers = new HashMap(); } public void startup() { @@ -207,7 +207,6 @@ public class SAMStreamSend { _started = _context.clock().now(); _context.statManager().addRateData("send." + _connectionId + ".started", 1, 0); byte data[] = new byte[1024]; - long value = 0; long lastSend = _context.clock().now(); while (!_closed) { try { diff --git a/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java b/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java index 8d29e3799..406150b36 100644 --- a/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java +++ b/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java @@ -31,10 +31,10 @@ public class SAMStreamSink { private OutputStream _samOut; private InputStream _samIn; private SAMReader _reader; - private boolean _dead; + //private boolean _dead; private SAMEventHandler _eventHandler; /** Connection id (Integer) to peer (Flooder) */ - private Map _remotePeers; + private Map _remotePeers; public static void main(String args[]) { if (args.length < 4) { @@ -49,14 +49,14 @@ public class SAMStreamSink { public SAMStreamSink(I2PAppContext ctx, String samHost, String samPort, String destFile, String sinkDir) { _context = ctx; _log = ctx.logManager().getLog(SAMStreamSink.class); - _dead = false; + //_dead = false; _samHost = samHost; _samPort = samPort; _destFile = destFile; _sinkDir = sinkDir; _conOptions = ""; _eventHandler = new SinkEventHandler(_context); - _remotePeers = new HashMap(); + _remotePeers = new HashMap(); } public void startup() { @@ -70,7 +70,8 @@ public class SAMStreamSink { String ourDest = handshake(); _log.debug("Handshake complete. we are " + ourDest); if (ourDest != null) { - boolean written = writeDest(ourDest); + //boolean written = + writeDest(ourDest); _log.debug("Dest written"); } } diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java index 7d1d4827f..35eca57e5 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java @@ -1,9 +1,11 @@ package net.i2p.client.streaming; +import java.net.ConnectException; import java.util.ArrayList; import java.util.List; import net.i2p.I2PAppContext; +import net.i2p.util.Clock; import net.i2p.util.Log; import net.i2p.util.SimpleTimer; @@ -14,7 +16,7 @@ class ConnectionHandler { private I2PAppContext _context; private Log _log; private ConnectionManager _manager; - private List _synQueue; + private List _synQueue; private boolean _active; private int _acceptTimeout; @@ -61,13 +63,45 @@ class ConnectionHandler { } } + public boolean waitSyn( long ms ) throws InterruptedException { + boolean incoming = false ; + boolean isTimed = (ms>=0); + + Clock clock = I2PAppContext.getGlobalContext().clock(); + long now = clock.now(); + long end = now + ms; + while (!incoming && (!isTimed || now<=end) ) { + synchronized (_synQueue) { + + for (Packet p : _synQueue) + { + if (p.isFlagSet(Packet.FLAG_SYNCHRONIZE)) { + incoming = true ; + break; + } + } + if (!incoming) { + if (!isTimed) { + _synQueue.wait(); + } else { + now = clock.now(); + if (now < end) { + _synQueue.wait(end-now); + } + } + } + } + } + return incoming ; + } + /** * Receive an incoming connection (built from a received SYN) * Non-SYN packets with a zero SendStreamID may also be queued here so * that they don't get thrown away while the SYN packet before it is queued. * - * @param timeoutMs max amount of time to wait for a connection (if less - * than 1ms, wait indefinitely) + * @param timeoutMs max amount of time to wait for a connection (if negative, + * wait indefinitely) * @return connection received, or null if there was a timeout or the * handler was shut down */ @@ -77,8 +111,6 @@ class ConnectionHandler { long expiration = timeoutMs + _context.clock().now(); while (true) { - if ( (timeoutMs > 0) && (expiration < _context.clock().now()) ) - return null; if (!_active) { // fail all the ones we had queued up synchronized (_synQueue) { @@ -97,7 +129,7 @@ class ConnectionHandler { if (_log.shouldLog(Log.DEBUG)) _log.debug("Accept("+ timeoutMs+"): active=" + _active + " queue: " + _synQueue.size()); - if (timeoutMs <= 0) { + if (timeoutMs < 0) { try { _synQueue.wait(); } catch (InterruptedException ie) {} } else { long remaining = expiration - _context.clock().now(); @@ -129,6 +161,8 @@ class ConnectionHandler { } } // keep looping... + if ( (timeoutMs >= 0) && (expiration < _context.clock().now()) ) + return null; } } diff --git a/apps/streaming/java/src/net/i2p/client/streaming/I2PServerSocketFull.java b/apps/streaming/java/src/net/i2p/client/streaming/I2PServerSocketFull.java index 83f7c8376..ab9cb1c9d 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/I2PServerSocketFull.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/I2PServerSocketFull.java @@ -1,7 +1,12 @@ package net.i2p.client.streaming; +import java.net.ConnectException; import java.net.SocketTimeoutException; + +import net.i2p.I2PAppContext; import net.i2p.I2PException; +import net.i2p.util.Clock; +import net.i2p.util.Log; /** * Bridge to allow accepting new connections @@ -45,4 +50,43 @@ public class I2PServerSocketFull implements I2PServerSocket { public I2PSocketManager getManager() { return _socketManager; } + + /** + * accept(true) has the same behaviour as accept(). + * accept(false) does not wait for a socket connecting. If a socket is + * available in the queue, it is accepted. Else, null is returned. + * + * @param true if the call should block until a socket is available + * + * @return a connected I2PSocket, or null + * + * @throws I2PException if there is a problem with reading a new socket + * from the data available (aka the I2PSession closed, etc) + * @throws SocketTimeoutException if the timeout has been reached + */ + + public I2PSocket accept(boolean blocking) throws I2PException, SocketTimeoutException { + long timeout = this.getSoTimeout(); + + try { + if (blocking) + { + this.setSoTimeout(-1); + } else { + this.setSoTimeout(0); + } + try { + return this.accept(); + } catch (SocketTimeoutException e) { + if (blocking) throw e; + else return null ; + } + } finally { + this.setSoTimeout(timeout); + } + } + + public boolean waitIncoming(long timeoutMs) throws InterruptedException { + return this._socketManager.getConnectionManager().getConnectionHandler().waitSyn(timeoutMs); + } } From e0dccb59705afa45cff809c791f5b4a013a109f4 Mon Sep 17 00:00:00 2001 From: mkvore-commit Date: Thu, 2 Apr 2009 08:54:28 +0000 Subject: [PATCH 073/688] --- .../client/streaming/ConnectionHandler.java | 30 +------------------ 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java index 35eca57e5..a189d9a30 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java @@ -64,35 +64,7 @@ class ConnectionHandler { } public boolean waitSyn( long ms ) throws InterruptedException { - boolean incoming = false ; - boolean isTimed = (ms>=0); - - Clock clock = I2PAppContext.getGlobalContext().clock(); - long now = clock.now(); - long end = now + ms; - while (!incoming && (!isTimed || now<=end) ) { - synchronized (_synQueue) { - - for (Packet p : _synQueue) - { - if (p.isFlagSet(Packet.FLAG_SYNCHRONIZE)) { - incoming = true ; - break; - } - } - if (!incoming) { - if (!isTimed) { - _synQueue.wait(); - } else { - now = clock.now(); - if (now < end) { - _synQueue.wait(end-now); - } - } - } - } - } - return incoming ; + throw new InterruptedException(); } /** From 53cb80636a957db640c3c4444e2e8f08e060b5c1 Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 2 Apr 2009 18:12:38 +0000 Subject: [PATCH 074/688] remove dup comment in persisted profiles --- core/java/src/net/i2p/stat/RateStat.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/java/src/net/i2p/stat/RateStat.java b/core/java/src/net/i2p/stat/RateStat.java index 329706040..dd1fe63e3 100644 --- a/core/java/src/net/i2p/stat/RateStat.java +++ b/core/java/src/net/i2p/stat/RateStat.java @@ -134,7 +134,6 @@ public class RateStat { buf.append("# Period : ").append(DataHelper.formatDuration(_rates[i].getPeriod())).append(" for rate ") .append(_groupName).append(" - ").append(_statName).append(NL); buf.append(NL); - out.write(buf.toString().getBytes()); String curPrefix = prefix + "." + DataHelper.formatDuration(_rates[i].getPeriod()); _rates[i].store(curPrefix, buf); out.write(buf.toString().getBytes()); From f6bc9e87072a26ced4e22311751ae028d4b14efe Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 2 Apr 2009 18:55:40 +0000 Subject: [PATCH 075/688] Profiles: - Remove unused calculators and RateStats: CapacityCalculator, StrictSpeedCalculator, IsFailingCalculator; sendFailureSize, processSuccessRate, processfailureRate, commErrorRate, tunnelTestResponseTimeSlow - Reduced number of Rates in these RateStats: sendSuccessSize, receiveSize, rejectRate, failRate - ~5KB/profile savings total - Deflate speed calculation once an hour instead of once a day, to improve fast tier selection --- .../src/net/i2p/router/RouterContext.java | 10 -- .../peermanager/IsFailingCalculator.java | 82 -------------- .../i2p/router/peermanager/PeerProfile.java | 103 +++--------------- .../peermanager/ProfileManagerImpl.java | 6 - .../router/peermanager/ProfileOrganizer.java | 4 +- .../peermanager/ProfilePersistenceHelper.java | 7 -- .../peermanager/ReliabilityCalculator.java | 91 ---------------- .../peermanager/StrictSpeedCalculator.java | 90 --------------- .../i2p/router/peermanager/TunnelHistory.java | 27 +---- 9 files changed, 21 insertions(+), 399 deletions(-) delete mode 100644 router/java/src/net/i2p/router/peermanager/IsFailingCalculator.java delete mode 100644 router/java/src/net/i2p/router/peermanager/ReliabilityCalculator.java delete mode 100644 router/java/src/net/i2p/router/peermanager/StrictSpeedCalculator.java diff --git a/router/java/src/net/i2p/router/RouterContext.java b/router/java/src/net/i2p/router/RouterContext.java index 517a5ba35..e3b5e3846 100644 --- a/router/java/src/net/i2p/router/RouterContext.java +++ b/router/java/src/net/i2p/router/RouterContext.java @@ -16,9 +16,7 @@ import net.i2p.router.peermanager.IsFailingCalculator; import net.i2p.router.peermanager.PeerManagerFacadeImpl; import net.i2p.router.peermanager.ProfileManagerImpl; import net.i2p.router.peermanager.ProfileOrganizer; -import net.i2p.router.peermanager.ReliabilityCalculator; import net.i2p.router.peermanager.SpeedCalculator; -import net.i2p.router.peermanager.StrictSpeedCalculator; import net.i2p.router.transport.CommSystemFacadeImpl; import net.i2p.router.transport.FIFOBandwidthLimiter; import net.i2p.router.transport.OutboundMessageRegistry; @@ -65,9 +63,7 @@ public class RouterContext extends I2PAppContext { private Calculator _isFailingCalc; private Calculator _integrationCalc; private Calculator _speedCalc; - private Calculator _reliabilityCalc; private Calculator _capacityCalc; - private Calculator _oldSpeedCalc; private static List _contexts = new ArrayList(1); @@ -135,8 +131,6 @@ public class RouterContext extends I2PAppContext { _isFailingCalc = new IsFailingCalculator(this); _integrationCalc = new IntegrationCalculator(this); _speedCalc = new SpeedCalculator(this); - _oldSpeedCalc = new StrictSpeedCalculator(this); - _reliabilityCalc = new ReliabilityCalculator(this); _capacityCalc = new CapacityCalculator(this); } @@ -270,9 +264,6 @@ public class RouterContext extends I2PAppContext { public Calculator integrationCalculator() { return _integrationCalc; } /** how do we rank the speed of profiles? */ public Calculator speedCalculator() { return _speedCalc; } - public Calculator oldSpeedCalculator() { return _oldSpeedCalc; } - /** how do we rank the reliability of profiles? */ - public Calculator reliabilityCalculator() { return _reliabilityCalc; } /** how do we rank the capacity of profiles? */ public Calculator capacityCalculator() { return _capacityCalc; } @@ -301,7 +292,6 @@ public class RouterContext extends I2PAppContext { buf.append(_isFailingCalc).append('\n'); buf.append(_integrationCalc).append('\n'); buf.append(_speedCalc).append('\n'); - buf.append(_reliabilityCalc).append('\n'); return buf.toString(); } diff --git a/router/java/src/net/i2p/router/peermanager/IsFailingCalculator.java b/router/java/src/net/i2p/router/peermanager/IsFailingCalculator.java deleted file mode 100644 index 1391bd8a1..000000000 --- a/router/java/src/net/i2p/router/peermanager/IsFailingCalculator.java +++ /dev/null @@ -1,82 +0,0 @@ -package net.i2p.router.peermanager; - -import net.i2p.router.RouterContext; -import net.i2p.util.Log; - -/** - * Simple boolean calculation to determine whether the given profile is "failing" - - * meaning we shouldn't bother trying to get them to do something. However, if we - * have a specific need to contact them in particular - e.g. instructions in a garlic - * or leaseSet - we will try. The currently implemented algorithm determines that - * a profile is failing if withing the last few minutes, they've done something bad:
      - *
    • It has a comm error (TCP disconnect, etc) in the last minute or two
    • - *
    • They've failed to respond to a db message in the last minute or two
    • - *
    • They've rejected a tunnel in the last 5 minutes
    • - *
    • They've been unreachable any time in the last 5 minutes
    • - *
    - * - */ -public class IsFailingCalculator extends Calculator { - private Log _log; - private RouterContext _context; - - /** if they haven't b0rked in the last 2 minutes, they're ok */ - private final static long GRACE_PERIOD = 2*60*1000; - - public IsFailingCalculator(RouterContext context) { - _context = context; - _log = context.logManager().getLog(IsFailingCalculator.class); - } - - public boolean calcBoolean(PeerProfile profile) { - // have we failed in the last 119 seconds? - /* - if ( (profile.getCommError().getRate(60*1000).getCurrentEventCount() > 0) || - (profile.getCommError().getRate(60*1000).getLastEventCount() > 0) || - (profile.getCommError().getRate(10*60*1000).getCurrentEventCount() > 0) ) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Peer " + profile.getPeer().toBase64() - + " is failing because it had a comm error recently "); - return true; - } else { - */ - //if ( (profile.getDBHistory().getFailedLookupRate().getRate(60*1000).getCurrentEventCount() > 0) || - // (profile.getDBHistory().getFailedLookupRate().getRate(60*1000).getLastEventCount() > 0) ) { - // // are they overloaded (or disconnected)? - // return true; - //} - - // this doesn't make sense with probabalistic rejections - we should be - // adequately dampening the capacity so these peers aren't queried - - //Rate rejectRate = profile.getTunnelHistory().getRejectionRate().getRate(10*60*1000); - //if (rejectRate.getCurrentEventCount() >= 2) { - // if (_log.shouldLog(Log.DEBUG)) - // _log.debug("Peer " + profile.getPeer().toBase64() - // + " is failing because they rejected some tunnels recently"); - // return true; - //} - - //// - // the right way to behave would be to use some statistical - // analysis on the failure rate, and only mark the peer as failing - // if their rate exceeded the expected rate (mean, median, stddev, etc) - //// - - //Rate failedRate = profile.getTunnelHistory().getFailedRate().getRate(60*1000); - //if (failedRate.getCurrentEventCount() >= 2) { - // if (_log.shouldLog(Log.DEBUG)) - // _log.debug("Peer " + profile.getPeer().toBase64() - // + " is failing because too many of their tunnels failed recently"); - // return true; - //} - - // if they have rejected us saying they're totally broken anytime in the last - // 10 minutes, dont bother 'em - if (profile.getTunnelHistory().getLastRejectedCritical() > _context.clock().now() - 10*60*1000) - return true; - - return false; - //} - } -} diff --git a/router/java/src/net/i2p/router/peermanager/PeerProfile.java b/router/java/src/net/i2p/router/peermanager/PeerProfile.java index 4a94100bd..5c058b493 100644 --- a/router/java/src/net/i2p/router/peermanager/PeerProfile.java +++ b/router/java/src/net/i2p/router/peermanager/PeerProfile.java @@ -37,25 +37,19 @@ public class PeerProfile { private double _tunnelTestResponseTimeAvg; // periodic rates private RateStat _sendSuccessSize = null; - private RateStat _sendFailureSize = null; private RateStat _receiveSize = null; private RateStat _dbResponseTime = null; private RateStat _tunnelCreateResponseTime = null; private RateStat _tunnelTestResponseTime = null; - private RateStat _tunnelTestResponseTimeSlow = null; - private RateStat _commError = null; private RateStat _dbIntroduction = null; // calculation bonuses private long _speedBonus; - private long _reliabilityBonus; private long _capacityBonus; private long _integrationBonus; // calculation values private double _speedValue; - private double _reliabilityValue; private double _capacityValue; private double _integrationValue; - private double _oldSpeedValue; private boolean _isFailing; // good vs bad behavior private TunnelHistory _tunnelHistory; @@ -72,7 +66,6 @@ public class PeerProfile { _log = context.logManager().getLog(PeerProfile.class); _expanded = false; _speedValue = 0; - _reliabilityValue = 0; _capacityValue = 0; _integrationValue = 0; _isFailing = false; @@ -111,6 +104,11 @@ public class PeerProfile { * given period?) * Also mark active if it is connected, as this will tend to encourage use * of already-connected peers. + * + * Note: this appears to be the only use for these two RateStats. + * + * @param period must be one of the periods in the RateStat constructors below + * (5*60*1000 or 60*60*1000) */ public boolean getIsActive(long period) { if ( (getSendSuccessSize().getRate(period).getCurrentEventCount() > 0) || @@ -154,8 +152,6 @@ public class PeerProfile { /** how large successfully sent messages are, calculated over a 1 minute, 1 hour, and 1 day period */ public RateStat getSendSuccessSize() { return _sendSuccessSize; } - /** how large messages that could not be sent were, calculated over a 1 minute, 1 hour, and 1 day period */ - public RateStat getSendFailureSize() { return _sendFailureSize; } /** how large received messages are, calculated over a 1 minute, 1 hour, and 1 day period */ public RateStat getReceiveSize() { return _receiveSize; } /** how long it takes to get a db response from the peer (in milliseconds), calculated over a 1 minute, 1 hour, and 1 day period */ @@ -164,10 +160,6 @@ public class PeerProfile { public RateStat getTunnelCreateResponseTime() { return _tunnelCreateResponseTime; } /** how long it takes to successfully test a tunnel this peer participates in (in milliseconds), calculated over a 10 minute, 1 hour, and 1 day period */ public RateStat getTunnelTestResponseTime() { return _tunnelTestResponseTime; } - /** how long it takes to successfully test the peer (in milliseconds) when the time exceeds 5s */ - public RateStat getTunnelTestResponseTimeSlow() { return _tunnelTestResponseTimeSlow; } - /** how long between communication errors with the peer (disconnection, etc), calculated over a 1 minute, 1 hour, and 1 day period */ - public RateStat getCommError() { return _commError; } /** how many new peers we get from dbSearchReplyMessages or dbStore messages, calculated over a 1 hour, 1 day, and 1 week period */ public RateStat getDbIntroduction() { return _dbIntroduction; } @@ -179,14 +171,6 @@ public class PeerProfile { public long getSpeedBonus() { return _speedBonus; } public void setSpeedBonus(long bonus) { _speedBonus = bonus; } - /** - * extra factor added to the reliability ranking - this can be updated in the profile - * written to disk to affect how the algorithm ranks reliability. Negative values are - * penalties - */ - public long getReliabilityBonus() { return _reliabilityBonus; } - public void setReliabilityBonus(long bonus) { _reliabilityBonus = bonus; } - /** * extra factor added to the capacity ranking - this can be updated in the profile * written to disk to affect how the algorithm ranks capacity. Negative values are @@ -210,14 +194,6 @@ public class PeerProfile { * */ public double getSpeedValue() { return _speedValue; } - public double getOldSpeedValue() { return _oldSpeedValue; } - /** - * How likely are they to stay up and pass on messages over the next few minutes. - * Positive numbers means more likely, negative numbers means its probably not - * even worth trying. - * - */ - public double getReliabilityValue() { return _reliabilityValue; } /** * How many tunnels do we think this peer can handle over the next hour? * @@ -354,13 +330,10 @@ public class PeerProfile { */ public void shrinkProfile() { _sendSuccessSize = null; - _sendFailureSize = null; _receiveSize = null; _dbResponseTime = null; _tunnelCreateResponseTime = null; _tunnelTestResponseTime = null; - _tunnelTestResponseTimeSlow = null; - _commError = null; _dbIntroduction = null; _tunnelHistory = null; _dbHistory = null; @@ -378,21 +351,15 @@ public class PeerProfile { public void expandProfile() { String group = (null == _peer ? "profileUnknown" : _peer.toBase64().substring(0,6)); if (_sendSuccessSize == null) - _sendSuccessSize = new RateStat("sendSuccessSize", "How large successfully sent messages are", group, new long[] { 60*1000l, 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - if (_sendFailureSize == null) - _sendFailureSize = new RateStat("sendFailureSize", "How large messages that could not be sent were", group, new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000 } ); + _sendSuccessSize = new RateStat("sendSuccessSize", "How large successfully sent messages are", group, new long[] { 5*60*1000l, 60*60*1000l }); if (_receiveSize == null) - _receiveSize = new RateStat("receiveSize", "How large received messages are", group, new long[] { 60*1000l, 5*60*1000l, 60*60*1000l, 24*60*60*1000 } ); + _receiveSize = new RateStat("receiveSize", "How large received messages are", group, new long[] { 5*60*1000l, 60*60*1000l } ); if (_dbResponseTime == null) _dbResponseTime = new RateStat("dbResponseTime", "how long it takes to get a db response from the peer (in milliseconds)", group, new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000 } ); if (_tunnelCreateResponseTime == null) _tunnelCreateResponseTime = new RateStat("tunnelCreateResponseTime", "how long it takes to get a tunnel create response from the peer (in milliseconds)", group, new long[] { 10*60*1000l, 30*60*1000l, 60*60*1000l, 24*60*60*1000 } ); if (_tunnelTestResponseTime == null) _tunnelTestResponseTime = new RateStat("tunnelTestResponseTime", "how long it takes to successfully test a tunnel this peer participates in (in milliseconds)", group, new long[] { 10*60*1000l, 30*60*1000l, 60*60*1000l, 3*60*60*1000l, 24*60*60*1000 } ); - if (_tunnelTestResponseTimeSlow == null) - _tunnelTestResponseTimeSlow = new RateStat("tunnelTestResponseTimeSlow", "how long it takes to successfully test a peer when the time exceeds 5s", group, new long[] { 10*60*1000l, 30*60*1000l, 60*60*1000l, 3*60*60*1000l, 24*60*60*1000l, }); - if (_commError == null) - _commError = new RateStat("commErrorRate", "how long between communication errors with the peer (e.g. disconnection)", group, new long[] { 60*1000l, 10*60*1000l, 60*60*1000l, 24*60*60*1000 } ); if (_dbIntroduction == null) _dbIntroduction = new RateStat("dbIntroduction", "how many new peers we get from dbSearchReplyMessages or dbStore messages", group, new long[] { 60*60*1000l, 6*60*60*1000l, 24*60*60*1000l }); @@ -402,18 +369,17 @@ public class PeerProfile { _dbHistory = new DBHistory(_context, group); _sendSuccessSize.setStatLog(_context.statManager().getStatLog()); - _sendFailureSize.setStatLog(_context.statManager().getStatLog()); _receiveSize.setStatLog(_context.statManager().getStatLog()); _dbResponseTime.setStatLog(_context.statManager().getStatLog()); _tunnelCreateResponseTime.setStatLog(_context.statManager().getStatLog()); _tunnelTestResponseTime.setStatLog(_context.statManager().getStatLog()); - _tunnelTestResponseTimeSlow.setStatLog(_context.statManager().getStatLog()); - _commError.setStatLog(_context.statManager().getStatLog()); _dbIntroduction.setStatLog(_context.statManager().getStatLog()); _expanded = true; } /** once a day, on average, cut the measured throughtput values in half */ - private static final long DROP_PERIOD_MINUTES = 24*60; + /** let's try once an hour times 3/4 */ + private static final int DROP_PERIOD_MINUTES = 60; + private static final double DEGRADE_FACTOR = 0.75; private long _lastCoalesceDate = System.currentTimeMillis(); private void coalesceThroughput() { long now = System.currentTimeMillis(); @@ -430,46 +396,19 @@ public class PeerProfile { break; } } - - if (false && _log.shouldLog(Log.WARN) ) { - StringBuffer buf = new StringBuffer(128); - buf.append("Updating throughput after ").append(tot).append(" to "); - for (int i = 0; i < THROUGHPUT_COUNT; i++) - buf.append(_peakThroughput[i]).append(','); - buf.append(" for ").append(_peer.toBase64()); - _log.warn(buf.toString()); - } } else { - if (_context.random().nextLong(DROP_PERIOD_MINUTES*2) <= 0) { - for (int i = 0; i < THROUGHPUT_COUNT; i++) - _peakThroughput[i] /= 2; - - if (false && _log.shouldLog(Log.WARN) ) { - StringBuffer buf = new StringBuffer(128); - buf.append("Degrading the throughput measurements to "); - for (int i = 0; i < THROUGHPUT_COUNT; i++) - buf.append(_peakThroughput[i]).append(','); - buf.append(" for ").append(_peer.toBase64()); - _log.warn(buf.toString()); - } + if (_context.random().nextInt(DROP_PERIOD_MINUTES*2) <= 0) { + for (int i = 0; i < THROUGHPUT_COUNT; i++) + _peakThroughput[i] *= DEGRADE_FACTOR; } } // we degrade the tunnel throughput here too, regardless of the current // activity - if (_context.random().nextLong(DROP_PERIOD_MINUTES*2) <= 0) { + if (_context.random().nextInt(DROP_PERIOD_MINUTES*2) <= 0) { for (int i = 0; i < THROUGHPUT_COUNT; i++) { - _peakTunnelThroughput[i] /= 2; - _peakTunnel1mThroughput[i] /= 2; - } - - if (_log.shouldLog(Log.WARN) ) { - StringBuffer buf = new StringBuffer(128); - buf.append("Degrading the tunnel throughput measurements to "); - for (int i = 0; i < THROUGHPUT_COUNT; i++) - buf.append(_peakTunnel1mThroughput[i]).append(','); - buf.append(" for ").append(_peer.toBase64()); - _log.warn(buf.toString()); + _peakTunnelThroughput[i] *= DEGRADE_FACTOR; + _peakTunnel1mThroughput[i] *= DEGRADE_FACTOR; } } _peakThroughputCurrentTotal = 0; @@ -480,34 +419,27 @@ public class PeerProfile { /** update the stats and rates (this should be called once a minute) */ public void coalesceStats() { if (!_expanded) return; - _commError.coalesceStats(); _dbIntroduction.coalesceStats(); _dbResponseTime.coalesceStats(); _receiveSize.coalesceStats(); - _sendFailureSize.coalesceStats(); _sendSuccessSize.coalesceStats(); _tunnelCreateResponseTime.coalesceStats(); _tunnelTestResponseTime.coalesceStats(); - _tunnelTestResponseTimeSlow.coalesceStats(); _dbHistory.coalesceStats(); _tunnelHistory.coalesceStats(); coalesceThroughput(); _speedValue = calculateSpeed(); - _oldSpeedValue = calculateOldSpeed(); - _reliabilityValue = calculateReliability(); _capacityValue = calculateCapacity(); _integrationValue = calculateIntegration(); _isFailing = calculateIsFailing(); if (_log.shouldLog(Log.DEBUG)) - _log.debug("Coalesced: speed [" + _speedValue + "] reliability [" + _reliabilityValue + "] capacity [" + _capacityValue + "] integration [" + _integrationValue + "] failing? [" + _isFailing + "]"); + _log.debug("Coalesced: speed [" + _speedValue + "] capacity [" + _capacityValue + "] integration [" + _integrationValue + "] failing? [" + _isFailing + "]"); } private double calculateSpeed() { return _context.speedCalculator().calc(this); } - private double calculateOldSpeed() { return _context.oldSpeedCalculator().calc(this); } - private double calculateReliability() { return _context.reliabilityCalculator().calc(this); } private double calculateCapacity() { return _context.capacityCalculator().calc(this); } private double calculateIntegration() { return _context.integrationCalculator().calc(this); } private boolean calculateIsFailing() { return _context.isFailingCalculator().calcBoolean(this); } @@ -584,7 +516,6 @@ public class PeerProfile { //profile.coalesceStats(); buf.append("Peer " + profile.getPeer().toBase64() + ":\t Speed:\t" + fmt.format(profile.calculateSpeed()) - + " Reliability:\t" + fmt.format(profile.calculateReliability()) + " Capacity:\t" + fmt.format(profile.calculateCapacity()) + " Integration:\t" + fmt.format(profile.calculateIntegration()) + " Active?\t" + profile.getIsActive() diff --git a/router/java/src/net/i2p/router/peermanager/ProfileManagerImpl.java b/router/java/src/net/i2p/router/peermanager/ProfileManagerImpl.java index 9e789f9c6..df7691bd1 100644 --- a/router/java/src/net/i2p/router/peermanager/ProfileManagerImpl.java +++ b/router/java/src/net/i2p/router/peermanager/ProfileManagerImpl.java @@ -50,7 +50,6 @@ public class ProfileManagerImpl implements ProfileManager { PeerProfile data = getProfile(peer); if (data == null) return; data.setLastSendFailed(_context.clock().now()); - data.getSendFailureSize().addData(0, 0); // yeah, should be a frequency... } /** @@ -61,7 +60,6 @@ public class ProfileManagerImpl implements ProfileManager { PeerProfile data = getProfile(peer); if (data == null) return; data.setLastSendFailed(_context.clock().now()); - data.getSendFailureSize().addData(0, 0); // yeah, should be a frequency... } /** @@ -74,8 +72,6 @@ public class ProfileManagerImpl implements ProfileManager { PeerProfile data = getProfile(peer); if (data == null) return; data.setLastSendFailed(_context.clock().now()); - data.getSendFailureSize().addData(1, 0); // yeah, should be a frequency... - data.getCommError().addData(1, 0); // see above } /** @@ -125,8 +121,6 @@ public class ProfileManagerImpl implements ProfileManager { if (data == null) return; data.updateTunnelTestTimeAverage(responseTimeMs); data.getTunnelTestResponseTime().addData(responseTimeMs, responseTimeMs); - if (responseTimeMs > getSlowThreshold()) - data.getTunnelTestResponseTimeSlow().addData(responseTimeMs, responseTimeMs); } public void tunnelDataPushed(Hash peer, long rtt, int size) { diff --git a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java index b090d6cc7..1a42a244c 100644 --- a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java +++ b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java @@ -1028,7 +1028,7 @@ public class ProfileOrganizer { /** * called after locking the reorganizeLock, place the profile in the appropriate tier. - * This is where we implement the (betterThanAverage ? goToPierX : goToPierY) algorithms + * This is where we implement the (betterThanAverage ? goToTierX : goToTierY) algorithms * */ private void locked_placeProfile(PeerProfile profile) { @@ -1153,7 +1153,6 @@ public class ProfileOrganizer { organizer.isHighCapacity(peer) ? "IR " : organizer.isFailing(peer) ? "IX " : "I ") + "]: " + "\t Speed:\t" + fmt.format(profile.getSpeedValue()) - + " Reliability:\t" + fmt.format(profile.getReliabilityValue()) + " Capacity:\t" + fmt.format(profile.getCapacityValue()) + " Integration:\t" + fmt.format(profile.getIntegrationValue()) + " Active?\t" + profile.getIsActive() @@ -1164,7 +1163,6 @@ public class ProfileOrganizer { organizer.isHighCapacity(peer) ? "R " : organizer.isFailing(peer) ? "X " : " ") + "]: " + "\t Speed:\t" + fmt.format(profile.getSpeedValue()) - + " Reliability:\t" + fmt.format(profile.getReliabilityValue()) + " Capacity:\t" + fmt.format(profile.getCapacityValue()) + " Integration:\t" + fmt.format(profile.getIntegrationValue()) + " Active?\t" + profile.getIsActive() diff --git a/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java b/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java index fd88c406b..4f5d23611 100644 --- a/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java +++ b/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java @@ -95,7 +95,6 @@ class ProfilePersistenceHelper { if (_us != null) buf.append("# as calculated by ").append(_us.toBase64()).append(NL); buf.append("#").append(NL); - buf.append("# reliability: ").append(profile.getReliabilityValue()).append(NL); buf.append("# capacity: ").append(profile.getCapacityValue()).append(NL); buf.append("# integration: ").append(profile.getIntegrationValue()).append(NL); buf.append("# speedValue: ").append(profile.getSpeedValue()).append(NL); @@ -134,15 +133,12 @@ class ProfilePersistenceHelper { if (profile.getIsExpanded()) { // only write out expanded data if, uh, we've got it - profile.getCommError().store(out, "commError"); profile.getDbIntroduction().store(out, "dbIntroduction"); profile.getDbResponseTime().store(out, "dbResponseTime"); profile.getReceiveSize().store(out, "receiveSize"); - profile.getSendFailureSize().store(out, "sendFailureSize"); profile.getSendSuccessSize().store(out, "sendSuccessSize"); profile.getTunnelCreateResponseTime().store(out, "tunnelCreateResponseTime"); profile.getTunnelTestResponseTime().store(out, "tunnelTestResponseTime"); - profile.getTunnelTestResponseTimeSlow().store(out, "tunnelTestResponseTimeSlow"); } } @@ -217,15 +213,12 @@ class ProfilePersistenceHelper { profile.getTunnelHistory().load(props); profile.getDBHistory().load(props); - profile.getCommError().load(props, "commError", true); profile.getDbIntroduction().load(props, "dbIntroduction", true); profile.getDbResponseTime().load(props, "dbResponseTime", true); profile.getReceiveSize().load(props, "receiveSize", true); - profile.getSendFailureSize().load(props, "sendFailureSize", true); profile.getSendSuccessSize().load(props, "sendSuccessSize", true); profile.getTunnelCreateResponseTime().load(props, "tunnelCreateResponseTime", true); profile.getTunnelTestResponseTime().load(props, "tunnelTestResponseTime", true); - profile.getTunnelTestResponseTimeSlow().load(props, "tunnelTestResponseTimeSlow", true); if (_log.shouldLog(Log.DEBUG)) _log.debug("Loaded the profile for " + peer.toBase64() + " from " + file.getName()); diff --git a/router/java/src/net/i2p/router/peermanager/ReliabilityCalculator.java b/router/java/src/net/i2p/router/peermanager/ReliabilityCalculator.java deleted file mode 100644 index 674b2fd91..000000000 --- a/router/java/src/net/i2p/router/peermanager/ReliabilityCalculator.java +++ /dev/null @@ -1,91 +0,0 @@ -package net.i2p.router.peermanager; - -import net.i2p.router.RouterContext; -import net.i2p.stat.RateStat; -import net.i2p.util.Log; - -/** - * Determine how reliable the peer is - how likely they'll be able to respond or - * otherwise carry out whatever we ask them to (or even merely be reachable) - * - */ -public class ReliabilityCalculator extends Calculator { - private Log _log; - private RouterContext _context; - - public ReliabilityCalculator(RouterContext context) { - _context = context; - _log = context.logManager().getLog(ReliabilityCalculator.class); - } - - public double calc(PeerProfile profile) { - // if we've never succeeded (even if we've never tried), the reliability is zip - if (profile.getSendSuccessSize().getRate(60*60*1000).getLifetimeEventCount() < 0) - return profile.getReliabilityBonus(); - - long val = 0; - val += profile.getSendSuccessSize().getRate(60*1000).getCurrentEventCount() * 20; - val += profile.getSendSuccessSize().getRate(60*1000).getLastEventCount() * 10; - val += profile.getSendSuccessSize().getRate(60*60*1000).getLastEventCount() * 1; - val += profile.getSendSuccessSize().getRate(60*60*1000).getCurrentEventCount() * 5; - - val += profile.getTunnelCreateResponseTime().getRate(10*60*1000).getLastEventCount() * 5; - val += profile.getTunnelCreateResponseTime().getRate(60*60*1000).getCurrentEventCount(); - val += profile.getTunnelCreateResponseTime().getRate(60*60*1000).getLastEventCount(); - - //val -= profile.getSendFailureSize().getRate(60*1000).getLastEventCount() * 5; - //val -= profile.getSendFailureSize().getRate(60*60*1000).getCurrentEventCount()*2; - //val -= profile.getSendFailureSize().getRate(60*60*1000).getLastEventCount()*2; - - RateStat rejRate = profile.getTunnelHistory().getRejectionRate(); - if (rejRate.getRate(60*1000).getCurrentEventCount() > 0) - val -= 200; - if (rejRate.getRate(60*1000).getLastEventCount() > 0) - val -= 100; - if (rejRate.getRate(10*60*1000).getCurrentEventCount() > 0) - val -= 10; - if (rejRate.getRate(10*60*1000).getCurrentEventCount() > 0) - val -= 5; - - // penalize them heavily for dropping netDb requests (though these could have - // failed due to tunnel timeouts, so don't be too mean) - if (profile.getDBHistory().getFailedLookupRate().getRate(60*1000).getCurrentEventCount() > 0) - val -= 10; - if (profile.getDBHistory().getFailedLookupRate().getRate(60*1000).getLastEventCount() > 0) - val -= 5; - - // scream and shout on network errors - if (profile.getCommError().getRate(60*1000).getCurrentEventCount() > 0) - val -= 200; - if (profile.getCommError().getRate(60*1000).getLastEventCount() > 0) - val -= 200; - - if (profile.getCommError().getRate(60*60*1000).getCurrentEventCount() > 0) - val -= 10; - if (profile.getCommError().getRate(60*60*1000).getLastEventCount() > 0) - val -= 10; - - val -= profile.getCommError().getRate(24*60*60*1000).getCurrentEventCount() * 1; - - //long now = _context.clock().now(); - - long timeSinceRejection = 61*60*1000; // now - profile.getTunnelHistory().getLastRejected(); - if (timeSinceRejection > 60*60*1000) { - // noop. rejection was over 60 minutes ago - } else if (timeSinceRejection > 10*60*1000) { - val -= 10; // 10-60 minutes ago we got a rejection - } else if (timeSinceRejection > 60*1000) { - val -= 50; // 1-10 minutes ago we got a rejection - } else { - val -= 100; // we got a rejection within the last minute - } - - //if ( (profile.getLastSendSuccessful() > 0) && (now - 24*60*60*1000 > profile.getLastSendSuccessful()) ) { - // // we know they're real, but we havent sent them a message successfully in over a day. - // val -= 1000; - //} - - val += profile.getReliabilityBonus(); - return val; - } -} diff --git a/router/java/src/net/i2p/router/peermanager/StrictSpeedCalculator.java b/router/java/src/net/i2p/router/peermanager/StrictSpeedCalculator.java deleted file mode 100644 index 99453b0ab..000000000 --- a/router/java/src/net/i2p/router/peermanager/StrictSpeedCalculator.java +++ /dev/null @@ -1,90 +0,0 @@ -package net.i2p.router.peermanager; - -import net.i2p.router.RouterContext; -import net.i2p.stat.Rate; -import net.i2p.stat.RateStat; -import net.i2p.util.Log; - -/** - * Simple speed calculator that just counts how many messages go through the - * tunnel. - * - */ -public class StrictSpeedCalculator extends Calculator { - private Log _log; - private RouterContext _context; - - public StrictSpeedCalculator(RouterContext context) { - _context = context; - _log = context.logManager().getLog(StrictSpeedCalculator.class); - } - - public double calc(PeerProfile profile) { - return countSuccesses(profile); - } - private double countSuccesses(PeerProfile profile) { - RateStat success = profile.getTunnelHistory().getProcessSuccessRate(); - RateStat failure = profile.getTunnelHistory().getProcessFailureRate(); - return messagesPerMinute(success, failure); - } - private double messagesPerMinute(RateStat success, RateStat failure) { - double rv = 0.0d; - if (success != null) { - Rate rate = null; - long periods[] = success.getPeriods(); - for (int i = 0; i < periods.length; i++) { - rate = success.getRate(periods[i]); - if ( (rate != null) && (rate.getCurrentTotalValue() > 0) ) - break; - } - - double value = rate.getCurrentTotalValue(); - value += rate.getLastTotalValue(); - rv = value * 10.0d * 60.0d * 1000.0d / (double)rate.getPeriod(); - - // if any of the messages are getting fragmented and cannot be - // handled, penalize like crazy - Rate fail = failure.getRate(rate.getPeriod()); - if (fail.getCurrentTotalValue() > 0) - rv /= fail.getCurrentTotalValue(); - } - return rv; - } - - /* - public double calc(PeerProfile profile) { - double successCount = countSuccesses(profile); - double failureCount = countFailures(profile); - - double rv = successCount - 5*failureCount; - if (rv < 0) - rv = 0; - return rv; - } - private double countSuccesses(PeerProfile profile) { - RateStat success = profile.getTunnelHistory().getProcessSuccessRate(); - return messagesPerMinute(success); - } - private double countFailures(PeerProfile profile) { - RateStat failure = profile.getTunnelHistory().getProcessFailureRate(); - return messagesPerMinute(failure); - } - private double messagesPerMinute(RateStat stat) { - double rv = 0.0d; - if (stat != null) { - Rate rate = null; - long periods[] = stat.getPeriods(); - for (int i = 0; i < periods.length; i++) { - rate = stat.getRate(periods[i]); - if ( (rate != null) && (rate.getCurrentTotalValue() > 0) ) - break; - } - - double value = rate.getCurrentTotalValue(); - value += rate.getLastTotalValue(); - rv = value * 60.0d * 1000.0d / (double)rate.getPeriod(); - } - return rv; - } - */ -} diff --git a/router/java/src/net/i2p/router/peermanager/TunnelHistory.java b/router/java/src/net/i2p/router/peermanager/TunnelHistory.java index 2ea23099d..d43aff6cd 100644 --- a/router/java/src/net/i2p/router/peermanager/TunnelHistory.java +++ b/router/java/src/net/i2p/router/peermanager/TunnelHistory.java @@ -26,8 +26,6 @@ public class TunnelHistory { private volatile long _lastFailed; private RateStat _rejectRate; private RateStat _failRate; - private RateStat _processSuccessRate; - private RateStat _processFailureRate; private String _statGroup; /** probabalistic tunnel rejection due to a flood of requests */ @@ -47,14 +45,10 @@ public class TunnelHistory { } private void createRates(String statGroup) { - _rejectRate = new RateStat("tunnelHistory.rejectRate", "How often does this peer reject a tunnel request?", statGroup, new long[] { 60*1000l, 10*60*1000l, 30*60*1000l, 60*60*1000l, 24*60*60*1000l }); - _failRate = new RateStat("tunnelHistory.failRate", "How often do tunnels this peer accepts fail?", statGroup, new long[] { 60*1000l, 10*60*1000l, 30*60*1000l, 60*60*1000l, 24*60*60*1000l }); - _processSuccessRate = new RateStat("tunnelHistory.processSuccessRate", "How many messages does a tunnel process?", statGroup, new long[] { 5*60*1000l, 10*60*1000l, 30*60*1000l, 60*60*1000l, 24*60*60*1000l }); - _processFailureRate = new RateStat("tunnelHistory.processfailureRate", "How many messages does a tunnel fail?", statGroup, new long[] { 5*60*1000l, 10*60*1000l, 30*60*1000l, 60*60*1000l, 24*60*60*1000l }); + _rejectRate = new RateStat("tunnelHistory.rejectRate", "How often does this peer reject a tunnel request?", statGroup, new long[] { 10*60*1000l, 30*60*1000l, 60*60*1000l, 24*60*60*1000l }); + _failRate = new RateStat("tunnelHistory.failRate", "How often do tunnels this peer accepts fail?", statGroup, new long[] { 10*60*1000l, 30*60*1000l, 60*60*1000l, 24*60*60*1000l }); _rejectRate.setStatLog(_context.statManager().getStatLog()); _failRate.setStatLog(_context.statManager().getStatLog()); - _processSuccessRate.setStatLog(_context.statManager().getStatLog()); - _processFailureRate.setStatLog(_context.statManager().getStatLog()); } /** total tunnels the peer has agreed to participate in */ @@ -77,10 +71,7 @@ public class TunnelHistory { public long getLastFailed() { return _lastFailed; } public void incrementProcessed(int processedSuccessfully, int failedProcessing) { - if (processedSuccessfully > 0) - _processSuccessRate.addData(processedSuccessfully, 0); - if (failedProcessing > 0) - _processFailureRate.addData(failedProcessing, 0); + // old strict speed calculator } public void incrementAgreedTo() { @@ -129,16 +120,12 @@ public class TunnelHistory { public RateStat getRejectionRate() { return _rejectRate; } public RateStat getFailedRate() { return _failRate; } - public RateStat getProcessSuccessRate() { return _processSuccessRate; } - public RateStat getProcessFailureRate() { return _processFailureRate; } public void coalesceStats() { if (_log.shouldLog(Log.DEBUG)) _log.debug("Coallescing stats"); _rejectRate.coalesceStats(); _failRate.coalesceStats(); - _processFailureRate.coalesceStats(); - _processSuccessRate.coalesceStats(); } private final static String NL = System.getProperty("line.separator"); @@ -161,8 +148,6 @@ public class TunnelHistory { out.write(buf.toString().getBytes()); _rejectRate.store(out, "tunnelHistory.rejectRate"); _failRate.store(out, "tunnelHistory.failRate"); - _processSuccessRate.store(out, "tunnelHistory.processSuccessRate"); - _processFailureRate.store(out, "tunnelHistory.processFailureRate"); } private void add(StringBuffer buf, String name, long val, String description) { @@ -187,12 +172,6 @@ public class TunnelHistory { _failRate.load(props, "tunnelHistory.failRate", true); if (_log.shouldLog(Log.DEBUG)) _log.debug("Loading tunnelHistory.failRate"); - _processFailureRate.load(props, "tunnelHistory.processFailureRate", true); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Loading tunnelHistory.processFailureRate"); - _processSuccessRate.load(props, "tunnelHistory.processSuccessRate", true); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Loading tunnelHistory.processSuccessRate"); } catch (IllegalArgumentException iae) { _log.warn("TunnelHistory rates are corrupt, resetting", iae); createRates(_statGroup); From 49c7fc30c09ab42ce4741d2177d65e54bc461e99 Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 2 Apr 2009 19:01:26 +0000 Subject: [PATCH 076/688] cleanup --- apps/i2ptunnel/jsp/editServer.jsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/i2ptunnel/jsp/editServer.jsp b/apps/i2ptunnel/jsp/editServer.jsp index 59a7b9cf7..3a958fd42 100644 --- a/apps/i2ptunnel/jsp/editServer.jsp +++ b/apps/i2ptunnel/jsp/editServer.jsp @@ -268,7 +268,7 @@
    From 3a1218283846c44fbc06591aba97dc9089110178 Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 2 Apr 2009 20:33:54 +0000 Subject: [PATCH 077/688] Transport: - Maintain a router hash -> IP map in transport, to support additional IP checks (unused for now) - Catch error on pre-2.6 kernels - Some concurrent conversion - Fix an HTML error on peers.jsp --- .../transport/CommSystemFacadeImpl.java | 4 +++ .../i2p/router/transport/TransportImpl.java | 34 ++++++++++++------- .../router/transport/TransportManager.java | 15 +++++++- .../router/transport/ntcp/EstablishState.java | 6 +++- .../router/transport/ntcp/EventPumper.java | 11 +++--- .../router/transport/ntcp/NTCPTransport.java | 7 +++- .../transport/udp/EstablishmentManager.java | 2 ++ .../router/transport/udp/UDPTransport.java | 3 ++ 8 files changed, 62 insertions(+), 20 deletions(-) diff --git a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java index c94178004..0eafb8fa9 100644 --- a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java +++ b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java @@ -133,6 +133,10 @@ public class CommSystemFacadeImpl extends CommSystemFacade { return _manager.wasUnreachable(dest); } + public byte[] getIP(Hash dest) { + return _manager.getIP(dest); + } + public List getMostRecentErrorMessages() { return _manager.getMostRecentErrorMessages(); } diff --git a/router/java/src/net/i2p/router/transport/TransportImpl.java b/router/java/src/net/i2p/router/transport/TransportImpl.java index 04a2cde91..1dffde799 100644 --- a/router/java/src/net/i2p/router/transport/TransportImpl.java +++ b/router/java/src/net/i2p/router/transport/TransportImpl.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.Vector; +import java.util.concurrent.ConcurrentHashMap; import net.i2p.data.Hash; import net.i2p.data.RouterAddress; @@ -34,6 +35,7 @@ import net.i2p.router.OutNetMessage; import net.i2p.router.Router; import net.i2p.router.RouterContext; import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade; +import net.i2p.util.ConcurrentHashSet; import net.i2p.util.Log; /** @@ -47,8 +49,10 @@ public abstract class TransportImpl implements Transport { private List _sendPool; protected RouterContext _context; /** map from routerIdentHash to timestamp (Long) that the peer was last unreachable */ - private Map _unreachableEntries; - private Set _wasUnreachableEntries; + private Map _unreachableEntries; + private Set _wasUnreachableEntries; + /** global router ident -> IP */ + private static Map _IPMap = new ConcurrentHashMap(128); /** * Initialize the new transport @@ -67,7 +71,7 @@ public abstract class TransportImpl implements Transport { _context.statManager().createRateStat("transport.expiredOnQueueLifetime", "How long a message that expires on our outbound queue is processed", "Transport", new long[] { 60*1000l, 10*60*1000l, 60*60*1000l, 24*60*60*1000l } ); _sendPool = new ArrayList(16); _unreachableEntries = new HashMap(16); - _wasUnreachableEntries = new HashSet(16); + _wasUnreachableEntries = new ConcurrentHashSet(16); _currentAddress = null; } @@ -483,10 +487,8 @@ public abstract class TransportImpl implements Transport { * This is NOT reset if the peer contacts us and it is never expired. */ public boolean wasUnreachable(Hash peer) { - synchronized (_wasUnreachableEntries) { - if (_wasUnreachableEntries.contains(peer)) - return true; - } + if (_wasUnreachableEntries.contains(peer)) + return true; RouterInfo ri = _context.netDb().lookupRouterInfoLocally(peer); if (ri == null) return false; @@ -496,16 +498,22 @@ public abstract class TransportImpl implements Transport { * Maintain the WasUnreachable list */ public void markWasUnreachable(Hash peer, boolean yes) { - synchronized (_wasUnreachableEntries) { - if (yes) - _wasUnreachableEntries.add(peer); - else - _wasUnreachableEntries.remove(peer); - } + if (yes) + _wasUnreachableEntries.add(peer); + else + _wasUnreachableEntries.remove(peer); if (_log.shouldLog(Log.WARN)) _log.warn(this.getStyle() + " setting wasUnreachable to " + yes + " for " + peer); } + public static void setIP(Hash peer, byte[] ip) { + _IPMap.put(peer, ip); + } + + public static byte[] getIP(Hash peer) { + return _IPMap.get(peer); + } + public static boolean isPubliclyRoutable(byte addr[]) { if (addr.length == 4) { if ((addr[0]&0xFF) == 127) return false; diff --git a/router/java/src/net/i2p/router/transport/TransportManager.java b/router/java/src/net/i2p/router/transport/TransportManager.java index bade75913..112f51efa 100644 --- a/router/java/src/net/i2p/router/transport/TransportManager.java +++ b/router/java/src/net/i2p/router/transport/TransportManager.java @@ -33,7 +33,7 @@ import net.i2p.util.Log; public class TransportManager implements TransportEventListener { private Log _log; - private List _transports; + private List _transports; private RouterContext _context; private final static String PROP_ENABLE_UDP = "i2np.udp.enable"; @@ -229,6 +229,19 @@ public class TransportManager implements TransportEventListener { return true; } + /** + * IP of the peer from the last connection (in or out, any transport). + * This may be different from that advertised in the netDb, + * as the peer may be hidden, or connect from a different IP, or + * change his netDb later, in an attempt to avoid restrictions. + * + * For blocking purposes, etc. it's worth checking both + * the netDb addresses and this address. + */ + public byte[] getIP(Hash dest) { + return TransportImpl.getIP(dest); + } + Map getAddresses() { Map rv = new HashMap(_transports.size()); for (int i = 0; i < _transports.size(); i++) { diff --git a/router/java/src/net/i2p/router/transport/ntcp/EstablishState.java b/router/java/src/net/i2p/router/transport/ntcp/EstablishState.java index d18cc1ad3..942d6cbc9 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/EstablishState.java +++ b/router/java/src/net/i2p/router/transport/ntcp/EstablishState.java @@ -472,6 +472,8 @@ public class EstablishState { byte nextReadIV[] = new byte[16]; System.arraycopy(_e_bobSig, _e_bobSig.length-16, nextReadIV, 0, nextReadIV.length); _con.finishOutboundEstablishment(_dh.getSessionKey(), (_tsA-_tsB), nextWriteIV, nextReadIV); // skew in seconds + _transport.setIP(_con.getRemotePeer().calculateHash(), + _con.getChannel().socket().getInetAddress().getAddress()); return; } } @@ -546,15 +548,17 @@ public class EstablishState { Signature sig = new Signature(s); _verified = _context.dsa().verifySignature(sig, toVerify, alice.getSigningPublicKey()); if (_verified) { + byte[] ip = _con.getChannel().socket().getInetAddress().getAddress(); if (_context.shitlist().isShitlistedForever(alice.calculateHash())) { if (_log.shouldLog(Log.WARN)) _log.warn("Dropping inbound connection from permanently shitlisted peer: " + alice.calculateHash().toBase64()); // So next time we will not accept the con from this IP, // rather than doing the whole handshake - _context.blocklist().add(_con.getChannel().socket().getInetAddress().getAddress()); + _context.blocklist().add(ip); fail("Peer is shitlisted forever: " + alice.calculateHash().toBase64()); return; } + _transport.setIP(alice.calculateHash(), ip); if (_log.shouldLog(Log.DEBUG)) _log.debug(prefix() + "verification successful for " + _con); diff --git a/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java b/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java index 9c75f5328..0c062fa6c 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java +++ b/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java @@ -66,22 +66,25 @@ public class EventPumper implements Runnable { public void startPumping() { if (_log.shouldLog(Log.INFO)) _log.info("Starting pumper"); - _alive = true; _wantsRead = new ArrayList(16); _wantsWrite = new ArrayList(4); _wantsRegister = new ArrayList(1); _wantsConRegister = new ArrayList(4); try { _selector = Selector.open(); + _alive = true; + new I2PThread(this, "NTCP Pumper", true).start(); } catch (IOException ioe) { - _log.error("Error opening the selector", ioe); + _log.log(Log.CRIT, "Error opening the NTCP selector", ioe); + } catch (java.lang.InternalError jlie) { + // "unable to get address of epoll functions, pre-2.6 kernel?" + _log.log(Log.CRIT, "Error opening the NTCP selector", jlie); } - new I2PThread(this, "NTCP Pumper", true).start(); } public void stopPumping() { _alive = false; - if (_selector.isOpen()) + if (_selector != null && _selector.isOpen()) _selector.wakeup(); } diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java index c23245bae..103b46b15 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java +++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java @@ -241,6 +241,8 @@ public class NTCPTransport extends TransportImpl { super.afterSend(msg, sendSuccessful, allowRequeue, msToSend); } public TransportBid bid(RouterInfo toAddress, long dataSize) { + if (!isAlive()) + return null; Hash peer = toAddress.getIdentity().calculateHash(); if (_context.shitlist().isShitlisted(peer, STYLE)) { // we aren't shitlisted in general (since we are trying to get a bid), but we have @@ -591,7 +593,10 @@ public class NTCPTransport extends TransportImpl { for (Iterator iter = peers.iterator(); iter.hasNext(); ) { NTCPConnection con = (NTCPConnection)iter.next(); String name = con.getRemotePeer().calculateHash().toBase64().substring(0,6); - buf.append("").append(name); + buf.append("").append(name).append(""); + //byte[] ip = getIP(con.getRemotePeer().calculateHash()); + //if (ip != null) + // buf.append(' ').append(_context.blocklist().toStr(ip)); buf.append(""); if (con.isInbound()) buf.append("in"); diff --git a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java index 896fe1ce4..c5b654b90 100644 --- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java +++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java @@ -450,6 +450,7 @@ public class EstablishmentManager { _transport.addRemotePeerState(peer); _transport.inboundConnectionReceived(); + _transport.setIP(remote.calculateHash(), state.getSentIP()); _context.statManager().addRateData("udp.inboundEstablishTime", state.getLifetime(), 0); sendInboundComplete(peer); @@ -531,6 +532,7 @@ public class EstablishmentManager { _transport.addRemotePeerState(peer); + _transport.setIP(remote.calculateHash(), state.getSentIP()); _context.statManager().addRateData("udp.outboundEstablishTime", state.getLifetime(), 0); sendOurInfo(peer, false); diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java index e5185defa..289e42ba3 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -1762,6 +1762,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority buf.append(" [shitlisted]"); appended = true; } + //byte[] ip = getIP(peer.getRemotePeer()); + //if (ip != null) + // buf.append(' ').append(_context.blocklist().toStr(ip)); buf.append(""); long idleIn = (now-peer.getLastReceiveTime())/1000; From d2fc3972955e686d61506d9295378468e60ae7cc Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 2 Apr 2009 21:00:26 +0000 Subject: [PATCH 078/688] -3 --- history.txt | 22 +++++++++++++++++++ .../src/net/i2p/router/RouterVersion.java | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/history.txt b/history.txt index 9ac00e87b..789aca373 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,25 @@ +2009-04-02 zzz + * Profiles: + - Remove unused calculators and RateStats: + CapacityCalculator, StrictSpeedCalculator, IsFailingCalculator; + sendFailureSize, processSuccessRate, processfailureRate, commErrorRate, + tunnelTestResponseTimeSlow + - Reduced number of Rates in these RateStats: + sendSuccessSize, receiveSize, rejectRate, failRate + - ~5KB/profile savings total + - Deflate speed calculation once an hour instead of once a day, + to improve fast tier selection + - Remove dup comment in persisted files + * StatisticsManager - effective in 0.7.2: + - Spoof uptime to 90m for all + - Change tunnel stats from 10m to 60m + * Transport: + - Maintain a router hash -> IP map in transport, + to support additional IP checks (unused for now) + - Catch error on pre-2.6 kernels + - Some concurrent conversion + - Fix an HTML error on peers.jsp + 2009-04-01 zzz * I2PTunnel: Fix tunnel close http://forum.i2p/viewtopic.php?t=3231 diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index a727710d6..a34e9238c 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -17,7 +17,7 @@ import net.i2p.CoreVersion; public class RouterVersion { public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 2; + public final static long BUILD = 3; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID); From 1aa7fbbba056e9484123dcb119f894f9356ef9a8 Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 2 Apr 2009 22:07:25 +0000 Subject: [PATCH 079/688] -3 didnt build --- router/java/src/net/i2p/router/RouterContext.java | 6 ------ router/java/src/net/i2p/router/peermanager/PeerProfile.java | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/router/java/src/net/i2p/router/RouterContext.java b/router/java/src/net/i2p/router/RouterContext.java index e3b5e3846..ba82fb839 100644 --- a/router/java/src/net/i2p/router/RouterContext.java +++ b/router/java/src/net/i2p/router/RouterContext.java @@ -12,7 +12,6 @@ import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade; import net.i2p.router.peermanager.Calculator; import net.i2p.router.peermanager.CapacityCalculator; import net.i2p.router.peermanager.IntegrationCalculator; -import net.i2p.router.peermanager.IsFailingCalculator; import net.i2p.router.peermanager.PeerManagerFacadeImpl; import net.i2p.router.peermanager.ProfileManagerImpl; import net.i2p.router.peermanager.ProfileOrganizer; @@ -60,7 +59,6 @@ public class RouterContext extends I2PAppContext { private MessageStateMonitor _messageStateMonitor; private RouterThrottle _throttle; private RouterClock _clock; - private Calculator _isFailingCalc; private Calculator _integrationCalc; private Calculator _speedCalc; private Calculator _capacityCalc; @@ -128,7 +126,6 @@ public class RouterContext extends I2PAppContext { _messageValidator = new MessageValidator(this); //_throttle = new RouterThrottleImpl(this); _throttle = new RouterDoSThrottle(this); - _isFailingCalc = new IsFailingCalculator(this); _integrationCalc = new IntegrationCalculator(this); _speedCalc = new SpeedCalculator(this); _capacityCalc = new CapacityCalculator(this); @@ -258,8 +255,6 @@ public class RouterContext extends I2PAppContext { */ public RouterThrottle throttle() { return _throttle; } - /** how do we rank the failure of profiles? */ - public Calculator isFailingCalculator() { return _isFailingCalc; } /** how do we rank the integration of profiles? */ public Calculator integrationCalculator() { return _integrationCalc; } /** how do we rank the speed of profiles? */ @@ -289,7 +284,6 @@ public class RouterContext extends I2PAppContext { buf.append(_statPublisher).append('\n'); buf.append(_shitlist).append('\n'); buf.append(_messageValidator).append('\n'); - buf.append(_isFailingCalc).append('\n'); buf.append(_integrationCalc).append('\n'); buf.append(_speedCalc).append('\n'); return buf.toString(); diff --git a/router/java/src/net/i2p/router/peermanager/PeerProfile.java b/router/java/src/net/i2p/router/peermanager/PeerProfile.java index 5c058b493..2f311b52f 100644 --- a/router/java/src/net/i2p/router/peermanager/PeerProfile.java +++ b/router/java/src/net/i2p/router/peermanager/PeerProfile.java @@ -442,7 +442,7 @@ public class PeerProfile { private double calculateSpeed() { return _context.speedCalculator().calc(this); } private double calculateCapacity() { return _context.capacityCalculator().calc(this); } private double calculateIntegration() { return _context.integrationCalculator().calc(this); } - private boolean calculateIsFailing() { return _context.isFailingCalculator().calcBoolean(this); } + private boolean calculateIsFailing() { return false; } void setIsFailing(boolean val) { _isFailing = val; } public int hashCode() { return (_peer == null ? 0 : _peer.hashCode()); } From c7d815b5b886131de11b1ee2244a058dd8919761 Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 2 Apr 2009 22:08:39 +0000 Subject: [PATCH 080/688] -4 (-3 didnt build) --- router/java/src/net/i2p/router/RouterVersion.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index a34e9238c..5f58c2cdd 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -17,7 +17,7 @@ import net.i2p.CoreVersion; public class RouterVersion { public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 3; + public final static long BUILD = 4; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID); From 25d5883a0e0bb88addf5b2ef445c9869ceffe110 Mon Sep 17 00:00:00 2001 From: sponge Date: Fri, 3 Apr 2009 13:31:41 +0000 Subject: [PATCH 081/688] 2009-04-03 sponge * Fix broken dependencies for BOB.jar --- apps/BOB/nbproject/project.properties | 41 ++++++++------------------- history.txt | 2 ++ 2 files changed, 14 insertions(+), 29 deletions(-) diff --git a/apps/BOB/nbproject/project.properties b/apps/BOB/nbproject/project.properties index 7a94ff6dd..ee493f764 100644 --- a/apps/BOB/nbproject/project.properties +++ b/apps/BOB/nbproject/project.properties @@ -24,40 +24,23 @@ dist.dir=dist dist.jar=${dist.dir}/BOB.jar dist.javadoc.dir=${dist.dir}/javadoc excludes= -file.reference.build-javadoc=../../../i2p.i2p/build/javadoc -file.reference.core.jar=../i2p.i2p/core/dist/core.jar -file.reference.i2p.jar=../../bob/i2p/i2p.i2p/build/i2p.jar -file.reference.i2p.jar-1=../../build/i2p.jar -file.reference.i2p.jar-2=../i2p.i2p/core/java/build/i2p.jar -file.reference.i2p.jar-3=../../../i2p.i2p/build/i2p.jar -file.reference.i2ptunnel.jar=../../../i2p.i2p/build/i2ptunnel.jar -file.reference.java-src=../i2p.i2p/core/java/src/ -file.reference.jbigi.jar=../../bob/i2p/i2p.i2p/build/jbigi.jar -file.reference.mstreaming.jar=../../bob/i2p/i2p.i2p/build/mstreaming.jar -file.reference.mstreaming.jar-1=../../build/mstreaming.jar -file.reference.mstreaming.jar-2=../../../i2p.i2p/build/mstreaming.jar -file.reference.NetBeansProjects-i2p.i2p=../i2p.i2p/ -file.reference.router.jar=../../build/router.jar -file.reference.streaming.jar=../../bob/i2p/i2p.i2p/build/streaming.jar -file.reference.streaming.jar-1=../../../i2p.i2p/build/streaming.jar -file.reference.wrapper-freebsd=../../installer/lib/wrapper/freebsd/ -file.reference.wrapper-linux=../../installer/lib/wrapper/linux/ -file.reference.wrapper-linux64=../../installer/lib/wrapper/linux64/ -file.reference.wrapper-macosx=../../installer/lib/wrapper/macosx/ -file.reference.wrapper-solaris=../../installer/lib/wrapper/solaris/ -file.reference.wrapper-win32=../../installer/lib/wrapper/win32/ +file.reference.build-javadoc=../../i2p.i2p/build/javadoc +file.reference.i2p.jar=../../core/java/build/i2p.jar +file.reference.i2ptunnel.jar=../i2ptunnel/java/build/i2ptunnel.jar +file.reference.mstreaming.jar=../ministreaming/java/build/mstreaming.jar +file.reference.router.jar=../../router/java/build/router.jar +file.reference.streaming.jar=../streaming/java/build/streaming.jar file.reference.wrapper.jar=../../installer/lib/wrapper/linux/wrapper.jar includes=** jar.compress=false javac.classpath=\ - ${file.reference.wrapper.jar}:\ - ${file.reference.streaming.jar-1}:\ - ${file.reference.i2ptunnel.jar}:\ - ${file.reference.i2p.jar-1}:\ ${file.reference.router.jar}:\ - ${file.reference.mstreaming.jar-1}:\ - ${file.reference.mstreaming.jar-2}:\ - ${file.reference.i2p.jar-3} + ${file.reference.i2ptunnel.jar}:\ + ${file.reference.mstreaming.jar}:\ + ${file.reference.streaming.jar}:\ + ${file.reference.wrapper.jar}:\ + ${file.reference.i2p.jar}:\ + ${file.reference.router.jar} # Space-separated list of extra javac options javac.compilerargs= javac.deprecation=false diff --git a/history.txt b/history.txt index 789aca373..a7bfb78b5 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,5 @@ +2009-04-03 sponge + * Fix broken dependencies for BOB.jar 2009-04-02 zzz * Profiles: - Remove unused calculators and RateStats: From 0c7cb9d78197a355784c60d595a9a76023bb1a58 Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 3 Apr 2009 14:26:48 +0000 Subject: [PATCH 082/688] put java version on logs.jsp --- apps/routerconsole/jsp/logs.jsp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/routerconsole/jsp/logs.jsp b/apps/routerconsole/jsp/logs.jsp index 6bfc9c469..36d3562fe 100644 --- a/apps/routerconsole/jsp/logs.jsp +++ b/apps/routerconsole/jsp/logs.jsp @@ -21,6 +21,12 @@

    Service (Wrapper) logs:

    +
    +

    Java Version:

    +
    +<%=System.getProperty("java.vendor")%> <%=System.getProperty("java.version")%>
    +<%=System.getProperty("os.name")%> <%=System.getProperty("os.arch")%> <%=System.getProperty("os.version")%>
    + 
    From e5b1450e83c44c6d4fc79af7ff55c77974816089 Mon Sep 17 00:00:00 2001 From: sponge Date: Fri, 3 Apr 2009 15:33:51 +0000 Subject: [PATCH 083/688] 2009-04-03 sponge * Router build version incremented to 5 now that the build succeeds. --- history.txt | 1 + router/java/src/net/i2p/router/RouterVersion.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/history.txt b/history.txt index a7bfb78b5..283be6c21 100644 --- a/history.txt +++ b/history.txt @@ -1,5 +1,6 @@ 2009-04-03 sponge * Fix broken dependencies for BOB.jar + * Router build version incremented to 5. 2009-04-02 zzz * Profiles: - Remove unused calculators and RateStats: diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 5f58c2cdd..5203f2360 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -17,7 +17,7 @@ import net.i2p.CoreVersion; public class RouterVersion { public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 4; + public final static long BUILD = 5; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID); From bb0531053dca4abe57978996b6c17ece81a8834d Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 3 Apr 2009 21:33:35 +0000 Subject: [PATCH 084/688] Console: Fix bug with IE buttons not working, because it sends the label instead of the value --- .../java/src/net/i2p/i2ptunnel/web/IndexBean.java | 2 +- .../src/net/i2p/router/web/ConfigClientsHandler.java | 12 ++++++++++++ .../src/net/i2p/router/web/ConfigClientsHelper.java | 6 +++--- .../src/net/i2p/router/web/ConfigRestartBean.java | 11 ++++++----- apps/routerconsole/jsp/configclients.jsp | 5 +++++ 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java index fcf45d94d..ce547cd02 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java @@ -142,7 +142,7 @@ public class IndexBean { } private String processAction() { - if ( (_action == null) || (_action.trim().length() <= 0) ) + if ( (_action == null) || (_action.trim().length() <= 0) || ("Cancel".equals(_action))) return ""; if ( (_prevNonce != _curNonce) && (!validPassphrase(_passphrase)) ) return "Invalid nonce, are you being spoofed?"; diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java index 7af6125d6..b612c005b 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java @@ -41,6 +41,18 @@ public class ConfigClientsHandler extends FormHandler { startClient(appnum); else startWebApp(app); + } else if (_action.toLowerCase().startsWith("start ") && + _action.toLowerCase().endsWith("")) { + // IE sucks + String app = _action.substring(23, _action.length() - 7); + int appnum = -1; + try { + appnum = Integer.parseInt(app); + } catch (NumberFormatException nfe) {} + if (appnum >= 0) + startClient(appnum); + else + startWebApp(app); } else { addFormError("Unsupported " + _action); } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java index 2bee43533..984569b96 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java @@ -62,10 +62,10 @@ public class ConfigClientsHelper extends HelperBase { if (ro) buf.append("disabled=\"true\" "); } - buf.append("/> "); + buf.append("/> "); if (!enabled) { - buf.append(""); + buf.append(""); } - buf.append(" ").append(desc).append("\n"); + buf.append(" ").append(desc).append("\n"); } } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigRestartBean.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigRestartBean.java index e8eb6b26d..7ae7181bc 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigRestartBean.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigRestartBean.java @@ -24,20 +24,21 @@ public class ConfigRestartBean { RouterContext ctx = ContextHelper.getContext(null); String systemNonce = getNonce(); if ( (nonce != null) && (systemNonce.equals(nonce)) && (action != null) ) { - if ("shutdownImmediate".equals(action)) { + // Normal browsers send value, IE sends button label + if ("shutdownImmediate".equals(action) || "Shutdown immediately".equals(action)) { ctx.addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_HARD)); //ctx.router().shutdown(Router.EXIT_HARD); // never returns ctx.router().shutdownGracefully(Router.EXIT_HARD); // give the UI time to respond - } else if ("cancelShutdown".equals(action)) { + } else if ("cancelShutdown".equals(action) || "Cancel shutdown".equals(action)) { ctx.router().cancelGracefulShutdown(); - } else if ("restartImmediate".equals(action)) { + } else if ("restartImmediate".equals(action) || "Restart immediately".equals(action)) { ctx.addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_HARD_RESTART)); //ctx.router().shutdown(Router.EXIT_HARD_RESTART); // never returns ctx.router().shutdownGracefully(Router.EXIT_HARD_RESTART); // give the UI time to respond - } else if ("restart".equals(action)) { + } else if ("restart".equalsIgnoreCase(action)) { ctx.addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_GRACEFUL_RESTART)); ctx.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART); - } else if ("shutdown".equals(action)) { + } else if ("shutdown".equalsIgnoreCase(action)) { ctx.addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_GRACEFUL)); ctx.router().shutdownGracefully(); } diff --git a/apps/routerconsole/jsp/configclients.jsp b/apps/routerconsole/jsp/configclients.jsp index 724028198..8e66cb54e 100644 --- a/apps/routerconsole/jsp/configclients.jsp +++ b/apps/routerconsole/jsp/configclients.jsp @@ -5,6 +5,11 @@ I2P Router Console - config clients + <%@include file="nav.jsp" %> From 8de560981709863847cfc90755de697a3d56dc40 Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 3 Apr 2009 21:42:48 +0000 Subject: [PATCH 085/688] * Update: - Change default to "Download and verify" - Change news fetch default to 24h (was 12h) --- .../src/net/i2p/router/web/ConfigUpdateHandler.java | 4 ++-- .../java/src/net/i2p/router/web/UpdateHandler.java | 2 ++ core/java/src/net/i2p/crypto/TrustedUpdate.java | 13 +++++++++---- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java index 818b748a7..3c6e1692c 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java @@ -21,9 +21,9 @@ public class ConfigUpdateHandler extends FormHandler { // public static final String DEFAULT_NEWS_URL = "http://dev.i2p.net/cgi-bin/cvsweb.cgi/i2p/news.xml?rev=HEAD"; public static final String DEFAULT_NEWS_URL = "http://complication.i2p/news.xml"; public static final String PROP_REFRESH_FREQUENCY = "router.newsRefreshFrequency"; - public static final String DEFAULT_REFRESH_FREQUENCY = 12*60*60*1000 + ""; + public static final String DEFAULT_REFRESH_FREQUENCY = 24*60*60*1000 + ""; public static final String PROP_UPDATE_POLICY = "router.updatePolicy"; - public static final String DEFAULT_UPDATE_POLICY = "notify"; + public static final String DEFAULT_UPDATE_POLICY = "download"; public static final String PROP_SHOULD_PROXY = "router.updateThroughProxy"; public static final String DEFAULT_SHOULD_PROXY = Boolean.TRUE.toString(); public static final String PROP_PROXY_HOST = "router.updateProxyHost"; diff --git a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java index 83495f33e..81f8d6e8d 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java @@ -166,6 +166,8 @@ public class UpdateHandler { } else { _log.log(Log.CRIT, "Update was VERIFIED, will be installed at next restart"); _status = "Update downloaded
    Click Restart to Install"; + if (up.newVersion() != null) + _status += " Version " + up.newVersion(); } } else { err = err + " from " + url; diff --git a/core/java/src/net/i2p/crypto/TrustedUpdate.java b/core/java/src/net/i2p/crypto/TrustedUpdate.java index dfd055382..06e37544b 100644 --- a/core/java/src/net/i2p/crypto/TrustedUpdate.java +++ b/core/java/src/net/i2p/crypto/TrustedUpdate.java @@ -108,6 +108,7 @@ D8usM7Dxp5yrDrCYZ5AIijc= private Log _log; private ArrayList _trustedKeys; + private String _newVersion; /** * Constructs a new TrustedUpdate with the default global @@ -127,6 +128,7 @@ D8usM7Dxp5yrDrCYZ5AIijc= _context = context; _log = _context.logManager().getLog(TrustedUpdate.class); _trustedKeys = new ArrayList(); + _newVersion = null; String propertyTrustedKeys = context.getProperty(PROP_TRUSTED_KEYS); @@ -379,6 +381,11 @@ D8usM7Dxp5yrDrCYZ5AIijc= } } + /** version in the .sud file, valid only after calling migrateVerified() */ + public String newVersion() { + return _newVersion; + } + /** * Verifies that the version of the given signed update file is newer than * currentVersion. @@ -390,10 +397,8 @@ D8usM7Dxp5yrDrCYZ5AIijc= * than the current version, otherwise false. */ public boolean isUpdatedVersion(String currentVersion, String signedFile) { - if (needsUpdate(currentVersion, getVersionString(signedFile))) - return true; - else - return false; + _newVersion = getVersionString(signedFile); + return needsUpdate(currentVersion, getVersionString(signedFile)); } /** From fe9b891b37d8775ffb95a7f58e4c4cdaa6649149 Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 3 Apr 2009 21:46:10 +0000 Subject: [PATCH 086/688] -6 --- history.txt | 10 ++++++++++ router/java/src/net/i2p/router/RouterVersion.java | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/history.txt b/history.txt index 283be6c21..b4ed77480 100644 --- a/history.txt +++ b/history.txt @@ -1,6 +1,16 @@ +2009-04-03 zzz + * Console: + - Fix bug with IE buttons not working, + because it sends the label instead of the value + - Display version of downloaded update + * Update: + - Change default to "Download and verify" + - Change news fetch default to 24h (was 12h) + 2009-04-03 sponge * Fix broken dependencies for BOB.jar * Router build version incremented to 5. + 2009-04-02 zzz * Profiles: - Remove unused calculators and RateStats: diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 5203f2360..c08948969 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -17,7 +17,7 @@ import net.i2p.CoreVersion; public class RouterVersion { public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 5; + public final static long BUILD = 6; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID); From de6edc6a99d1c2b41f8a0cdb58703e39523eb183 Mon Sep 17 00:00:00 2001 From: mkvore-commit Date: Sat, 4 Apr 2009 10:28:31 +0000 Subject: [PATCH 087/688] --- .../i2p/client/streaming/I2PServerSocket.java | 4 +- .../client/streaming/I2PServerSocketImpl.java | 37 ++++++++++--------- apps/sam/Demos/datagramTests/samForward.py | 2 +- .../src/net/i2p/sam/SAMv3StreamSession.java | 6 +-- .../client/streaming/ConnectionHandler.java | 27 +++++++++++--- .../client/streaming/I2PServerSocketFull.java | 26 +++++-------- 6 files changed, 55 insertions(+), 47 deletions(-) diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocket.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocket.java index e7db251fa..6d6cfdc15 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocket.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocket.java @@ -44,7 +44,7 @@ public interface I2PServerSocket { * @throws ConnectException if the I2PServerSocket is closed * @throws SocketTimeoutException */ - public I2PSocket accept(boolean blocking) throws I2PException, ConnectException, SocketTimeoutException; + public I2PSocket accept(long timeout) throws I2PException, ConnectException, SocketTimeoutException, InterruptedException; /** * Waits until there is a socket waiting for acception or the timeout is @@ -58,7 +58,7 @@ public interface I2PServerSocket { * from the data available (aka the I2PSession closed, etc) * @throws ConnectException if the I2PServerSocket is closed */ - public boolean waitIncoming(long timeoutMs) throws I2PException, ConnectException, InterruptedException; + public void waitIncoming(long timeoutMs) throws I2PException, ConnectException, InterruptedException; /** * Set Sock Option accept timeout diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocketImpl.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocketImpl.java index a2c075e3e..59459286e 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocketImpl.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocketImpl.java @@ -60,28 +60,24 @@ class I2PServerSocketImpl implements I2PServerSocket { * * @param timeoutMs timeout in ms. A negative value waits forever. * - * @return true if a socket is available, false if not - * * @throws I2PException if there is a problem with reading a new socket * from the data available (aka the I2PSession closed, etc) * @throws ConnectException if the I2PServerSocket is closed */ - public boolean waitIncoming(long timeoutMs) throws I2PException, ConnectException { + public void waitIncoming(long timeoutMs) throws I2PException, ConnectException, InterruptedException { if (_log.shouldLog(Log.DEBUG)) _log.debug("waitIncoming() called, pending: " + pendingSockets.size()); - boolean isTimed = (timeoutMs>=0); + boolean isTimed = (timeoutMs>0); if (isTimed) { Clock clock = I2PAppContext.getGlobalContext().clock(); long now = clock.now(); long end = now + timeoutMs; while (pendingSockets.size() <= 0 && now0); } @@ -112,16 +107,20 @@ class I2PServerSocketImpl implements I2PServerSocket { * @throws ConnectException if the I2PServerSocket is closed */ - public I2PSocket accept(boolean blocking) throws I2PException, ConnectException { + public I2PSocket accept(long timeout) throws I2PException, ConnectException, InterruptedException { I2PSocket ret = null; - if (blocking) { + if (timeout<=0) { ret = accept(); } else { + long now = I2PAppContext.getGlobalContext().clock().now(); + long expiration = timeout + now ; synchronized (pendingSockets) { - if (pendingSockets.size() > 0) { - ret = (I2PSocket)pendingSockets.remove(0); + while (pendingSockets.size() == 0 && expiration>now) { + pendingSockets.wait(expiration-now); + now = I2PAppContext.getGlobalContext().clock().now(); } + ret = (I2PSocket)pendingSockets.remove(0); } if (ret != null) { synchronized (socketAcceptedLock) { @@ -151,10 +150,12 @@ class I2PServerSocketImpl implements I2PServerSocket { I2PSocket ret = null; while ( (ret == null) && (!closing) ){ - - this.waitIncoming(-1); - - ret = accept(false); + try { + this.waitIncoming(0); + ret = accept(1); + } catch (InterruptedException e) { + throw new I2PException("Thread interrupted") ; + } } if (_log.shouldLog(Log.DEBUG)) diff --git a/apps/sam/Demos/datagramTests/samForward.py b/apps/sam/Demos/datagramTests/samForward.py index 56590e7ef..1706b3fa5 100755 --- a/apps/sam/Demos/datagramTests/samForward.py +++ b/apps/sam/Demos/datagramTests/samForward.py @@ -16,7 +16,7 @@ else : if len(sys.argv)==3 : name = sys.argv[2] else : - name = "essaiSamForward" + name = "datagramSamForward" sess = socket.socket( socket.AF_INET, socket.SOCK_STREAM) diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java b/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java index b9ea710c6..ca83e8a1f 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java +++ b/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java @@ -269,10 +269,9 @@ public class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handle { while (session.socketServer!=null) { - boolean available = false ; I2PSocket i2ps = null ; try { - available = session.socketServer.waitIncoming(-1); + session.socketServer.waitIncoming(0); } catch (ConnectException e) { _log.debug("ConnectException"); break ; @@ -283,7 +282,6 @@ public class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handle _log.debug("InterruptedException"); break ; } - if ( !available ) continue ; java.net.InetSocketAddress addr = new java.net.InetSocketAddress(host,port); @@ -296,7 +294,7 @@ public class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handle } try { - i2ps = session.socketServer.accept(false); + i2ps = session.socketServer.accept(1); } catch (Exception e) {} if (i2ps==null) { diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java index 37a1f0d7c..9dd6aee41 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java @@ -23,6 +23,7 @@ class ConnectionHandler { private Log _log; private ConnectionManager _manager; private LinkedBlockingQueue _synQueue; + private Object _synSignal; private boolean _active; private int _acceptTimeout; @@ -81,7 +82,13 @@ class ConnectionHandler { boolean success = _synQueue.offer(packet); // fail immediately if full if (success) { SimpleScheduler.getInstance().addEvent(new TimeoutSyn(packet), _acceptTimeout); - } else { + if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) + synchronized (this._synSignal) + { + this._synSignal.notifyAll(); + } + + } else { if (_log.shouldLog(Log.WARN)) _log.warn("Dropping new SYN request, as the queue is full"); if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) @@ -89,8 +96,17 @@ class ConnectionHandler { } } - public boolean waitSyn( long ms ) throws InterruptedException { - throw new InterruptedException(); + /** + * Wait until some SYN packet is available + * @param ms max amount of time to wait for a connection (if negative or null, + * wait indefinitely) + * @throws InterruptedException + */ + public void waitSyn( long ms ) throws InterruptedException { + synchronized (this._synSignal) + { + this._synSignal.wait(ms); + } } /** @@ -120,6 +136,9 @@ class ConnectionHandler { return null; } + if ( (timeoutMs > 0) && (expiration < _context.clock().now()) ) + return null; + Packet syn = null; while ( _active && syn == null) { if (_log.shouldLog(Log.DEBUG)) @@ -162,8 +181,6 @@ class ConnectionHandler { } } // keep looping... - if ( (timeoutMs >= 0) && (expiration < _context.clock().now()) ) - return null; } } diff --git a/apps/streaming/java/src/net/i2p/client/streaming/I2PServerSocketFull.java b/apps/streaming/java/src/net/i2p/client/streaming/I2PServerSocketFull.java index ab9cb1c9d..8fe7fa9ce 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/I2PServerSocketFull.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/I2PServerSocketFull.java @@ -65,28 +65,20 @@ public class I2PServerSocketFull implements I2PServerSocket { * @throws SocketTimeoutException if the timeout has been reached */ - public I2PSocket accept(boolean blocking) throws I2PException, SocketTimeoutException { - long timeout = this.getSoTimeout(); + public I2PSocket accept(long timeout) throws I2PException { + long reset_timeout = this.getSoTimeout(); try { - if (blocking) - { - this.setSoTimeout(-1); - } else { - this.setSoTimeout(0); - } - try { - return this.accept(); - } catch (SocketTimeoutException e) { - if (blocking) throw e; - else return null ; - } - } finally { this.setSoTimeout(timeout); + return this.accept(); + } catch (SocketTimeoutException e) { + return null ; + } finally { + this.setSoTimeout(reset_timeout); } } - public boolean waitIncoming(long timeoutMs) throws InterruptedException { - return this._socketManager.getConnectionManager().getConnectionHandler().waitSyn(timeoutMs); + public void waitIncoming(long timeoutMs) throws InterruptedException { + this._socketManager.getConnectionManager().getConnectionHandler().waitSyn(timeoutMs); } } From 2cf52216208284945fb62e432acf69d01f32598b Mon Sep 17 00:00:00 2001 From: mkvore-commit Date: Sat, 4 Apr 2009 12:37:19 +0000 Subject: [PATCH 088/688] minimize differences with mainstream apps/streaming --- .../i2p/client/streaming/I2PServerSocket.java | 17 ++++---- .../client/streaming/I2PServerSocketImpl.java | 17 +++----- .../client/streaming/ConnectionHandler.java | 41 +++++++++++-------- .../client/streaming/I2PServerSocketFull.java | 23 +++++++---- 4 files changed, 54 insertions(+), 44 deletions(-) diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocket.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocket.java index 6d6cfdc15..733958736 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocket.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocket.java @@ -31,32 +31,33 @@ public interface I2PServerSocket { public I2PSocket accept() throws I2PException, ConnectException, SocketTimeoutException; /** - * accept(true) has the same behaviour as accept(). - * accept(false) does not wait for a socket connecting. If a socket is - * available in the queue, it is accepted. Else, null is returned. + * accept(timeout) waits timeout ms for a socket connecting. If a socket is + * not available during the timeout, return null. accept(0) behaves like accept() * - * @param true if the call should block until a socket is available + * @param timeout in ms * * @return a connected I2PSocket, or null * * @throws I2PException if there is a problem with reading a new socket * from the data available (aka the I2PSession closed, etc) * @throws ConnectException if the I2PServerSocket is closed - * @throws SocketTimeoutException + * @throws InterruptedException if thread is interrupted while waiting */ - public I2PSocket accept(long timeout) throws I2PException, ConnectException, SocketTimeoutException, InterruptedException; + public I2PSocket accept(long timeout) throws I2PException, ConnectException, InterruptedException; /** - * Waits until there is a socket waiting for acception or the timeout is + * Wait until there is a socket waiting for acception or the timeout is * reached. * - * @param timeoutMs timeout in ms. A negative value waits forever. + * @param timeoutMs timeout in ms. If ms is 0, wait forever. * * @return true if a socket is available, false if not * * @throws I2PException if there is a problem with reading a new socket * from the data available (aka the I2PSession closed, etc) * @throws ConnectException if the I2PServerSocket is closed + * @throws InterruptedException if the thread is interrupted before + * completion */ public void waitIncoming(long timeoutMs) throws I2PException, ConnectException, InterruptedException; diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocketImpl.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocketImpl.java index 59459286e..2da8320fc 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocketImpl.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PServerSocketImpl.java @@ -1,6 +1,7 @@ package net.i2p.client.streaming; import java.net.ConnectException; +import java.net.SocketTimeoutException; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -49,11 +50,6 @@ class I2PServerSocketImpl implements I2PServerSocket { this.mgr = mgr; } - - - - - /** * Waits until there is a socket waiting for acception or the timeout is * reached. @@ -63,6 +59,7 @@ class I2PServerSocketImpl implements I2PServerSocket { * @throws I2PException if there is a problem with reading a new socket * from the data available (aka the I2PSession closed, etc) * @throws ConnectException if the I2PServerSocket is closed + * @throws InterruptedException if thread is interrupted while waiting */ public void waitIncoming(long timeoutMs) throws I2PException, ConnectException, InterruptedException { if (_log.shouldLog(Log.DEBUG)) @@ -92,21 +89,19 @@ class I2PServerSocketImpl implements I2PServerSocket { } } - /** - * accept(true) has the same behaviour as accept(). - * accept(false) does not wait for a socket connecting. If a socket is - * available in the queue, it is accepted. Else, null is returned. + * accept(timeout) waits timeout ms for a socket connecting. If a socket is + * not available during the timeout, return null. accept(0) behaves like accept() * - * @param true if the call should block until a socket is available + * @param timeout in ms * * @return a connected I2PSocket, or null * * @throws I2PException if there is a problem with reading a new socket * from the data available (aka the I2PSession closed, etc) * @throws ConnectException if the I2PServerSocket is closed + * @throws InterruptedException if thread is interrupted while waiting */ - public I2PSocket accept(long timeout) throws I2PException, ConnectException, InterruptedException { I2PSocket ret = null; diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java index 9dd6aee41..e53e701a1 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java @@ -2,8 +2,6 @@ package net.i2p.client.streaming; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; -import java.util.ArrayList; -import java.util.List; import net.i2p.I2PAppContext; import net.i2p.util.Log; @@ -42,7 +40,8 @@ class ConnectionHandler { _context = context; _log = context.logManager().getLog(ConnectionHandler.class); _manager = mgr; - _synQueue = new LinkedBlockingQueue(MAX_QUEUE_SIZE); + _synQueue = new LinkedBlockingQueue(MAX_QUEUE_SIZE); + _synSignal= new Object(); _active = false; _acceptTimeout = DEFAULT_ACCEPT_TIMEOUT; } @@ -83,12 +82,8 @@ class ConnectionHandler { if (success) { SimpleScheduler.getInstance().addEvent(new TimeoutSyn(packet), _acceptTimeout); if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) - synchronized (this._synSignal) - { - this._synSignal.notifyAll(); - } - - } else { + synchronized (this._synSignal) {this._synSignal.notifyAll();} + } else { if (_log.shouldLog(Log.WARN)) _log.warn("Dropping new SYN request, as the queue is full"); if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) @@ -103,9 +98,24 @@ class ConnectionHandler { * @throws InterruptedException */ public void waitSyn( long ms ) throws InterruptedException { - synchronized (this._synSignal) + synchronized (this._synSignal) { - this._synSignal.wait(ms); + long now = this._context.clock().now() ; + long expiration = now + ms ; + while ( expiration > now || ms<=0 ) { + // check we have not missed a SYN packet before entering + // the lock + for ( Packet p : this._synQueue ) { + if ( p.isFlagSet(Packet.FLAG_SYNCHRONIZE) ) return ; + } + // wait until a SYN is signaled + if ( ms == 0) { + this._synSignal.wait(); + } else { + this._synSignal.wait(expiration-now); + now = this._context.clock().now(); + } + } } } @@ -114,8 +124,8 @@ class ConnectionHandler { * Non-SYN packets with a zero SendStreamID may also be queued here so * that they don't get thrown away while the SYN packet before it is queued. * - * @param timeoutMs max amount of time to wait for a connection (if negative, - * wait indefinitely) + * @param timeoutMs max amount of time to wait for a connection (if less + * than 1ms, wait indefinitely) * @return connection received, or null if there was a timeout or the * handler was shut down */ @@ -125,6 +135,8 @@ class ConnectionHandler { long expiration = timeoutMs + _context.clock().now(); while (true) { + if ( (timeoutMs > 0) && (expiration < _context.clock().now()) ) + return null; if (!_active) { // fail all the ones we had queued up while(true) { @@ -136,9 +148,6 @@ class ConnectionHandler { return null; } - if ( (timeoutMs > 0) && (expiration < _context.clock().now()) ) - return null; - Packet syn = null; while ( _active && syn == null) { if (_log.shouldLog(Log.DEBUG)) diff --git a/apps/streaming/java/src/net/i2p/client/streaming/I2PServerSocketFull.java b/apps/streaming/java/src/net/i2p/client/streaming/I2PServerSocketFull.java index 8fe7fa9ce..0a183afef 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/I2PServerSocketFull.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/I2PServerSocketFull.java @@ -1,12 +1,9 @@ package net.i2p.client.streaming; -import java.net.ConnectException; import java.net.SocketTimeoutException; import net.i2p.I2PAppContext; import net.i2p.I2PException; -import net.i2p.util.Clock; -import net.i2p.util.Log; /** * Bridge to allow accepting new connections @@ -52,17 +49,15 @@ public class I2PServerSocketFull implements I2PServerSocket { } /** - * accept(true) has the same behaviour as accept(). - * accept(false) does not wait for a socket connecting. If a socket is - * available in the queue, it is accepted. Else, null is returned. + * accept(timeout) waits timeout ms for a socket connecting. If a socket is + * not available during the timeout, return null. accept(0) behaves like accept() * - * @param true if the call should block until a socket is available + * @param timeout in ms * * @return a connected I2PSocket, or null * * @throws I2PException if there is a problem with reading a new socket * from the data available (aka the I2PSession closed, etc) - * @throws SocketTimeoutException if the timeout has been reached */ public I2PSocket accept(long timeout) throws I2PException { @@ -78,7 +73,17 @@ public class I2PServerSocketFull implements I2PServerSocket { } } - public void waitIncoming(long timeoutMs) throws InterruptedException { + /** + * block until a SYN packet is detected or the timeout is reached. If timeout is 0, + * block until a SYN packet is detected. + * + * @param timeoutMs + * @throws InterruptedException + * @throws I2PException + */ + public void waitIncoming(long timeoutMs) throws I2PException, InterruptedException { + if (this._socketManager.getConnectionManager().getSession().isClosed()) + throw new I2PException("Session is closed"); this._socketManager.getConnectionManager().getConnectionHandler().waitSyn(timeoutMs); } } From 3dd5950bd1bf2891d04e3294b20a09dbf17a3793 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 4 Apr 2009 17:08:20 +0000 Subject: [PATCH 089/688] Don't let NTCP bid on msgs too big to handle --- .../net/i2p/router/transport/ntcp/NTCPConnection.java | 9 +++++++++ .../src/net/i2p/router/transport/ntcp/NTCPTransport.java | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java index 5610d62e1..070caae84 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java +++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java @@ -104,6 +104,15 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { private static final int META_FREQUENCY = 10*60*1000; private static final int INFO_FREQUENCY = 6*60*60*1000; + /** + * Why this is 16K, and where it is documented, good question? + * We claim we can do 32K datagrams so this is a problem. + * Needs to be fixed. But SSU can handle it? + * In the meantime, don't let the transport bid on big messages. + */ + public static final int BUFFER_SIZE = 16*1024; + /** 2 bytes for length and 4 for CRC */ + public static final int MAX_MSG_SIZE = BUFFER_SIZE - (2 + 4); /** * Create an inbound connected (though not established) NTCP connection diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java index 103b46b15..a9208e9a6 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java +++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java @@ -105,6 +105,7 @@ public class NTCPTransport extends TransportImpl { _context.statManager().createRateStat("ntcp.outboundEstablishFailed", "", "ntcp", new long[] { 60*1000, 10*60*1000 }); _context.statManager().createRateStat("ntcp.outboundFailedIOEImmediate", "", "ntcp", new long[] { 60*1000, 10*60*1000 }); _context.statManager().createRateStat("ntcp.invalidOutboundSkew", "", "ntcp", new long[] { 60*1000, 10*60*1000 }); + _context.statManager().createRateStat("ntcp.noBidTooLargeI2NP", "send size", "ntcp", new long[] { 60*60*1000 }); _context.statManager().createRateStat("ntcp.prepBufCache", "", "ntcp", new long[] { 60*1000, 10*60*1000 }); _context.statManager().createRateStat("ntcp.queuedRecv", "", "ntcp", new long[] { 60*1000, 10*60*1000 }); _context.statManager().createRateStat("ntcp.read", "", "ntcp", new long[] { 60*1000, 10*60*1000 }); @@ -243,6 +244,11 @@ public class NTCPTransport extends TransportImpl { public TransportBid bid(RouterInfo toAddress, long dataSize) { if (!isAlive()) return null; + if (dataSize > NTCPConnection.MAX_MSG_SIZE) { + // let SSU deal with it + _context.statManager().addRateData("ntcp.noBidTooLargeI2NP", dataSize, 0); + return null; + } Hash peer = toAddress.getIdentity().calculateHash(); if (_context.shitlist().isShitlisted(peer, STYLE)) { // we aren't shitlisted in general (since we are trying to get a bid), but we have From a4b5c63702e04fa1799d6abf0a20efe3873d7995 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 4 Apr 2009 17:17:34 +0000 Subject: [PATCH 090/688] -7 --- history.txt | 3 +++ router/java/src/net/i2p/router/RouterVersion.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/history.txt b/history.txt index b4ed77480..acc8a79b0 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,6 @@ +2009-04-04 zzz + * NTCP: Don't bid on messages too big to handle + 2009-04-03 zzz * Console: - Fix bug with IE buttons not working, diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index c08948969..d1e87948b 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -17,7 +17,7 @@ import net.i2p.CoreVersion; public class RouterVersion { public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 6; + public final static long BUILD = 7; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID); From bd489cf4391d10bbd238cc8a640be64d6c43b252 Mon Sep 17 00:00:00 2001 From: sponge Date: Sat, 4 Apr 2009 19:47:36 +0000 Subject: [PATCH 091/688] 2009-04-04 sponge * Hopeful fixups to the infamous orpahned tunnel problem. * BOB now 0.0.5 --- apps/BOB/nbproject/project.properties | 1 + apps/BOB/src/net/i2p/BOB/BOB.java | 3 +- apps/BOB/src/net/i2p/BOB/DoCMDS.java | 2 +- apps/BOB/src/net/i2p/BOB/I2Plistener.java | 110 +++++++---- apps/BOB/src/net/i2p/BOB/I2PtoTCP.java | 4 +- apps/BOB/src/net/i2p/BOB/MUXlisten.java | 14 +- apps/BOB/src/net/i2p/BOB/Main.java | 5 +- apps/BOB/src/net/i2p/BOB/TCPio.java | 21 +- apps/BOB/src/net/i2p/BOB/TCPlistener.java | 180 ++++++++++++------ apps/BOB/src/net/i2p/BOB/TCPtoI2P.java | 21 +- .../src/net/i2p/router/RouterVersion.java | 2 +- 11 files changed, 236 insertions(+), 127 deletions(-) diff --git a/apps/BOB/nbproject/project.properties b/apps/BOB/nbproject/project.properties index ee493f764..9eeffd29f 100644 --- a/apps/BOB/nbproject/project.properties +++ b/apps/BOB/nbproject/project.properties @@ -27,6 +27,7 @@ excludes= file.reference.build-javadoc=../../i2p.i2p/build/javadoc file.reference.i2p.jar=../../core/java/build/i2p.jar file.reference.i2ptunnel.jar=../i2ptunnel/java/build/i2ptunnel.jar +file.reference.jbigi.jar=../../installer/lib/jbigi/jbigi.jar file.reference.mstreaming.jar=../ministreaming/java/build/mstreaming.jar file.reference.router.jar=../../router/java/build/router.jar file.reference.streaming.jar=../streaming/java/build/streaming.jar diff --git a/apps/BOB/src/net/i2p/BOB/BOB.java b/apps/BOB/src/net/i2p/BOB/BOB.java index 59b46b8d7..48a0e067c 100644 --- a/apps/BOB/src/net/i2p/BOB/BOB.java +++ b/apps/BOB/src/net/i2p/BOB/BOB.java @@ -34,6 +34,7 @@ import java.util.Properties; import net.i2p.client.I2PClient; import net.i2p.client.streaming.RetransmissionTimer; import net.i2p.util.Log; + /** * * ################################################################################
    @@ -157,12 +158,12 @@ public class BOB { boolean save = false; // Set up all defaults to be passed forward to other threads. // Re-reading the config file in each thread is pretty damn stupid. - // I2PClient client = I2PClientFactory.createClient(); String configLocation = System.getProperty(PROP_CONFIG_LOCATION, "bob.config"); // This is here just to ensure there is no interference with our threadgroups. RetransmissionTimer Y = RetransmissionTimer.getInstance(); i = Y.hashCode(); + { try { FileInputStream fi = new FileInputStream(configLocation); diff --git a/apps/BOB/src/net/i2p/BOB/DoCMDS.java b/apps/BOB/src/net/i2p/BOB/DoCMDS.java index 099d69fec..12d8d6156 100644 --- a/apps/BOB/src/net/i2p/BOB/DoCMDS.java +++ b/apps/BOB/src/net/i2p/BOB/DoCMDS.java @@ -46,7 +46,7 @@ public class DoCMDS implements Runnable { // FIX ME // I need a better way to do versioning, but this will do for now. - public static final String BMAJ = "00", BMIN = "00", BREV = "04", BEXT = ""; + public static final String BMAJ = "00", BMIN = "00", BREV = "05", BEXT = ""; public static final String BOBversion = BMAJ + "." + BMIN + "." + BREV + BEXT; private Socket server; private Properties props; diff --git a/apps/BOB/src/net/i2p/BOB/I2Plistener.java b/apps/BOB/src/net/i2p/BOB/I2Plistener.java index c59683270..813bb5d34 100644 --- a/apps/BOB/src/net/i2p/BOB/I2Plistener.java +++ b/apps/BOB/src/net/i2p/BOB/I2Plistener.java @@ -62,6 +62,26 @@ public class I2Plistener implements Runnable { tgwatch = 1; } + private void rlock() throws Exception { + database.getReadLock(); + info.getReadLock(); + } + + private void runlock() throws Exception { + database.releaseReadLock(); + info.releaseReadLock(); + } + + private void wlock() throws Exception { + database.getWriteLock(); + info.getWriteLock(); + } + + private void wunlock() throws Exception { + info.releaseWriteLock(); + database.releaseWriteLock(); + } + /** * Simply listen on I2P port, and thread connections * @@ -70,68 +90,86 @@ public class I2Plistener implements Runnable { boolean g = false; I2PSocket sessSocket = null; - serverSocket.setSoTimeout(50); - database.getReadLock(); - info.getReadLock(); - if(info.exists("INPORT")) { - tgwatch = 2; - } - info.releaseReadLock(); - database.releaseReadLock(); - boolean spin = true; - while(spin) { +die: { - database.getReadLock(); - info.getReadLock(); - spin = info.get("RUNNING").equals(Boolean.TRUE); - info.releaseReadLock(); - database.releaseReadLock(); + serverSocket.setSoTimeout(50); try { + if (info.exists("INPORT")) { + tgwatch = 2; + } + } catch (Exception e) { try { - sessSocket = serverSocket.accept(); - g = true; - } catch(ConnectException ce) { - g = false; - } catch(SocketTimeoutException ste) { - g = false; - } - if(g) { - g = false; - // toss the connection to a new thread. - I2PtoTCP conn_c = new I2PtoTCP(sessSocket, info, database); - Thread t = new Thread(conn_c, "BOBI2PtoTCP"); - t.start(); + runlock(); + } catch (Exception e2) { + break die; } + break die; + } + boolean spin = true; + while (spin) { - } catch(I2PException e) { - // System.out.println("Exception " + e); + try { + rlock(); + } catch (Exception e) { + break die; + } + try { + spin = info.get("RUNNING").equals(Boolean.TRUE); + } catch (Exception e) { + try { + runlock(); + } catch (Exception e2) { + break die; + } + break die; + } + try { + try { + sessSocket = serverSocket.accept(); + g = true; + } catch (ConnectException ce) { + g = false; + } catch (SocketTimeoutException ste) { + g = false; + } + if (g) { + g = false; + // toss the connection to a new thread. + I2PtoTCP conn_c = new I2PtoTCP(sessSocket, info, database); + Thread t = new Thread(conn_c, "BOBI2PtoTCP"); + t.start(); + } + + } catch (I2PException e) { + // System.out.println("Exception " + e); + } } } // System.out.println("I2Plistener: Close"); try { serverSocket.close(); - } catch(I2PException e) { + } catch (I2PException e) { // nop } // need to kill off the socket manager too. I2PSession session = socketManager.getSession(); - if(session != null) { + if (session != null) { // System.out.println("I2Plistener: destroySession"); try { session.destroySession(); - } catch(I2PSessionException ex) { + } catch (I2PSessionException ex) { // nop } } // System.out.println("I2Plistener: Waiting for children"); - while(Thread.activeCount() > tgwatch) { // wait for all threads in our threadgroup to finish + while (Thread.activeCount() > tgwatch) { // wait for all threads in our threadgroup to finish try { Thread.sleep(100); //sleep for 100 ms (One tenth second) - } catch(Exception e) { + } catch (Exception e) { // nop } } - // System.out.println("I2Plistener: Done."); + // System.out.println("I2Plistener: Done."); } } diff --git a/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java b/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java index 5d24e19d3..06c3131fe 100644 --- a/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java +++ b/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java @@ -105,8 +105,8 @@ die: { out.flush(); // not really needed, but... } // setup to cross the streams - TCPio conn_c = new TCPio(in, Iout, info, database); // app -> I2P - TCPio conn_a = new TCPio(Iin, out, info, database); // I2P -> app + TCPio conn_c = new TCPio(in, Iout /*, info, database */ ); // app -> I2P + TCPio conn_a = new TCPio(Iin, out /* , info, database */); // I2P -> app Thread t = new Thread(conn_c, "TCPioA"); Thread q = new Thread(conn_a, "TCPioB"); // Fire! diff --git a/apps/BOB/src/net/i2p/BOB/MUXlisten.java b/apps/BOB/src/net/i2p/BOB/MUXlisten.java index 89ab53fe6..879cf9a64 100644 --- a/apps/BOB/src/net/i2p/BOB/MUXlisten.java +++ b/apps/BOB/src/net/i2p/BOB/MUXlisten.java @@ -100,7 +100,7 @@ public class MUXlisten implements Runnable { // Everything is OK as far as we can tell. this.database.getWriteLock(); this.info.getWriteLock(); - this.info.add("STARTING", Boolean.TRUE); + this.info.add("STARTING", new Boolean(true)); this.info.releaseWriteLock(); this.database.releaseWriteLock(); } @@ -134,8 +134,8 @@ public class MUXlisten implements Runnable { try { wlock(); try { - info.add("RUNNING", Boolean.TRUE); - info.add("STARTING", Boolean.FALSE); + info.add("RUNNING", new Boolean(true)); + info.add("STARTING", new Boolean(false)); } catch(Exception e) { wunlock(); return; @@ -198,7 +198,7 @@ die: { try { wlock(); try { - info.add("RUNNING", Boolean.FALSE); + info.add("RUNNING", new Boolean(false)); } catch(Exception e) { wunlock(); break die; @@ -255,9 +255,9 @@ die: { try { wlock(); try { - info.add("STARTING", Boolean.FALSE); - info.add("STOPPING", Boolean.FALSE); - info.add("RUNNING", Boolean.FALSE); + info.add("STARTING", new Boolean(false)); + info.add("STOPPING", new Boolean(false)); + info.add("RUNNING", new Boolean(false)); } catch(Exception e) { wunlock(); return; diff --git a/apps/BOB/src/net/i2p/BOB/Main.java b/apps/BOB/src/net/i2p/BOB/Main.java index 2d81fb30e..b32e4758e 100644 --- a/apps/BOB/src/net/i2p/BOB/Main.java +++ b/apps/BOB/src/net/i2p/BOB/Main.java @@ -24,7 +24,7 @@ package net.i2p.BOB; import net.i2p.client.streaming.RetransmissionTimer; - +import net.i2p.util.SimpleScheduler; /** * Start from command line * @@ -39,6 +39,9 @@ public class Main { public static void main(String[] args) { // THINK THINK THINK THINK THINK THINK RetransmissionTimer Y = RetransmissionTimer.getInstance(); + // needs SimpleScheduler + // no way to stop the scheduler?!? + SimpleScheduler.getInstance(); BOB.main(args); Y.stop(); } diff --git a/apps/BOB/src/net/i2p/BOB/TCPio.java b/apps/BOB/src/net/i2p/BOB/TCPio.java index c9f4ab64c..109d8e8cb 100644 --- a/apps/BOB/src/net/i2p/BOB/TCPio.java +++ b/apps/BOB/src/net/i2p/BOB/TCPio.java @@ -35,7 +35,7 @@ public class TCPio implements Runnable { private InputStream Ain; private OutputStream Aout; - private NamedDB info, database; + // private NamedDB info, database; /** * Constructor @@ -43,13 +43,14 @@ public class TCPio implements Runnable { * @param Ain * @param Aout * @param info - * @param database + * + * param database */ - TCPio(InputStream Ain, OutputStream Aout, NamedDB info, NamedDB database) { + TCPio(InputStream Ain, OutputStream Aout /*, NamedDB info , NamedDB database */) { this.Ain = Ain; this.Aout = Aout; - this.info = info; - this.database = database; + // this.info = info; + // this.database = database; } /** @@ -86,11 +87,11 @@ public class TCPio implements Runnable { boolean spin = true; try { while(spin) { - database.getReadLock(); - info.getReadLock(); - spin = info.get("RUNNING").equals(Boolean.TRUE); - info.releaseReadLock(); - database.releaseReadLock(); + // database.getReadLock(); + // info.getReadLock(); + // spin = info.get("RUNNING").equals(Boolean.TRUE); + // info.releaseReadLock(); + // database.releaseReadLock(); b = Ain.read(a, 0, 1); // System.out.println(info.get("NICKNAME").toString() + " " + b); if(b > 0) { diff --git a/apps/BOB/src/net/i2p/BOB/TCPlistener.java b/apps/BOB/src/net/i2p/BOB/TCPlistener.java index 30380a55d..7e931768c 100644 --- a/apps/BOB/src/net/i2p/BOB/TCPlistener.java +++ b/apps/BOB/src/net/i2p/BOB/TCPlistener.java @@ -63,6 +63,26 @@ public class TCPlistener implements Runnable { tgwatch = 1; } + private void rlock() throws Exception { + database.getReadLock(); + info.getReadLock(); + } + + private void runlock() throws Exception { + database.releaseReadLock(); + info.releaseReadLock(); + } + + private void wlock() throws Exception { + database.getWriteLock(); + info.getWriteLock(); + } + + private void wunlock() throws Exception { + info.releaseWriteLock(); + database.releaseWriteLock(); + } + /** * Simply listen on TCP port, and thread connections * @@ -70,77 +90,121 @@ public class TCPlistener implements Runnable { public void run() { boolean g = false; boolean spin = true; - database.getReadLock(); - info.getReadLock(); - if(info.exists("OUTPORT")) { - tgwatch = 2; - } - try { - Socket server = new Socket(); - listener.setSoTimeout(50); // Half of the expected time from MUXlisten - info.releaseReadLock(); - database.releaseReadLock(); - while(spin) { - database.getReadLock(); - info.getReadLock(); - spin = info.get("RUNNING").equals(Boolean.TRUE); - info.releaseReadLock(); - database.releaseReadLock(); - try { - server = listener.accept(); - g = true; - } catch(SocketTimeoutException ste) { - g = false; - } - if(g) { - // toss the connection to a new thread. - TCPtoI2P conn_c = new TCPtoI2P(socketManager, server, info, database); - Thread t = new Thread(conn_c, "BOBTCPtoI2P"); - t.start(); - g = false; - } - } - //System.out.println("TCPlistener: destroySession"); - listener.close(); - } catch(IOException ioe) { - try { - listener.close(); - } catch(IOException e) { - } - // Fatal failure, cause a stop event - database.getReadLock(); - info.getReadLock(); - spin = info.get("RUNNING").equals(Boolean.TRUE); - info.releaseReadLock(); - database.releaseReadLock(); - if(spin) { - database.getWriteLock(); - info.getWriteLock(); - info.add("STOPPING", new Boolean(true)); - info.add("RUNNING", new Boolean(false)); - info.releaseWriteLock(); - database.releaseWriteLock(); - } - } +die: { + try { + rlock(); + } catch (Exception e) { + break die; + } + try { + if (info.exists("OUTPORT")) { + tgwatch = 2; + } + } catch (Exception e) { + try { + runlock(); + } catch (Exception e2) { + break die; + } + break die; + } + try { + runlock(); + } catch (Exception e) { + break die; + } + try { + Socket server = new Socket(); + listener.setSoTimeout(50); // Half of the expected time from MUXlisten + while (spin) { + try { + rlock(); + } catch (Exception e) { + break die; + } + try { + spin = info.get("RUNNING").equals(Boolean.TRUE); + } catch (Exception e) { + try { + runlock(); + } catch (Exception e2) { + break die; + } + break die; + } + try { + server = listener.accept(); + g = true; + } catch (SocketTimeoutException ste) { + g = false; + } + if (g) { + // toss the connection to a new thread. + TCPtoI2P conn_c = new TCPtoI2P(socketManager, server /* , info, database */); + Thread t = new Thread(conn_c, "BOBTCPtoI2P"); + t.start(); + g = false; + } + } + //System.out.println("TCPlistener: destroySession"); + listener.close(); + } catch (IOException ioe) { + try { + listener.close(); + } catch (IOException e) { + } + // Fatal failure, cause a stop event + try { + rlock(); + try { + spin = info.get("RUNNING").equals(Boolean.TRUE); + } catch (Exception e) { + runlock(); + break die; + } + } catch (Exception e) { + break die; + } + if (spin) { + try { + wlock(); + try { + info.add("STOPPING", new Boolean(true)); + info.add("RUNNING", new Boolean(false)); + } catch (Exception e) { + wunlock(); + break die; + } + } catch (Exception e) { + break die; + } + try { + wunlock(); + } catch (Exception e) { + break die; + } + } + } + } // need to kill off the socket manager too. I2PSession session = socketManager.getSession(); - if(session != null) { + if (session != null) { try { session.destroySession(); - } catch(I2PSessionException ex) { + } catch (I2PSessionException ex) { // nop } } //System.out.println("TCPlistener: Waiting for children"); - while(Thread.activeCount() > tgwatch) { // wait for all threads in our threadgroup to finish + while (Thread.activeCount() > tgwatch) { // wait for all threads in our threadgroup to finish try { Thread.sleep(100); //sleep for 100 ms (One tenth second) - } catch(Exception e) { + } catch (Exception e) { // nop - } + } } - //System.out.println("TCPlistener: Done."); + //System.out.println("TCPlistener: Done."); } } diff --git a/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java b/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java index df61e78e1..fe1ca3278 100644 --- a/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java +++ b/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java @@ -45,7 +45,7 @@ import net.i2p.i2ptunnel.I2PTunnel; public class TCPtoI2P implements Runnable { private I2PSocket I2P; - private NamedDB info, database; + // private NamedDB info, database; private Socket sock; private I2PSocketManager socketManager; @@ -84,13 +84,13 @@ public class TCPtoI2P implements Runnable { * Constructor * @param i2p * @param socket - * @param info - * @param database + * param info + * param database */ - TCPtoI2P(I2PSocketManager i2p, Socket socket, NamedDB info, NamedDB database) { + TCPtoI2P(I2PSocketManager i2p, Socket socket /*, NamedDB info, NamedDB database */) { this.sock = socket; - this.info = info; - this.database = database; + // this.info = info; + // this.database = database; this.socketManager = i2p; } @@ -110,6 +110,7 @@ public class TCPtoI2P implements Runnable { /** * TCP stream to I2P stream thread starter + * */ public void run() { String line, input; @@ -138,8 +139,8 @@ public class TCPtoI2P implements Runnable { InputStream Iin = I2P.getInputStream(); OutputStream Iout = I2P.getOutputStream(); // setup to cross the streams - TCPio conn_c = new TCPio(in, Iout, info, database); // app -> I2P - TCPio conn_a = new TCPio(Iin, out, info, database); // I2P -> app + TCPio conn_c = new TCPio(in, Iout /*, info, database */); // app -> I2P + TCPio conn_a = new TCPio(Iin, out /*, info, database */); // I2P -> app Thread t = new Thread(conn_c, "TCPioA"); Thread q = new Thread(conn_a, "TCPioB"); // Fire! @@ -167,7 +168,8 @@ public class TCPtoI2P implements Runnable { } catch(Exception e) { Emsg("ERROR " + e.toString(), out); } - } catch(IOException ioe) { + } catch(Exception e) { + // bail on anything else } try { // System.out.println("TCPtoI2P: Close I2P"); @@ -181,6 +183,5 @@ public class TCPtoI2P implements Runnable { } catch(Exception e) { } // System.out.println("TCPtoI2P: Done."); - } } diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index d1e87948b..eeea3faa9 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -17,7 +17,7 @@ import net.i2p.CoreVersion; public class RouterVersion { public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 7; + public final static long BUILD = 8; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID); From 884663d077d2608ee6a0f854cfa4d81efa6c08dc Mon Sep 17 00:00:00 2001 From: sponge Date: Sat, 4 Apr 2009 19:48:27 +0000 Subject: [PATCH 092/688] Forgot the history.txt entry, oops!! --- history.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/history.txt b/history.txt index acc8a79b0..1b09ebb63 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,7 @@ +2009-04-04 sponge + * Hopeful fixups to the infamous orpahned tunnel problem. + * BOB now 0.0.5 + 2009-04-04 zzz * NTCP: Don't bid on messages too big to handle From 6b825fbe2536e12938a41c171de894fcf702989b Mon Sep 17 00:00:00 2001 From: mkvore-commit Date: Sun, 5 Apr 2009 21:32:43 +0000 Subject: [PATCH 093/688] SAMv3 : protocol better specified, and small changes in the code reflecting the new protocol --- apps/sam/Demos/datagramTests/samForward.py | 2 +- apps/sam/Demos/datagramTests/samOut.py | 4 +- apps/sam/Demos/rawTests/samOut.py | 4 +- apps/sam/Demos/streamTests/samForward.py | 6 + apps/sam/Demos/streamTests/samIn.py | 6 +- apps/sam/java/src/net/i2p/sam/SAMBridge.java | 62 ++++--- .../src/net/i2p/sam/SAMHandlerFactory.java | 8 +- apps/sam/java/src/net/i2p/sam/SAMUtils.java | 39 ++++- .../java/src/net/i2p/sam/SAMv1Handler.java | 7 +- .../java/src/net/i2p/sam/SAMv3Handler.java | 156 ++++++++++-------- .../src/net/i2p/sam/SAMv3StreamSession.java | 7 +- 11 files changed, 180 insertions(+), 121 deletions(-) diff --git a/apps/sam/Demos/datagramTests/samForward.py b/apps/sam/Demos/datagramTests/samForward.py index 1706b3fa5..a1fd5b3e3 100755 --- a/apps/sam/Demos/datagramTests/samForward.py +++ b/apps/sam/Demos/datagramTests/samForward.py @@ -23,7 +23,7 @@ sess = socket.socket( sess.connect(("127.0.0.1",7656)); sess.send("HELLO VERSION MIN=3.0 MAX=3.0\n") sys.stdout.write(sess.recv(1000)) -sess.send("SESSION CREATE STYLE=DATAGRAM PORT="+str(port)+" ID="+name+" DESTINATION=EYUpJFeW9tiubXR0aOjvCJ~ndj3xN0Wn-ljuGdbpOEttPg7nj0VCTOQDJ~FAolzn9FIDdmR3VjM0OFFDT46Q5HN4vShXFE2VNC8e3~GjzxJfaJhijRC2R9oIOzsNlzKtInD2o9lh0PxPioNMCigwmgWuqlQHs4tjWeaYRAtooHxbrtuoCIhIdGfyVV-nAcPiyYbouKq3leETXE~4kBXm-LfWfyPtrv6OuDk3GBVVcthv19GYBmnl2YI8HpJjc-G-TvNkgYishjzIJyEW-Xrpy43R4ZBXlyQqnheGLlbOEY8NLDbyNHLRMMOGbcr~67SVE3Iw3RqQ3Dhrkq2FCaQwcDucfIUCCbOfCZgu0hlnCkS42xsUvegQeiwMxbdI~h9v7vcR3yFFOrHX6WQvIZSbFLKNGArGJcfmOJVLqw1wTC4AgYXjk3csVDPd-QWbMXOuodyBgrg27Ds2BBYTsVXWskoo6ASsMIQZ6jMfL7PkY9dPLCRParIyzb9aPmf~MntNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHNqwgkhJnBW4ymaRsdVmITAha-ff0UiALfKSlznqp5HcSewgMHbzQ0I01TQytFnW\n") +sess.send("SESSION CREATE STYLE=DATAGRAM PORT="+str(port)+" ID="+name+" DESTINATION=EYUpJFeW9tiubXR0aOjvCJ~ndj3xN0Wn-ljuGdbpOEttPg7nj0VCTOQDJ~FAolzn9FIDdmR3VjM0OFFDT46Q5HN4vShXFE2VNC8e3~GjzxJfaJhijRC2R9oIOzsNlzKtInD2o9lh0PxPioNMCigwmgWuqlQHs4tjWeaYRAtooHxbrtuoCIhIdGfyVV-nAcPiyYbouKq3leETXE~4kBXm-LfWfyPtrv6OuDk3GBVVcthv19GYBmnl2YI8HpJjc-G-TvNkgYishjzIJyEW-Xrpy43R4ZBXlyQqnheGLlbOEY8NLDbyNHLRMMOGbcr~67SVE3Iw3RqQ3Dhrkq2FCaQwcDucfIUCCbOfCZgu0hlnCkS42xsUvegQeiwMxbdI~h9v7vcR3yFFOrHX6WQvIZSbFLKNGArGJcfmOJVLqw1wTC4AgYXjk3csVDPd-QWbMXOuodyBgrg27Ds2BBYTsVXWskoo6ASsMIQZ6jMfL7PkY9dPLCRParIyzb9aPmf~MntNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHNqwgkhJnBW4ymaRsdVmITAha-ff0UiALfKSlznqp5HcSewgMHbzQ0I01TQytFnW outbound.nickname="+name+" inbound.nickname="+name+" outbound.length=0\n") sys.stdout.write(sess.recv(10000)) s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) diff --git a/apps/sam/Demos/datagramTests/samOut.py b/apps/sam/Demos/datagramTests/samOut.py index bb9be4db2..1a521334f 100755 --- a/apps/sam/Demos/datagramTests/samOut.py +++ b/apps/sam/Demos/datagramTests/samOut.py @@ -26,6 +26,6 @@ port = 7655 host = "localhost" s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind(("", 0)) -s.sendto(name+" tYhjbFlFL38WFuO5eCzTvE0UBr4RfaqWMKlekGeMoB-Ouz7nYaWfiS-9j3jMiZT7FH~pwdmoSREOs2ZbXK84sR59P~pPfeCMxnJrk57f3U9uKzXkesjkKWYco3YAGs-G8sw8Fu2FBx0Do57yBdA9~j8Zq6pMjmgPBXCLuXG3vo0Z8zUWCjApJyFY6OXYopHck9Fz9vKy7YhC6zXFHfEuNHVkAooduiLd~aCoGij0TW3lH2rTVU-lx-DUdi6edxQ5-RvDNkXfikvytoCpRkivbNVytjCJLk~7RNU4FpBD20wTZWNJmEG3OY3cjNjawJVFdNjtgczh9K7gZ7ad-NjVjZVhXEj1lU8mk~vAH-2QE5om8dstWUwWoNDwmVDlvIJNKzQmahG~VrpFexFHXO0n3fKIXcSgWGOHDExM8w9neCt7AxUjxPDtXXuYNW~bRwcfiL-C9~z4K9rmwiTPZX0lmsToSXTF28l7WAoj~TMT9kZAjQeFRRWU5oW5oxVuonVvAAAA\n"+message, (host, port)) -s.sendto(name+" EYUpJFeW9tiubXR0aOjvCJ~ndj3xN0Wn-ljuGdbpOEttPg7nj0VCTOQDJ~FAolzn9FIDdmR3VjM0OFFDT46Q5HN4vShXFE2VNC8e3~GjzxJfaJhijRC2R9oIOzsNlzKtInD2o9lh0PxPioNMCigwmgWuqlQHs4tjWeaYRAtooHxbrtuoCIhIdGfyVV-nAcPiyYbouKq3leETXE~4kBXm-LfWfyPtrv6OuDk3GBVVcthv19GYBmnl2YI8HpJjc-G-TvNkgYishjzIJyEW-Xrpy43R4ZBXlyQqnheGLlbOEY8NLDbyNHLRMMOGbcr~67SVE3Iw3RqQ3Dhrkq2FCaQwcDucfIUCCbOfCZgu0hlnCkS42xsUvegQeiwMxbdI~h9v7vcR3yFFOrHX6WQvIZSbFLKNGArGJcfmOJVLqw1wTC4AgYXjk3csVDPd-QWbMXOuodyBgrg27Ds2BBYTsVXWskoo6ASsMIQZ6jMfL7PkY9dPLCRParIyzb9aPmf~MntNAAAA\n"+message, (host, port)) +s.sendto("3.0 "+name+" tYhjbFlFL38WFuO5eCzTvE0UBr4RfaqWMKlekGeMoB-Ouz7nYaWfiS-9j3jMiZT7FH~pwdmoSREOs2ZbXK84sR59P~pPfeCMxnJrk57f3U9uKzXkesjkKWYco3YAGs-G8sw8Fu2FBx0Do57yBdA9~j8Zq6pMjmgPBXCLuXG3vo0Z8zUWCjApJyFY6OXYopHck9Fz9vKy7YhC6zXFHfEuNHVkAooduiLd~aCoGij0TW3lH2rTVU-lx-DUdi6edxQ5-RvDNkXfikvytoCpRkivbNVytjCJLk~7RNU4FpBD20wTZWNJmEG3OY3cjNjawJVFdNjtgczh9K7gZ7ad-NjVjZVhXEj1lU8mk~vAH-2QE5om8dstWUwWoNDwmVDlvIJNKzQmahG~VrpFexFHXO0n3fKIXcSgWGOHDExM8w9neCt7AxUjxPDtXXuYNW~bRwcfiL-C9~z4K9rmwiTPZX0lmsToSXTF28l7WAoj~TMT9kZAjQeFRRWU5oW5oxVuonVvAAAA\n"+message, (host, port)) +s.sendto("3.0 "+name+" EYUpJFeW9tiubXR0aOjvCJ~ndj3xN0Wn-ljuGdbpOEttPg7nj0VCTOQDJ~FAolzn9FIDdmR3VjM0OFFDT46Q5HN4vShXFE2VNC8e3~GjzxJfaJhijRC2R9oIOzsNlzKtInD2o9lh0PxPioNMCigwmgWuqlQHs4tjWeaYRAtooHxbrtuoCIhIdGfyVV-nAcPiyYbouKq3leETXE~4kBXm-LfWfyPtrv6OuDk3GBVVcthv19GYBmnl2YI8HpJjc-G-TvNkgYishjzIJyEW-Xrpy43R4ZBXlyQqnheGLlbOEY8NLDbyNHLRMMOGbcr~67SVE3Iw3RqQ3Dhrkq2FCaQwcDucfIUCCbOfCZgu0hlnCkS42xsUvegQeiwMxbdI~h9v7vcR3yFFOrHX6WQvIZSbFLKNGArGJcfmOJVLqw1wTC4AgYXjk3csVDPd-QWbMXOuodyBgrg27Ds2BBYTsVXWskoo6ASsMIQZ6jMfL7PkY9dPLCRParIyzb9aPmf~MntNAAAA\n"+message, (host, port)) diff --git a/apps/sam/Demos/rawTests/samOut.py b/apps/sam/Demos/rawTests/samOut.py index bb9be4db2..1a521334f 100755 --- a/apps/sam/Demos/rawTests/samOut.py +++ b/apps/sam/Demos/rawTests/samOut.py @@ -26,6 +26,6 @@ port = 7655 host = "localhost" s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind(("", 0)) -s.sendto(name+" tYhjbFlFL38WFuO5eCzTvE0UBr4RfaqWMKlekGeMoB-Ouz7nYaWfiS-9j3jMiZT7FH~pwdmoSREOs2ZbXK84sR59P~pPfeCMxnJrk57f3U9uKzXkesjkKWYco3YAGs-G8sw8Fu2FBx0Do57yBdA9~j8Zq6pMjmgPBXCLuXG3vo0Z8zUWCjApJyFY6OXYopHck9Fz9vKy7YhC6zXFHfEuNHVkAooduiLd~aCoGij0TW3lH2rTVU-lx-DUdi6edxQ5-RvDNkXfikvytoCpRkivbNVytjCJLk~7RNU4FpBD20wTZWNJmEG3OY3cjNjawJVFdNjtgczh9K7gZ7ad-NjVjZVhXEj1lU8mk~vAH-2QE5om8dstWUwWoNDwmVDlvIJNKzQmahG~VrpFexFHXO0n3fKIXcSgWGOHDExM8w9neCt7AxUjxPDtXXuYNW~bRwcfiL-C9~z4K9rmwiTPZX0lmsToSXTF28l7WAoj~TMT9kZAjQeFRRWU5oW5oxVuonVvAAAA\n"+message, (host, port)) -s.sendto(name+" EYUpJFeW9tiubXR0aOjvCJ~ndj3xN0Wn-ljuGdbpOEttPg7nj0VCTOQDJ~FAolzn9FIDdmR3VjM0OFFDT46Q5HN4vShXFE2VNC8e3~GjzxJfaJhijRC2R9oIOzsNlzKtInD2o9lh0PxPioNMCigwmgWuqlQHs4tjWeaYRAtooHxbrtuoCIhIdGfyVV-nAcPiyYbouKq3leETXE~4kBXm-LfWfyPtrv6OuDk3GBVVcthv19GYBmnl2YI8HpJjc-G-TvNkgYishjzIJyEW-Xrpy43R4ZBXlyQqnheGLlbOEY8NLDbyNHLRMMOGbcr~67SVE3Iw3RqQ3Dhrkq2FCaQwcDucfIUCCbOfCZgu0hlnCkS42xsUvegQeiwMxbdI~h9v7vcR3yFFOrHX6WQvIZSbFLKNGArGJcfmOJVLqw1wTC4AgYXjk3csVDPd-QWbMXOuodyBgrg27Ds2BBYTsVXWskoo6ASsMIQZ6jMfL7PkY9dPLCRParIyzb9aPmf~MntNAAAA\n"+message, (host, port)) +s.sendto("3.0 "+name+" tYhjbFlFL38WFuO5eCzTvE0UBr4RfaqWMKlekGeMoB-Ouz7nYaWfiS-9j3jMiZT7FH~pwdmoSREOs2ZbXK84sR59P~pPfeCMxnJrk57f3U9uKzXkesjkKWYco3YAGs-G8sw8Fu2FBx0Do57yBdA9~j8Zq6pMjmgPBXCLuXG3vo0Z8zUWCjApJyFY6OXYopHck9Fz9vKy7YhC6zXFHfEuNHVkAooduiLd~aCoGij0TW3lH2rTVU-lx-DUdi6edxQ5-RvDNkXfikvytoCpRkivbNVytjCJLk~7RNU4FpBD20wTZWNJmEG3OY3cjNjawJVFdNjtgczh9K7gZ7ad-NjVjZVhXEj1lU8mk~vAH-2QE5om8dstWUwWoNDwmVDlvIJNKzQmahG~VrpFexFHXO0n3fKIXcSgWGOHDExM8w9neCt7AxUjxPDtXXuYNW~bRwcfiL-C9~z4K9rmwiTPZX0lmsToSXTF28l7WAoj~TMT9kZAjQeFRRWU5oW5oxVuonVvAAAA\n"+message, (host, port)) +s.sendto("3.0 "+name+" EYUpJFeW9tiubXR0aOjvCJ~ndj3xN0Wn-ljuGdbpOEttPg7nj0VCTOQDJ~FAolzn9FIDdmR3VjM0OFFDT46Q5HN4vShXFE2VNC8e3~GjzxJfaJhijRC2R9oIOzsNlzKtInD2o9lh0PxPioNMCigwmgWuqlQHs4tjWeaYRAtooHxbrtuoCIhIdGfyVV-nAcPiyYbouKq3leETXE~4kBXm-LfWfyPtrv6OuDk3GBVVcthv19GYBmnl2YI8HpJjc-G-TvNkgYishjzIJyEW-Xrpy43R4ZBXlyQqnheGLlbOEY8NLDbyNHLRMMOGbcr~67SVE3Iw3RqQ3Dhrkq2FCaQwcDucfIUCCbOfCZgu0hlnCkS42xsUvegQeiwMxbdI~h9v7vcR3yFFOrHX6WQvIZSbFLKNGArGJcfmOJVLqw1wTC4AgYXjk3csVDPd-QWbMXOuodyBgrg27Ds2BBYTsVXWskoo6ASsMIQZ6jMfL7PkY9dPLCRParIyzb9aPmf~MntNAAAA\n"+message, (host, port)) diff --git a/apps/sam/Demos/streamTests/samForward.py b/apps/sam/Demos/streamTests/samForward.py index c12753405..687b265f6 100755 --- a/apps/sam/Demos/streamTests/samForward.py +++ b/apps/sam/Demos/streamTests/samForward.py @@ -55,4 +55,10 @@ while 1 : chunk = sock.recv(100) sys.stdout.write(chunk) if not chunk : break +print "Forward socket closed" +l=0 +while 1 : + chunk = sess.recv(100) + sys.stdout.write(chunk) + if not chunk : break diff --git a/apps/sam/Demos/streamTests/samIn.py b/apps/sam/Demos/streamTests/samIn.py index 2edfd65c6..ab2e3fa52 100755 --- a/apps/sam/Demos/streamTests/samIn.py +++ b/apps/sam/Demos/streamTests/samIn.py @@ -44,16 +44,18 @@ def accept() : sock.send("HELLO VERSION MIN=3.0 MAX=3.0\n") sys.stdout.write(sock.recv(1000)) sock.send("STREAM ACCEPT ID=" + name + silent+"\n") - print "STREAM ACCEPT ID="+name+silent+"\n" + print "STREAM ACCEPT ID="+name+silent + if (silent==" SILENT=false") : + sys.stdout.write( sock.recv(100) ) return sock def echo( sock, lines ) : l = 0 while lines==-1 or l map ; public SessionsDB() { map = new HashMap() ; } - synchronized public boolean put( String nick, SessionRecord session ) + synchronized public boolean put( String nick, SessionRecord session ) throws ExistingId, ExistingDest { + if ( map.containsKey(nick) ) { + throw new ExistingId(); + } + for ( SessionRecord r : map.values() ) { + if (r.getDest().equals(session.getDest())) { + throw new ExistingDest(); + } + } + if ( !map.containsKey(nick) ) { session.createThreadGroup("SAM session "+nick); map.put(nick, session) ; @@ -455,16 +473,10 @@ public class SAMv3Handler extends SAMv1Handler _log.debug("Custom destination specified [" + dest + "]"); } - boolean good_key = false ; try { - good_key = (new VerifiedDestination(dest)).verifyCert(true); - } catch (DataFormatException e) { - good_key = false ; - } - if (!good_key) - { - _log.debug("Bad destination key"); - return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"bad destination key\"\n"); + SAMUtils.checkPrivateDestination(dest); + } catch ( SAMUtils.InvalidDestination e ) { + return writeString("SESSION STATUS RESULT=INVALID_KEY\n"); } nick = props.getProperty("ID"); @@ -489,12 +501,17 @@ public class SAMv3Handler extends SAMv1Handler allProps.putAll(i2cpProps); allProps.putAll(props); - if (! sSessionsHash.put( nick, new SessionRecord(dest, allProps, this) ) ) { + + try { + sSessionsHash.put( nick, new SessionRecord(dest, allProps, this) ) ; + } catch (SessionsDB.ExistingId e) { _log.debug("SESSION ID parameter already in use"); - String n = nick ; - return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"ID "+n+" already in use\"\n"); + return writeString("SESSION STATUS RESULT=DUPLICATED_ID\n"); + } catch (SessionsDB.ExistingDest e) { + return writeString("SESSION STATUS RESULT=DUPLICATED_DEST\n"); } + // Create the session if (style.equals("RAW")) { @@ -568,14 +585,19 @@ public class SAMv3Handler extends SAMv1Handler if ( session != null ) { _log.error ( "STREAM message received, but this session is a master session" ); - writeString("STREAM STATUS RESULT=I2P_ERROR MESSAGE=\"master session cannot be used for streams"); + + try { + notifyStreamResult(true, "I2P_ERROR", "master session cannot be used for streams"); + } catch (IOException e) {} return false; } nick = props.getProperty("ID"); if (nick == null) { _log.debug("SESSION ID parameter not specified"); - writeString("STREAM STATUS RESULT=I2P_ERROR MESSAGE=\"ID not specified\"\n"); + try { + notifyStreamResult(true, "I2P_ERROR", "ID not specified"); + } catch (IOException e) {} return false ; } props.remove("ID"); @@ -584,7 +606,9 @@ public class SAMv3Handler extends SAMv1Handler if ( rec==null ) { _log.debug("STREAM SESSION ID does not exist"); - writeString("STREAM STATUS RESULT=I2P_ERROR MESSAGE=\"STREAM SESSION ID does not exist\"\n"); + try { + notifyStreamResult(true, "INVALID_ID", "STREAM SESSION ID does not exist"); + } catch (IOException e) {} return false ; } @@ -592,7 +616,9 @@ public class SAMv3Handler extends SAMv1Handler if (streamSession==null) { _log.debug("specified ID is not a stream session"); - writeString("STREAM STATUS RESULT=I2P_ERROR MESSAGE=\"specified ID is not a STREAM session\"\n"); + try { + notifyStreamResult(true, "I2P_ERROR", "specified ID is not a STREAM session"); + } catch (IOException e) {} return false ; } @@ -612,45 +638,49 @@ public class SAMv3Handler extends SAMv1Handler { _log.debug ( "Unrecognized RAW message opcode: \"" + opcode + "\"" ); - writeString("STREAM STATUS RESULT=I2P_ERROR MESSAGE=\"Unrecognized RAW message opcode: \"" - + opcode + "\"" ); + try { + notifyStreamResult(true, "I2P_ERROR", "Unrecognized RAW message opcode: "+opcode ); + } catch (IOException e) {} return false; } } + protected boolean execStreamConnect( Properties props) { - if (props == null) { - _log.debug("No parameters specified in STREAM CONNECT message"); - return false; - } - boolean verbose = props.getProperty("SILENT","false").equals("false"); - - String dest = props.getProperty("DESTINATION"); - if (dest == null) { - _log.debug("Destination not specified in RAW SEND message"); - return false; - } - props.remove("DESTINATION"); - try { + if (props == null) { + notifyStreamResult(true,"I2P_ERROR","No parameters specified in STREAM CONNECT message"); + _log.debug("No parameters specified in STREAM CONNECT message"); + return false; + } + boolean verbose = props.getProperty("SILENT","false").equals("false"); + + String dest = props.getProperty("DESTINATION"); + if (dest == null) { + notifyStreamResult(verbose, "I2P_ERROR", "Destination not specified in RAW SEND message"); + _log.debug("Destination not specified in RAW SEND message"); + return false; + } + props.remove("DESTINATION"); + try { streamSession.connect( this, dest, props ); return true ; } catch (DataFormatException e) { _log.debug("Invalid destination in STREAM CONNECT message"); - if (verbose) notifyStreamAccept ( "INVALID_KEY" ); + notifyStreamResult ( verbose, "INVALID_KEY", null ); } catch (ConnectException e) { _log.debug("STREAM CONNECT failed: " + e.getMessage()); - if (verbose) notifyStreamAccept ( "CONNECTION_REFUSED" ); + notifyStreamResult ( verbose, "CONNECTION_REFUSED", null ); } catch (NoRouteToHostException e) { _log.debug("STREAM CONNECT failed: " + e.getMessage()); - if (verbose) notifyStreamAccept ( "CANT_REACH_PEER" ); + notifyStreamResult ( verbose, "CANT_REACH_PEER", null ); } catch (InterruptedIOException e) { _log.debug("STREAM CONNECT failed: " + e.getMessage()); - if (verbose) notifyStreamAccept ( "TIMEOUT" ); + notifyStreamResult ( verbose, "TIMEOUT", null ); } catch (I2PException e) { _log.debug("STREAM CONNECT failed: " + e.getMessage()); - if (verbose) notifyStreamAccept ( "I2P_ERROR" ); + notifyStreamResult ( verbose, "I2P_ERROR", e.getMessage() ); } } catch (IOException e) { } @@ -661,11 +691,11 @@ public class SAMv3Handler extends SAMv1Handler try { try { streamSession.startForwardingIncoming(props); - notifyStreamAccept("OK"); - return true ; + notifyStreamResult( true, "OK", null ); + return false ; } catch (SAMException e) { _log.debug("Forwarding STREAM connections failed: " + e.getMessage()); - notifyStreamAccept ( "FORWARDER_FAILED" ); + notifyStreamResult ( true, "I2P_ERROR", "Forwarding failed : " + e.getMessage() ); } } catch (IOException e) { } @@ -677,17 +707,18 @@ public class SAMv3Handler extends SAMv1Handler boolean verbose = props.getProperty( "SILENT", "false").equals("false"); try { try { + notifyStreamResult(verbose, "OK", null); streamSession.accept(this, verbose); return true ; } catch (InterruptedIOException e) { _log.debug("STREAM ACCEPT failed: " + e.getMessage()); - if (verbose) notifyStreamAccept( "TIMEOUT" ); + notifyStreamResult( verbose, "TIMEOUT", e.getMessage() ); } catch (I2PException e) { _log.debug("STREAM ACCEPT failed: " + e.getMessage()); - if (verbose) notifyStreamAccept ( "I2P_ERROR" ); + notifyStreamResult ( verbose, "I2P_ERROR", e.getMessage() ); } catch (SAMException e) { _log.debug("STREAM ACCEPT failed: " + e.getMessage()); - if (verbose) notifyStreamAccept ( "ALREADY_ACCEPTING" ); + notifyStreamResult ( verbose, "ALREADY_ACCEPTING", null ); } } catch (IOException e) { } @@ -695,33 +726,16 @@ public class SAMv3Handler extends SAMv1Handler } - public void notifyStreamAccept(String status) throws IOException + public void notifyStreamResult(boolean verbose, String result, String message) throws IOException { - if ( streamSession == null ) - { - _log.error ( "BUG! Received stream connection, but session is null!" ); - throw new NullPointerException ( "BUG! STREAM session is null!" ); - } - - if ( !writeString ( "STREAM STATUS RESULT=" - + status - + "\n" ) ) - { - throw new IOException ( "Error notifying connection to SAM client" ); - } - } - - public void notifyStreamOutgoingConnection(String result) throws IOException - { - if ( streamSession == null ) - { - _log.error ( "BUG! Received stream connection, but session is null!" ); - throw new NullPointerException ( "BUG! STREAM session is null!" ); - } - - if ( !writeString ( "STREAM STATUS RESULT=" - + result - + "\n" ) ) + if (!verbose) return ; + + String out = "STREAM STATUS RESULT="+result; + if (message!=null) + out = out + " MESSAGE=\"" + message + "\""; + out = out + '\n'; + + if ( !writeString ( out ) ) { throw new IOException ( "Error notifying connection to SAM client" ); } diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java b/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java index ca83e8a1f..f02591bb6 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java +++ b/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java @@ -77,7 +77,7 @@ public class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handle } private void initSAMStreamSession(String login) - throws IOException, DataFormatException, SAMException{ + throws IOException, DataFormatException, SAMException { SAMv3Handler.SessionRecord rec = getDB().get(login); String dest = rec.getDest() ; @@ -131,8 +131,7 @@ public class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handle public void connect ( SAMv3Handler handler, String dest, Properties props ) throws I2PException, ConnectException, NoRouteToHostException, DataFormatException, InterruptedIOException, IOException { boolean verbose = (props.getProperty("SILENT", "false").equals("false")); - Destination d = new Destination(); - d = SAMUtils.getDest(dest); + Destination d = SAMUtils.getDest(dest); I2PSocketOptions opts = socketMgr.buildOptions(props); if (props.getProperty(I2PSocketOptions.PROP_CONNECT_TIMEOUT) == null) @@ -148,7 +147,7 @@ public class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handle if ( rec==null ) throw new InterruptedIOException() ; - if (verbose) handler.notifyStreamOutgoingConnection("OK") ; + handler.notifyStreamResult(verbose, "OK", null) ; handler.stealSocket() ; From 1bc4cb382ed49a79f506e1fd534177f1108ef4f5 Mon Sep 17 00:00:00 2001 From: mkvore-commit Date: Sun, 5 Apr 2009 21:35:56 +0000 Subject: [PATCH 094/688] SAMv3 : protocol better specified, and small changes in the code reflecting the new protocol --- apps/sam/doc/protocol-v3.txt | 17 -- apps/sam/doc/sam.3.0-protocol.txt | 477 ++++++++++++++++++++++++++++++ 2 files changed, 477 insertions(+), 17 deletions(-) delete mode 100644 apps/sam/doc/protocol-v3.txt create mode 100644 apps/sam/doc/sam.3.0-protocol.txt diff --git a/apps/sam/doc/protocol-v3.txt b/apps/sam/doc/protocol-v3.txt deleted file mode 100644 index 9aacfa124..000000000 --- a/apps/sam/doc/protocol-v3.txt +++ /dev/null @@ -1,17 +0,0 @@ -telnet localhost 7656 -HELLO VERSION MIN=3.0 MAX=3.0 -SESSION CREATE STYLE=STREAM ID=essaiSamIn DESTINATION=tYhjbFlFL38WFuO5eCzTvE0UBr4RfaqWMKlekGeMoB-Ouz7nYaWfiS-9j3jMiZT7FH~pwdmoSREOs2ZbXK84sR59P~pPfeCMxnJrk57f3U9uKzXkesjkKWYco3YAGs-G8sw8Fu2FBx0Do57yBdA9~j8Zq6pMjmgPBXCLuXG3vo0Z8zUWCjApJyFY6OXYopHck9Fz9vKy7YhC6zXFHfEuNHVkAooduiLd~aCoGij0TW3lH2rTVU-lx-DUdi6edxQ5-RvDNkXfikvytoCpRkivbNVytjCJLk~7RNU4FpBD20wTZWNJmEG3OY3cjNjawJVFdNjtgczh9K7gZ7ad-NjVjZVhXEj1lU8mk~vAH-2QE5om8dstWUwWoNDwmVDlvIJNKzQmahG~VrpFexFHXO0n3fKIXcSgWGOHDExM8w9neCt7AxUjxPDtXXuYNW~bRwcfiL-C9~z4K9rmwiTPZX0lmsToSXTF28l7WAoj~TMT9kZAjQeFRRWU5oW5oxVuonVvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABngJSS8xMyF4t82otZmCDhrKjbm-QLMtOLoumwR28ebDHEd4clF6O7aRa3d3yRH7p - -telnet localhost 7656 -HELLO VERSION MIN=3.0 MAX=3.0 -STREAM ACCEPT ID=essaiSamIn - - -telnet localhost 7656 -HELLO VERSION MIN=3.0 MAX=3.0 -SESSION CREATE STYLE=STREAM ID=essaiSamOut DESTINATION=EYUpJFeW9tiubXR0aOjvCJ~ndj3xN0Wn-ljuGdbpOEttPg7nj0VCTOQDJ~FAolzn9FIDdmR3VjM0OFFDT46Q5HN4vShXFE2VNC8e3~GjzxJfaJhijRC2R9oIOzsNlzKtInD2o9lh0PxPioNMCigwmgWuqlQHs4tjWeaYRAtooHxbrtuoCIhIdGfyVV-nAcPiyYbouKq3leETXE~4kBXm-LfWfyPtrv6OuDk3GBVVcthv19GYBmnl2YI8HpJjc-G-TvNkgYishjzIJyEW-Xrpy43R4ZBXlyQqnheGLlbOEY8NLDbyNHLRMMOGbcr~67SVE3Iw3RqQ3Dhrkq2FCaQwcDucfIUCCbOfCZgu0hlnCkS42xsUvegQeiwMxbdI~h9v7vcR3yFFOrHX6WQvIZSbFLKNGArGJcfmOJVLqw1wTC4AgYXjk3csVDPd-QWbMXOuodyBgrg27Ds2BBYTsVXWskoo6ASsMIQZ6jMfL7PkY9dPLCRParIyzb9aPmf~MntNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHNqwgkhJnBW4ymaRsdVmITAha-ff0UiALfKSlznqp5HcSewgMHbzQ0I01TQytFnW - -telnet localhost 7656 -HELLO VERSION MIN=3.0 MAX=3.0 -STREAM CONNECT ID=essaiSamOut DESTINATION=tYhjbFlFL38WFuO5eCzTvE0UBr4RfaqWMKlekGeMoB-Ouz7nYaWfiS-9j3jMiZT7FH~pwdmoSREOs2ZbXK84sR59P~pPfeCMxnJrk57f3U9uKzXkesjkKWYco3YAGs-G8sw8Fu2FBx0Do57yBdA9~j8Zq6pMjmgPBXCLuXG3vo0Z8zUWCjApJyFY6OXYopHck9Fz9vKy7YhC6zXFHfEuNHVkAooduiLd~aCoGij0TW3lH2rTVU-lx-DUdi6edxQ5-RvDNkXfikvytoCpRkivbNVytjCJLk~7RNU4FpBD20wTZWNJmEG3OY3cjNjawJVFdNjtgczh9K7gZ7ad-NjVjZVhXEj1lU8mk~vAH-2QE5om8dstWUwWoNDwmVDlvIJNKzQmahG~VrpFexFHXO0n3fKIXcSgWGOHDExM8w9neCt7AxUjxPDtXXuYNW~bRwcfiL-C9~z4K9rmwiTPZX0lmsToSXTF28l7WAoj~TMT9kZAjQeFRRWU5oW5oxVuonVvAAAA - diff --git a/apps/sam/doc/sam.3.0-protocol.txt b/apps/sam/doc/sam.3.0-protocol.txt new file mode 100644 index 000000000..c544068db --- /dev/null +++ b/apps/sam/doc/sam.3.0-protocol.txt @@ -0,0 +1,477 @@ +---------------------------------------------------------------------- +Simple Anonymous Messaging (SAM version 3.0) +---------------------------------------------------------------------- +Client application talks to SAM bridge, which deals with +all of the I2P functionality (using the ministreaming +lib for virtual streams, or I2CP directly for async messages). + +All client<-->SAM bridge communication is unencrypted and +unauthenticated. Access to the SAM +bridge should be protected through firewalls or other means +(perhaps the bridge may have ACLs on what IPs it accepts +connections from). + +All of these SAM messages are sent on a single line in plain ASCII, +terminated by the newline character (\n). The formatting shown +below is merely for readability, and while the first two words in +each message must stay in their specific order, the ordering of +the key=value pairs can change (e.g. "ONE TWO A=B C=D" or +"ONE TWO C=D A=B" are both perfectly valid constructions). In +addition, the protocol is case-sensitive. +In the following, message examples are preceded by "-> " for +messages sent by the client to the SAM bridge, and by "<- " for +messages sent by the SAM bridge to the client. + +I2P communications can take three distinct forms: +* Virtual streams +* Repliable datagrams (messages with a FROM field) +* Anonymous datagrams (raw anonymous messages) + +I2P communications are supported by I2P sessions, and each I2P +session is bound to an address (called destination). An I2P session +is associated with one of the three types above, and cannot carry +communications of another type. + + +---------------------------------------------------------------------- +SAM connection handshake +---------------------------------------------------------------------- +No SAM communication can occur until after the client and bridge have +agreed on a protocol version, which is done by the client sending +a HELLO and the bridge sending a HELLO REPLY: + +-> HELLO VERSION MIN=$min MAX=$max + +and + +<- HELLO REPLY RESULT=OK VERSION=3.0 + +*** In order to force protocol version 3.0, the values of $min and $max +*** must be "3.0". + +If the SAM bridge cannot find a suitable version, it replies with : + +<- HELLO REPLY RESULT=NOVERSION + +If some error occurred, such as a bad request format, it replies with : + +<- HELLO REPLY RESULT=I2P_ERROR MESSAGE={$message} + + +---------------------------------------------------------------------- +SAM sessions +---------------------------------------------------------------------- +A SAM session is created by a client opening a socket to the SAM +bridge, operating a handshake, and sending a SESSION CREATE message, +and the session terminates when the socket is disconnected. + +Each registered I2P Destination is uniquely associated with a session ID +(or nickname). + +Each session is uniquely associated with : + * the socket from which the client creates the session + * its ID (or nickname) + +The session creation message can only use one of these forms (messages +received through other forms are answered with an error message) : + +-> SESSION CREATE + STYLE={STREAM,DATAGRAM,RAW} + ID={$nickname} + DESTINATION={$private_destination_key,TRANSIENT} + [option=value]* + +DESTINATION specifies what destination should be used for +sending and receiving messages/streams. It has to be a suitable +private base64 destination key. If the destination is +specified as TRANSIENT, the SAM bridge creates a new destination. + +{$nickname} is the choice of the client. No whitespace is allowed. + +Additional options given are passed to the I2P session +configuration if not interpreted by the SAM bridge (e.g. +outbound.length=0). These options are documented below. + +The SAM bridge itself should already be configured with what router +it should communicate over I2P through (though if need be there may +be a way to provide an override, e.g. i2cp.tcp.host=localhost and +i2cp.tcp.port=7654). + +After receiving the session create message, the SAM bridge will reply +with a session status message, as follows: + +If the creation was successful : +<- SESSION STATUS RESULT=OK DESTINATION={$private_destination_key} + +If the nickname is already associated with a session : +<- SESSION STATUS RESULT=DUPLICATED_ID + +If the destination is already in use : +<- SESSION STATUS RESULT=DUPLICATED_DEST + +If the destination is not a valid private destination key : +<- SESSION STATUS RESULT=INVALID_KEY + +If some other error has occurred : +<- SESSION STATUS RESULT=I2P_ERROR MESSAGE={$message} + +If it's not OK, the MESSAGE should contain human-readable information +as to why the session could not be created. + + +SAM sessions live and die with the socket they are associated with. +When the socket is closed, the session dies, and all communications +using the session die at the same time. And the other way round, when +the session dies for any reason, the SAM bridge closes the socket. + + +---------------------------------------------------------------------- +SAM virtual streams +---------------------------------------------------------------------- +Virtual streams are guaranteed to be sent reliably and in order, with +failure and success notification as soon as it is available. + +Streams are bidirectional communication sockets between two I2P +destinations, but their opening has to be requested by one of them. +Hereafter, CONNECT commands are used by the SAM client for such a +request. FORWARD / ACCEPT commands are used by the SAM client when +it wants to listen to requests coming from other I2P destinations. + + +----------------------------- +SAM virtual streams : CONNECT +----------------------------- +A client asks for a connection by : + * opening a new socket with the SAM bridge + * passing the same HELLO handshake as above + * sending the connection command : + +-> STREAM CONNECT + ID={$nickname} + DESTINATION=$peer_public_base64_key + [SILENCE={true,false}] + +This establishes a new virtual connection from the local session +whose ID is {$nickname} to the specified peer. + +If SILENCE=true is passed, the SAM bridge won't issue any other message +on the socket : if the connection fails, the socket will be closed. +If the connection succeeds, all remaining data passing through the +current socket is forwarded from and to the connected I2P destination +peer. + +If SILENCE=false, which is the default value, the SAM bridge sends a +last message to its client before forwarding or shutting down the +socket : + +<- STREAM STATUS + RESULT=$result + [MESSAGE=...] + +The RESULT value may be one of: + + OK + CANT_REACH_PEER + I2P_ERROR + INVALID_KEY + INVALID_ID + TIMEOUT + +If the RESULT is OK, all remaining data passing through the +current socket is forwarded from and to the connected I2P destination +peer. If the connection was not possible (timeout, etc), +RESULT will contain the appropriate error value (accompanied by an +optional human-readable MESSAGE), and the SAM bridge closes the +socket. + +---------------------------- +SAM virtual streams : ACCEPT +---------------------------- + +A client waits for an incoming connection request by : + * opening a new socket with the SAM bridge + * passing the same HELLO handshake as above + * sending the accept command : + +-> STREAM ACCEPT + ID={$nickname} + [SILENCE={true,false}] + +This makes the session ${nickname} listen for one incoming +connection request from the I2P network. + +The SAM bridge answers with : + +<- STREAM STATUS + RESULT=$result + [MESSAGE=...] + +The RESULT value may be one of: + + OK + I2P_ERROR + INVALID_ID + +If the result is not OK, the socket is closed immediately by the SAM +bridge. If the result is OK, the SAM bridge starts waiting for an +incoming connection request from another I2P peer. When a request +arrives, the SAM bridge accepts it and : + + * If SILENCE=true was passed, the SAM bridge won't issue any other message +on the client socket : all remaining data passing through the +current socket is forwarded from and to the connected I2P destination +peer. + * If SILENCE=false was passed, which is the default value, the SAM bridge +sends the client a ASCII line containing the base64 public destination key +of the requesting peer. After this '\n' terminated line, all remaining data +passing through the current socket is forwarded from and to the connected +I2P destination peer, until one of the peer closes the socket. + +----------------------------- +SAM virtual streams : FORWARD +----------------------------- + +A client waits for an incoming connection request by : + * opening a new socket with the SAM bridge + * passing the same HELLO handshake as above + * sending the forward command : + +-> STREAM FORWARD + ID={$nickname} + PORT={$port} + [HOST={$host}] + [SILENCE={true,false}] + +This makes the session ${nickname} listen forever for incoming +connection requests from the I2P network. + +The SAM bridge answers with : + +<- STREAM STATUS + RESULT=$result + [MESSAGE=...] + +The RESULT value may be one of: + + OK + I2P_ERROR + INVALID_ID + +The socket is closed immediately after the message by the SAM +bridge. If the result is OK, the SAM bridge starts waiting for +incoming connection requests from other I2P peers. + + * {$host} is the hostname or IP address of the socket server to which +SAM will forward connection requests. If not given, SAM takes the IP +of the socket that issued the forward command. + + * {$port} is the port number of the socket server to which SAM will +forward connection requests. Is is mandatory. + +When a connexion request arrives from I2P, the SAM bridge requests a +socket connexion from {$host}:{$port}. If it is accepted after no more +than 3 seconds, SAM will accept the connexion from I2P, and then : + + * If SILENCE=true was passed, all data passing through the obtained +current socket is forwarded from and to the connected I2P destination +peer. + * If SILENCE=false was passed, which is the default value, the SAM bridge +sends on the obtained socket an ASCII line containing the base64 public +destination key of the requesting peer. After this '\n' terminated line, +all remaining data passing through the socket is forwarded from and to +the connected I2P destination peer, until one of the sides closes the +socket. + + + + +---------------------------------------------------------------------- +SAM repliable datagrams : sending a datagram +---------------------------------------------------------------------- +While I2P doesn't inherently contain a FROM address, for ease of use +an additional layer is provided as repliable datagrams - unordered +and unreliable messages of up to 31KB in size that include a FROM +address (leaving up to 1KB for header material). This FROM address +is authenticated internally by SAM (making use of the destination's +signing key to verify the source) and includes replay prevention. + +After establishing a SAM session with STYLE=DATAGRAM, the client can +send datagrams through SAM's UDP port (7655). + +The first line of a datagram sent through this port has to be in the +following format : + +3.0 {$nickname} {$base64_public_destination_key} + + * 3.0 is the version of SAM + * {$nickname} is the id of the DGRAM session that will be used + * {$base64_public_destination_key} is the destination of the + datagram + * this line is '\n' terminated. + +The first line will be discarded by SAM before sending the remaining +of the message to the specified destination. + +---------------------------------------------------------------------- +SAM repliable datagrams : receiving a datagram +---------------------------------------------------------------------- +Received datagrams are written by SAM on the socket from which the +datagram session was opened, unless specified otherwise by the CREATE +command. + +When a datagram arrives, the bridge delivers it to the client via the +message : + +<- DATAGRAM RECEIVED + DESTINATION=$base64key + SIZE=$numBytes\n[$numBytes of data] + +The SAM bridge never exposes to the client the authentication headers +or other fields, merely the data that the sender provided. This +continues until the session is closed (by the client dropping the +connection). + +---------------------------------------------------------------------- +SAM repliable datagrams : forwarding datagrams +---------------------------------------------------------------------- +When creating a datagram session, the client can ask SAM to forward +incoming messages to a specified ip:port. It does so by issuing the +CREATE command with PORT and HOST options : + +-> SESSION CREATE + STYLE=DATAGRAM + ID={$nickname} + DESTINATION={$private_destination_key,TRANSIENT} + PORT={$port} + [HOST={$host}] + [option=value]* + + * {$host} is the hostname or IP address of the datagram server to + which SAM will forward datagrams. If not given, SAM takes the + IP of the socket that issued the forward command. + + * {$port} is the port number of the datagram server to which SAM + will forward datagrams. + +When a datagram arrives, the bridge sends to the specified host:port +a message containing the following data : + +${sender_base64_destination_key}\n{$datagram_payload} + + +---------------------------------------------------------------------- +SAM anonymous datagrams +---------------------------------------------------------------------- +Squeezing the most out of I2P's bandwidth, SAM allows clients to send +and receive anonymous datagrams, leaving authentication and reply +information up to the client themselves. These datagrams are +unreliable and unordered, and may be up to 32KB in size. + +After establishing a SAM session with STYLE=RAW, the client can +send anonymous datagrams throug the SAM bridge exactly the same way +he sends non anonymous datagrams. + +Both ways of receiving datagrams are also available for anonymous +datagrams. + +When anonymous datagrams are to be written to the socket that created +the session,the bridge delivers it to the client via: + +<- RAW RECEIVED + SIZE=$numBytes\n[$numBytes of data] + +When anonymous datagrams are to be forwarded to some host:port, +the bridge sends to the specified host:port a message containing +the following data : + +{$datagram_payload} + + +---------------------------------------------------------------------- +SAM utility functionality +---------------------------------------------------------------------- +The following message can be used by the client to query the SAM +bridge for name resolution: + + NAMING LOOKUP + NAME=$name + +which is answered by + + NAMING REPLY + RESULT=$result + NAME=$name + [VALUE=$base64key] + [MESSAGE=$message] + + +The RESULT value may be one of: + + OK + INVALID_KEY + KEY_NOT_FOUND + +If NAME=ME, then the reply will contain the base64key used by the +current session (useful if you're using a TRANSIENT one). If $result +is not OK, MESSAGE may convey a descriptive message, such as "bad +format", etc. + +Public and private base64 keys can be generated using the following +message: + + DEST GENERATE + +which is answered by + + DEST REPLY + PUB=$pubkey + PRIV=$privkey + +---------------------------------------------------------------------- +RESULT values +---------------------------------------------------------------------- +These are the values that can be carried by the RESULT field, with +their meaning: + + OK Operation completed succesfully + CANT_REACH_PEER The peer exists, but cannot be reached + DUPLICATED_DEST The specified Destination is already in use + I2P_ERROR A generic I2P error (e.g. I2CP disconnection, etc.) + INVALID_KEY The specified key is not valid (bad format, etc.) + KEY_NOT_FOUND The naming system can't resolve the given name + PEER_NOT_FOUND The peer cannot be found on the network + TIMEOUT Timeout while waiting for an event (e.g. peer answer) + +---------------------------------------------------------------------- +Tunnel Pool Options +---------------------------------------------------------------------- + +These options can be passed in as name=value pairs at the end of a +SAM SESSION CREATE line. + + inbound.nickname - Name shows up in I2P router console. + inbound.quantity - Number of tunnels, default 2. + inbound.backupQuantity - Number of backup tunnels, default 0. + inbound.rebuildPeriod - Obsolete - ignored - the router controls rebuilding + inbound.duration - Tunnels last X ms, default 10*60*1000. + (change not recommended, will break anonymmity + if it works at all) + inbound.length - Depth of tunnels, default 2. + inbound.lengthVariance - If negative, randomly skews from + (length - variance) to + (length + variance). If positive, from + length to (length + var), inclusive. + Default -1. + inbound.allowZeroHop - Zero hop allowed? Default "true". + outbound.* - Same properties as inbound. + i2p.streaming.connectDelay - If 0, connect ASAP. If positive, wait + until X ms have passed or output stream + is flushed or buffer fills. Default 0. + i2p.streaming.maxWindowSize - Max window size, default 64. + +---------------------------------------------------------------------- +Client library implementations: +---------------------------------------------------------------------- + C/C++: libSAM: http://www.innographx.com/mpc/libsam/ or i2p/sam/c/ + Python: Python/I2P: http://dev.i2p.net/contrib/apps/sam/python/index.html + Others: See apps/sam/ in I2P CVS. From 4d27f18710ef9fd48d5a3b35716abf225830388b Mon Sep 17 00:00:00 2001 From: mkvore-commit Date: Mon, 6 Apr 2009 05:29:03 +0000 Subject: [PATCH 095/688] SAMv3: rawTests Demos scripts updated --- apps/sam/Demos/rawTests/samForward.py | 2 +- apps/sam/Demos/rawTests/samIn.py | 2 +- apps/sam/Demos/rawTests/samOut.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/sam/Demos/rawTests/samForward.py b/apps/sam/Demos/rawTests/samForward.py index 6d55da5f7..5a65b6614 100755 --- a/apps/sam/Demos/rawTests/samForward.py +++ b/apps/sam/Demos/rawTests/samForward.py @@ -16,7 +16,7 @@ else : if len(sys.argv)==3 : name = sys.argv[2] else : - name = "essaiSamForward" + name = "rawSamForward" sess = socket.socket( socket.AF_INET, socket.SOCK_STREAM) diff --git a/apps/sam/Demos/rawTests/samIn.py b/apps/sam/Demos/rawTests/samIn.py index 0f89deb17..ee2c3cbc8 100755 --- a/apps/sam/Demos/rawTests/samIn.py +++ b/apps/sam/Demos/rawTests/samIn.py @@ -11,7 +11,7 @@ import sys if len(sys.argv)==2 : name = sys.argv[1] else : - name = "datagramSamIn" + name = "rawSamIn" sess = socket.socket( diff --git a/apps/sam/Demos/rawTests/samOut.py b/apps/sam/Demos/rawTests/samOut.py index 1a521334f..b035d8997 100755 --- a/apps/sam/Demos/rawTests/samOut.py +++ b/apps/sam/Demos/rawTests/samOut.py @@ -13,7 +13,7 @@ import time if len(sys.argv)>=2 : name = sys.argv[1] else : - name = "datagramSamForward" + name = "rawSamForward" if len(sys.argv)>2 : message = ''.join([s+' ' for s in sys.argv[2:]]).strip() From fcbfd7554fd7be592b5c3448b7b7f47042021f5c Mon Sep 17 00:00:00 2001 From: mkvore-commit Date: Mon, 6 Apr 2009 06:39:30 +0000 Subject: [PATCH 096/688] SAMv3: alternate form of calling SAMBridge.main. Usage message updated. --- apps/sam/java/src/net/i2p/sam/SAMBridge.java | 80 ++++++++++++++------ 1 file changed, 58 insertions(+), 22 deletions(-) diff --git a/apps/sam/java/src/net/i2p/sam/SAMBridge.java b/apps/sam/java/src/net/i2p/sam/SAMBridge.java index 34dc4c59b..54a721ce1 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMBridge.java +++ b/apps/sam/java/src/net/i2p/sam/SAMBridge.java @@ -51,11 +51,17 @@ public class SAMBridge implements Runnable { private boolean acceptConnections = true; private static final int SAM_LISTENPORT = 7656; + public static final String DEFAULT_SAM_KEYFILE = "sam.keys"; - public static final String PROP_DATAGRAM_HOST = "sam.datagram.host"; - public static final String PROP_DATAGRAM_PORT = "sam.datagram.port"; - public static final String DEFAULT_DATAGRAM_HOST = "0.0.0.0"; - public static final String DEFAULT_DATAGRAM_PORT = "7655"; + public static final String PROP_TCP_HOST = "sam.tcp.host"; + public static final String PROP_TCP_PORT = "sam.tcp.port"; + protected static final String DEFAULT_TCP_HOST = "0.0.0.0"; + protected static final String DEFAULT_TCP_PORT = "7656"; + + public static final String PROP_DATAGRAM_HOST = "sam.udp.host"; + public static final String PROP_DATAGRAM_PORT = "sam.udp.port"; + protected static final String DEFAULT_DATAGRAM_HOST = "0.0.0.0"; + protected static final String DEFAULT_DATAGRAM_PORT = "7655"; private SAMBridge() {} @@ -199,10 +205,14 @@ public class SAMBridge implements Runnable { } } + static class HelpRequested extends Exception {static final long serialVersionUID=0x1;} + /** * Usage: *
    SAMBridge [ keyfile [listenHost ] listenPort [ name=val ]* ]
    - * + * or: + *
    SAMBridge [ name=val ]* 
    + * * name=val options are passed to the I2CP code to build a session, * allowing the bridge to specify an alternate I2CP host and port, tunnel * depth, etc. @@ -211,24 +221,34 @@ public class SAMBridge implements Runnable { public static void main(String args[]) { String keyfile = DEFAULT_SAM_KEYFILE; int port = SAM_LISTENPORT; - String host = "0.0.0.0"; + String host = DEFAULT_TCP_HOST; Properties opts = null; if (args.length > 0) { - keyfile = args[0]; - int portIndex = 1; - try { - port = Integer.parseInt(args[portIndex]); - } catch (NumberFormatException nfe) { - host = args[1]; - portIndex++; - try { - port = Integer.parseInt(args[portIndex]); - } catch (NumberFormatException nfe1) { - usage(); - return; - } - } - opts = parseOptions(args, portIndex+1); + try { + opts = parseOptions(args, 0); + keyfile = args[0]; + int portIndex = 1; + try { + if (args.length>portIndex) port = Integer.parseInt(args[portIndex]); + } catch (NumberFormatException nfe) { + host = args[portIndex]; + portIndex++; + try { + if (args.length>portIndex) port = Integer.parseInt(args[portIndex]); + } catch (NumberFormatException nfe1) { + try { + port = Integer.parseInt(opts.getProperty(SAMBridge.PROP_TCP_PORT, SAMBridge.DEFAULT_TCP_PORT)); + host = opts.getProperty(SAMBridge.PROP_TCP_HOST, SAMBridge.DEFAULT_TCP_HOST); + } catch (NumberFormatException e) { + usage(); + return; + } + } + } + } catch (HelpRequested e) { + usage(); + return; + } } SAMBridge bridge = new SAMBridge(host, port, opts, keyfile); I2PAppThread t = new I2PAppThread(bridge, "SAMListener"); @@ -244,10 +264,11 @@ public class SAMBridge implements Runnable { t.start(); } - private static Properties parseOptions(String args[], int startArgs) { + private static Properties parseOptions(String args[], int startArgs) throws HelpRequested { Properties props = new Properties(); // skip over first few options for (int i = startArgs; i < args.length; i++) { + if (args[i].equals("-h")) throw new HelpRequested(); int eq = args[i].indexOf('='); if (eq <= 0) continue; if (eq >= args[i].length()-1) continue; @@ -263,11 +284,26 @@ public class SAMBridge implements Runnable { private static void usage() { System.err.println("Usage: SAMBridge [keyfile [listenHost] listenPortNum[ name=val]*]"); + System.err.println("or:"); + System.err.println(" SAMBridge [ name=val ]*"); System.err.println(" keyfile: location to persist private keys (default sam.keys)"); System.err.println(" listenHost: interface to listen on (0.0.0.0 for all interfaces)"); System.err.println(" listenPort: port to listen for SAM connections on (default 7656)"); System.err.println(" name=val: options to pass when connecting via I2CP, such as "); System.err.println(" i2cp.host=localhost and i2cp.port=7654"); + System.err.println(""); + System.err.println("Host and ports of the SAM bridge can be specified with the alternate"); + System.err.println("form by specifying options "+SAMBridge.PROP_TCP_HOST+" and/or "+ + SAMBridge.PROP_TCP_PORT); + System.err.println(""); + System.err.println("Options "+SAMBridge.PROP_DATAGRAM_HOST+" and "+SAMBridge.PROP_DATAGRAM_PORT+ + " specify the listening ip"); + System.err.println("range and the port of SAM datagram server. This server is"); + System.err.println("only launched after a client creates the first SAM datagram"); + System.err.println("or raw session, after a handshake with SAM version >= 3.0."); + System.err.println(""); + System.err.println("The option loglevel=[DEBUG|WARN|ERROR|CRIT] can be used"); + System.err.println("for tuning the log verbosity.\n"); } public void run() { From 12625a46c2b4d25f307c8c4d4a1ec53cc9014f69 Mon Sep 17 00:00:00 2001 From: mkvore-commit Date: Mon, 6 Apr 2009 07:54:19 +0000 Subject: [PATCH 097/688] comment added in net.i2p.client.streaming.ConnectionHanler --- .../java/src/net/i2p/client/streaming/ConnectionHandler.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java index e53e701a1..c42491379 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java @@ -81,6 +81,8 @@ class ConnectionHandler { boolean success = _synQueue.offer(packet); // fail immediately if full if (success) { SimpleScheduler.getInstance().addEvent(new TimeoutSyn(packet), _acceptTimeout); + // advertise the new syn packet to threads that could be waiting + // (by calling waitSyn(long) if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) synchronized (this._synSignal) {this._synSignal.notifyAll();} } else { @@ -103,8 +105,7 @@ class ConnectionHandler { long now = this._context.clock().now() ; long expiration = now + ms ; while ( expiration > now || ms<=0 ) { - // check we have not missed a SYN packet before entering - // the lock + // check if there is a SYN packet in the queue for ( Packet p : this._synQueue ) { if ( p.isFlagSet(Packet.FLAG_SYNCHRONIZE) ) return ; } From d54695e5420a0e2ab73a11a1c875c5081f599774 Mon Sep 17 00:00:00 2001 From: mkvore-commit Date: Mon, 6 Apr 2009 08:16:19 +0000 Subject: [PATCH 098/688] add dependency to streaming lib to apps/sam/java/build.xml --- apps/sam/java/build.xml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/sam/java/build.xml b/apps/sam/java/build.xml index bb692f2db..73ddab1c0 100644 --- a/apps/sam/java/build.xml +++ b/apps/sam/java/build.xml @@ -4,6 +4,7 @@ + @@ -18,6 +19,7 @@ + @@ -28,20 +30,20 @@ srcdir="./src" debug="true" deprecation="on" source="1.5" target="1.5" destdir="./build/obj" - classpath="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar" /> + classpath="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar:../../streaming/java/build/streaming.jar" /> + classpath="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar:../../streaming/java/build/streaming.jar" /> - + @@ -52,7 +54,7 @@ + + From bc831d3c3517b453b6b5485c398b7637bb7e0433 Mon Sep 17 00:00:00 2001 From: mkvore-commit Date: Mon, 6 Apr 2009 09:53:01 +0000 Subject: [PATCH 099/688] Changelog: SAM : big bug in SAMv1Handler corrected --- apps/sam/java/src/net/i2p/sam/SAMBridge.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/sam/java/src/net/i2p/sam/SAMBridge.java b/apps/sam/java/src/net/i2p/sam/SAMBridge.java index 54a721ce1..d6ed45ffe 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMBridge.java +++ b/apps/sam/java/src/net/i2p/sam/SAMBridge.java @@ -320,7 +320,8 @@ public class SAMBridge implements Runnable { SocketChannel s ; SAMBridge parent ; HelloHandler(SocketChannel s, SAMBridge parent) { - this.s = s ; + this.s = s ; + this.parent = parent ; } public void run() { try { From 495558a949b47931516b9ab2cda542cc2aa22b71 Mon Sep 17 00:00:00 2001 From: mathiasdm Date: Mon, 6 Apr 2009 17:53:32 +0000 Subject: [PATCH 100/688] New application for I2P: desktopgui. Should eventually replace systray, and have more functionality. To use the application, you need to (by default) add the following to clients.config : # desktopgui clientApp.6.args= clientApp.6.delay=5 clientApp.6.main=desktopgui.Main clientApp.6.name=desktopgui clientApp.6.startOnLoad=true --- apps/desktopgui/LICENSE | 15 + apps/desktopgui/build.xml | 69 ++ .../desktopgui/resources/howto/howto.html | 261 ++++++++ .../desktopgui/resources/logo/logo.jpg | Bin 0 -> 1105 bytes apps/desktopgui/lib/appframework.jar | Bin 0 -> 264341 bytes apps/desktopgui/lib/swing-worker.jar | Bin 0 -> 11103 bytes apps/desktopgui/manifest.mf | 3 + apps/desktopgui/nbproject/build-impl.xml | 629 ++++++++++++++++++ apps/desktopgui/nbproject/genfiles.properties | 8 + apps/desktopgui/nbproject/project.properties | 68 ++ apps/desktopgui/nbproject/project.xml | 19 + .../org.jdesktop.application.Application | 1 + apps/desktopgui/src/desktopgui/Main.java | 109 +++ .../src/desktopgui/resources/Main.properties | 11 + apps/desktopgui/src/gui/SpeedSelector.form | 160 +++++ apps/desktopgui/src/gui/SpeedSelector.java | 176 +++++ apps/desktopgui/src/gui/SpeedSelector2.form | 116 ++++ apps/desktopgui/src/gui/SpeedSelector2.java | 174 +++++ apps/desktopgui/src/gui/SpeedSelector3.form | 223 +++++++ apps/desktopgui/src/gui/SpeedSelector3.java | 286 ++++++++ .../src/gui/SpeedSelectorConstants.java | 25 + apps/desktopgui/src/gui/Tray.java | 138 ++++ .../gui/resources/SpeedSelector.properties | 7 + .../gui/resources/SpeedSelector2.properties | 6 + .../gui/resources/SpeedSelector3.properties | 16 + .../src/persistence/PropertyManager.java | 72 ++ apps/desktopgui/src/router/RouterHandler.java | 38 ++ apps/desktopgui/src/router/RouterHelper.java | 13 + .../router/configuration/SpeedHandler.java | 34 + .../src/router/configuration/SpeedHelper.java | 28 + apps/desktopgui/src/util/IntegerVerifier.java | 32 + build.xml | 9 + installer/resources/wrapper.config | 4 + 33 files changed, 2750 insertions(+) create mode 100644 apps/desktopgui/LICENSE create mode 100644 apps/desktopgui/build.xml create mode 100644 apps/desktopgui/desktopgui/resources/howto/howto.html create mode 100644 apps/desktopgui/desktopgui/resources/logo/logo.jpg create mode 100644 apps/desktopgui/lib/appframework.jar create mode 100644 apps/desktopgui/lib/swing-worker.jar create mode 100644 apps/desktopgui/manifest.mf create mode 100644 apps/desktopgui/nbproject/build-impl.xml create mode 100644 apps/desktopgui/nbproject/genfiles.properties create mode 100644 apps/desktopgui/nbproject/project.properties create mode 100644 apps/desktopgui/nbproject/project.xml create mode 100644 apps/desktopgui/src/META-INF/services/org.jdesktop.application.Application create mode 100644 apps/desktopgui/src/desktopgui/Main.java create mode 100644 apps/desktopgui/src/desktopgui/resources/Main.properties create mode 100644 apps/desktopgui/src/gui/SpeedSelector.form create mode 100644 apps/desktopgui/src/gui/SpeedSelector.java create mode 100644 apps/desktopgui/src/gui/SpeedSelector2.form create mode 100644 apps/desktopgui/src/gui/SpeedSelector2.java create mode 100644 apps/desktopgui/src/gui/SpeedSelector3.form create mode 100644 apps/desktopgui/src/gui/SpeedSelector3.java create mode 100644 apps/desktopgui/src/gui/SpeedSelectorConstants.java create mode 100644 apps/desktopgui/src/gui/Tray.java create mode 100644 apps/desktopgui/src/gui/resources/SpeedSelector.properties create mode 100644 apps/desktopgui/src/gui/resources/SpeedSelector2.properties create mode 100644 apps/desktopgui/src/gui/resources/SpeedSelector3.properties create mode 100644 apps/desktopgui/src/persistence/PropertyManager.java create mode 100644 apps/desktopgui/src/router/RouterHandler.java create mode 100644 apps/desktopgui/src/router/RouterHelper.java create mode 100644 apps/desktopgui/src/router/configuration/SpeedHandler.java create mode 100644 apps/desktopgui/src/router/configuration/SpeedHelper.java create mode 100644 apps/desktopgui/src/util/IntegerVerifier.java diff --git a/apps/desktopgui/LICENSE b/apps/desktopgui/LICENSE new file mode 100644 index 000000000..61febe901 --- /dev/null +++ b/apps/desktopgui/LICENSE @@ -0,0 +1,15 @@ +Desktop GUI: provides a simple GUI for I2P. +Copyright (C) 2009 Mathias De Maré + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; only version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. \ No newline at end of file diff --git a/apps/desktopgui/build.xml b/apps/desktopgui/build.xml new file mode 100644 index 000000000..c7ba1be11 --- /dev/null +++ b/apps/desktopgui/build.xml @@ -0,0 +1,69 @@ + + + + + + Builds, tests, and runs the project desktopgui. + + + diff --git a/apps/desktopgui/desktopgui/resources/howto/howto.html b/apps/desktopgui/desktopgui/resources/howto/howto.html new file mode 100644 index 000000000..ea1b025c7 --- /dev/null +++ b/apps/desktopgui/desktopgui/resources/howto/howto.html @@ -0,0 +1,261 @@ + + + Small Guide to I2P + + +

    Small Guide to I2P

    + +

    So, what's this all about?

    + +

    I2P builds up a new net inside the usual internet, connecting nodes together + via encrypted connections. + It is a JAVA prgram with its most used part (the encryption of the data) written + in handoptimized assembler code. + It will use your bandwith, your RAM and your CPU. It will use them all up if you + do not limit it. + I2P will route unknown traffic through your node, even stuff you dislike. + As that data is encrypted, nobody knows whats data went to or drom your node. +

    + +

    + First, ALWAYS use the latest stable release. + Development releases are called "mtn version" and are marked with a -, e.g. + 0.6.5-1. Those are usually useable by all but could do harm to your I2P + experience. + You can get the latest MTN builds from my eepsite echelon.i2p, but always + remember: I built them, you need to trust me not to changed the code! + After you get the right "i2pupdate.zip" file, put that file into the I2P + directory and hit restart on the router console http://127.0.0.1:7657. + Do NOT deflate the zip file! +

    + +

    + I2P is very dynamic - after startup it tries to get known to other I2P routers + and measures their speed - you need to wait some 10-120 minutes until your + I2P router knows enough other ones to obtain full power of I2P. +

    + +

    Filesharing

    + +

    + I2P is able to do anonymous filesharing. + But as there are NO gateways between real net and I2P, you can only share/ + download torrents from within I2P. Look e.g. postman.i2p or planet.i2p. + You CANNOT use azureus, utorrent or any other usual client. + You cannot download anonymous torrents from mininova, piratebay or else. + You need to use I2P internal torrents and I2P aware programs like + I2Psnark (builtin, suitable for 1-20 torrents) + I2PRufus (external, python, high CPU load, suitable >20 torrents) http://echelon.i2p/i2prufus + I2P-BT (external, python) + I2PsnarkXL (mod of I2Psnark made by fwd) +

    + +

    + There are also gnutella and edonkey clients: + i2phex for gnutella (http://echelon.i2p/i2phex) + imule for edonkey (http://echelon.i2p/imule) +

    + +

    + Remember, as I2P uses other routers to route your traffic via 1-6 other PCs, + your transferrates in P2P are slower than in usual internet. + But you are anonymous, no one can easily (within 2 months-2 years) get your IP! + torrents inside of I2P reaches up to 50 kb/sec, usual are 10-20 kb/sec per torrent + i2phex reaches up to 20 kb/sec, usually 5-10 kb/sec + imule in times reaches 10 kb/sec, usually 5-10 kb/sec +

    + +

    + In I2PHex and imule you can just tell "share file or directory", in torrent + you need to create a .torrent file and upload that (and ONLY that small .torrent) + file to the trackers like tracker.postman.i2p/ +

    + +

    + I2P is a smaller net (1000 users) which grows slowly. As of which amount of shared + data will slowly rise. +

    + +

    + I2P is anonymous and it does not censor - there is no administrator. + There IS unwanted stuff like kiddyporn, nazism, or else. + If you dislike all this, do not use I2P. + There is NO way to prohibite this stuff to appear in a anonymous net like I2P. + (as that stuff is available shows the anonymity and transfer function of I2P + is working well enough) + You can delete the destinations in question from your local hosts.txt file (or + deface them) which will partly prevent you to reach those bad sies by accident. +

    + +

    Internet (the websites)

    + +

    + Only one outproxy (gateway I2P - webpages in usual Internet) is working. + It is NOT official from I2P, I2P does work without. + If that outproxy is slow, offline, gone,.. I2P still works on and cannot do + anything to change that failure. + That outproxy translates usual internet webpages into the I2P net and you can + reach them via your Router. + The best way for usual webpages is TOR, not that outproxy. + Remember: the owner of the outproxy got ALL traffic from all I2P users + visiting Internet pages and will risk that into the police! +

    + +

    + This proxy is false.i2p. In newer I2P routers it is enabled, but not in + older ones. Go to http://127.0.0.1:7657/i2ptunnel/index.jsp tunnels page and + click on the eepProxy tunnel. Change the entry for the "Outproxies" to false.i2p + and save. On the tunnels page, stop the epproxy tunnel and start it again, + now your router will use the false.i2p outproxy. +

    + +

    + No other (known) gateways are setup and running. No one we know will run + the gateway for torrent or any other P2P data (and risk his life). +

    + +

    Bandwidth

    + +

    http://127.0.0.1:7657/config.jsp

    + +

    Setup your bandwith wisely. Know your linespeed!

    + +

    + E.g. most common terms are: + 1Mbit = roughly 100 kbyte/sec + 10 MBit = roughly 1100 kbyte/sec + 512 kbit = roughly 50 kbyte/sec + or in germany: + 16000er = roughly 1500 kbyte/sec + 6000er = roughly 600 kbyte/sec + 1000er = roughly 100 kb/sec +

    + +

    + Set your bandwith limits to 10% under your line speed and burst rate to + your line speed. + Set the bandwith share percentage to: + >80% if lowest bandwith setting is >50k + >50% if lowest bandwith setting is >30k + >20% if lowest bandwith setting is >16k +

    + +

    There is no shared bandwith under 16k.

    + +

    + Limit your participating tunnels (shared bandwith) on: + http://127.0.0.1:7657/configadvanced.jsp + with the line: + router.maxParticipatingTunnels=500 +

    + +

    + 2000 is for roughly 600 kb/sec - very high value with high CPU load + 1000 is for roughly 300 kb/sec + 600 is a good value for 150-200kb/sec + 300 is roughly 90 kb/sec + 150 roughly 50 kb/sec + Remember: even failed tunnel requests will result in a part tunnel on the hops in between! + Those said, there are far more part tunnels unused than used in the live net under load, which + results in slower bandwith per tunnel in the end. + It is wise to first limit the bandwith and afterwards the part tunnels, e.g. set some more part + tunnels and let I2P reach the bandwith limit instead of the part tunnels limit! +

    + +

    What is shared bandwidth?

    + +

    + I2P transports your date from the client to the server through 1-6 hops + (other I2P routers). Each of this hops sees the data from you as "participating + tunnel" - which is the shared bandwith of them. + With this in mind, I2P needs some amount of this shared bandwith at some + amount of routers. + Share as much as you are able of - others will thank you! +

    + +

    + With the "share percentage" set like above, you will obtain enough speed for + your own traffic and obtain some participating tunnels (if >16kb/sec) with some + noise traffic to hide your traffic in the stream. +

    + +

    + With release 0.6.5 there is some method to prefer your own traffic ahead + of shared traffic which will result in better experience to you! +

    + +

    Addressbook

    + +

    + I2P uses a local addressbook to link short DNS names with the internal used 512bit + hashes (which are destination IDs). + Those links are saved insside the hosts.txt and userhosts.txt files in the i2p + directory. + Hosts which are not in those files cannot be reached via the short DNS names + and a error message with "jumper links" will appear. Those links will ask + some hosts services and forward to the correct site (if the site is known to them). + Those hosts services just made a form to add new "hosts" and those results public + available. + You can subscribe to those hosts service and let your hosts.txt file be updated + automatic. Go to http://127.0.0.1:7657/susidns/subscriptions.jsp SusiDNS + and enter the hosts services into the textbox (and save afterwards): + http://www.i2p2.i2p/hosts.txt + http://stats.i2p/cgi-bin/newhosts.txt + http://tino.i2p/hosts.txt + http://i2host.i2p/cgi-bin/i2hostag + You can add one of them, two or all. + SusiDNS will now ask those hosts for new entries to the hosts.txt and those + will be added to your hosts.txt. The userhosts.txt will ONLY be updated by + yourself (the user) and not be published into the net! + Remember, names once set could not be changed! If you loose your key (destination + ID) to your eepsite, service,..., there is no way to change the linking + between the DNS name and the (lost) destination ID automatic! Only manual by each + user itself - great topic to discuss of need to renew DNS hostnames. + As this subscription will not update old entries, you can "deface" unwanted + eepsites with a false key and if you hit the bad name in browser by accident, + you will not see the bad stuff! +

    + +

    Out of Memory errors

    + +

    + If your router hits the Out of Memory error - check your logs! + Usual point for OOM are to much torrents in i2psnark - i2psnark is a real + memory hogg and >10 torrents it requiers hell a lot of memory! +

    + +

    + Maybe it is possible for you to increase the wrapper memory config. + This ONLY works if you start the I2P service restartable with console + (on Windows). + In I2P directory edit the wrapper.config file and change the values: + wrapper.java.maxmemory=256 (or even to 512, IF possible) + Afterwards shutdown I2P complete (the service) and restart it. +

    + +

    Blocklists

    + +

    + Sometimes attackers trying to flood the I2P net and try to do some harm. + And some folks setting localnet IPs as their internet reachable address. + To prevent those bad router to harm the local router, I2P implemented + a local blocklist system. It is NOT integrated automatic as it could + really harm your I2P experience if setup the wrong way. + The way to enable blocklists is: + Get the file http://zzz.i2p/files/blocklist.txt and copy this file into the + I2P directory. + On http://127.0.0.1:7657/configadvanced.jsp set the option + router.blocklist.enable=true - click on Apply and restart the router + with the restart link left on router console. + The blockfile.txt file follows a special order, you´ll get it if you read it. + The first entry is the reason to be shown on http://127.0.0.1:7657/profiles.jsp + at the bottom in the shitlist section. + The second entry is the IP or the dest ID of a router. + Right now there are only private subnets in the blocklist AND one chinese router + which floods the floodfill DB while restarting every few minutes with a different + router ID and far to less bandwith for being a floodfill router. +

    + +

    (By echelon -- echelon.i2p )

    + + diff --git a/apps/desktopgui/desktopgui/resources/logo/logo.jpg b/apps/desktopgui/desktopgui/resources/logo/logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f1b5ccfc8728618281387d7a4b8ed11dedb8f39e GIT binary patch literal 1105 zcmex=>Qk2%s@GP0R~1ECMHHk7A|Ip0#;@gHg+ZsMotDHVG&VD zK`|-i@QBFC(`H=0b_1wEk`ZVavQZ4I42(?7EI=t`L12h5GP5u-!z^QD5@Z%qWMDB2 z6n0cnE}Z!P76T7Z5-2LjV9(%jJ@4S+OL6n{+*w zeZ`}!kPTbTpL6Z#pQn*i<5MhNG-;+uKp1~?`89K)X0ZE^o@$*VFd^iK9DY*3rIB~;yC zQulT2#uu~87uJ@(I9?{5Z6<&6N_Op1se9TY2XkhxHr}vQZ%N<|5r5Ik2VJGN8>*=s zH0lrb^=W7GnW$Iz#rX6*Q7f6froVH)_Z_}3d+ScYwD2@*PO0q+c70rvbRaVBrC)gB z;-cwk7k|vUD6;xNMXe7@HrGEnsems98OFoKGL^?&^S#olr(Bq(!qk{wo0+*#!CI|y*HVjfcE8rFu+OmG@IB`Id#$_4rM4DV ztu}q^zcekfY*wasL+z0Z8x~A*o0uxOB=*dfZ098Y&ZJuj$E4YE-22i=y_vyc(Qn4RrJhN^RI->RA|#S$y;yfo-e~zdGuOr z=aJ8A(@nm`=6&6LHZk?aBi);Cw7XXQcp$Ut6vK_n2A7%M{#X1&2e_IZ1y>_zyj!1jd-t24JN&<~F-PI<}n)Wp-$EiplWwG1Gv|YPZ zwsC&&;4DhI^H($VoUh#acA3ZL;@0u=bG+$!p7Z3xTcg@v-}s&De0|hUR`{sBI#lKJ zslIBW)#1B;cQ3uj$-^A_<+;;~A1Uhs)?9LxX}-3-Td_6On?r;%I4`hxqu~iidCyt?OF9SKI`Y} G|2F|xQl?`7 literal 0 HcmV?d00001 diff --git a/apps/desktopgui/lib/appframework.jar b/apps/desktopgui/lib/appframework.jar new file mode 100644 index 0000000000000000000000000000000000000000..0b8ff0145f010a8e1e9bfe065704cb4ad675f761 GIT binary patch literal 264341 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

    b3C#0adb5Et>9)K}}&1LqoU{fLLrP^m)I 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

    +&eO1NyNTy);90KwDiffI8) z--!nry4QCSfm7-v12m<`F{v;gZc4#*={w~C>P!86r!sK*JE_1K;0z3$DrZn33=A=b z8u{*fGe|WC#u*$qL!9bB>`sgEc`jog;j;tMrx zYjg!Dr>B+tpS ztfON$8eijAEZMNQW^&E8miF;`Ybd{F@9sS{yWlD8s=3fGYdULo0UmYORtAWk-tIfA zbih{o&YD0Y(*+2Yt_d{aobQ|*5Ce{7vYxPHGNve4%YZ*Eb))ZW@`)cMIj)$x93?2o z7cH+aa0oP}6UTSaY!T_xu~9gx6leOP=RoGMNBz8GR&$G}wY@>pfxanCDUB9Ek1*)% z?r@8wWRn&j{zy9Zep~xFxLwU41uXC)%7~nXz31c?(QYlQ+SF@R8ymLA1tflNpUyCgCd9J3%VYx^2OZnZI)_1Ilw zx+F4<@j+D84{Dk@5pfy^g@8(}PV0)+wjDd+oqBs(ww(+5UD)4(N6J%3#0`L^1+Bdh z@hz(@jiu&5`6|Xa?Y3cED_$GgFvr2CiaMDH@#u{9V?%d4IX5xuxVM_%SXzZ=d>_q` z@^MDaXmvT!!Vt|3iJAyTSx})gvH>%Mj>SdIahfqp(<6>{ZeFymd1dp7xb3hRyC$OF z-Dv&BC=eVhY+QldUE34+vSDY(?hDnmpjk4-RK^XDOij^n4?>V2RuoIK7bBbDzk@ok zCPHe6g*4$*Bc@j@g>{&rsO*}wsuPYZ>|ww|OQ=Y1a9*g|z*B7rnQ=2Smirs?ic7WK z=Td#nyoN0PGu-L1f^y7rZ)8Wsob#MYY`F+#WxmqU zH$lPMYsb>CD6p|G=xi?C&>6xFhUXsTTprq0TWNUf|=D=X6EMph(NpH~$PFAlS;rgbrXACI~hO0Z;t zxoR!22Wb#V#Mkgz%TfUsY}d2(8)tDSe|jhdm!J$>26N9-W1XU&ZK z@2B``@2Ct$<(GPJp3+bqzek2v$5oTIo>0BRP}C*T?;#m+Kx)D~LrfM`FMU)-npElu zMSmQoaA zn&CYr$C=>rM^LIw9F&;{Wfq}KDQEU);Wxx#0Sq#}Xmp(4i|N0}KO>3!982Ia7}<{_ z{5y(}?D5r2m;fImZt__uIa|01ulTwg<@_UGuZ{)U$P(itMZawf>H zotcCf^g>NV-59Ehp-XRq)ij*5B{df<+9z}#mA^l)AvGV3+In>P#&QO)ZE<7ijd^McF-Kw9K74 ziXBU_<0*E63~?r!;?i5JYjKNnii@<;7Iy;0EugrC6t{@t7SkFSOJVDD*CST7Dxp(U z19Vh4=^6=l$pNWswlkbs#x~FiW*P?}#aPNyb|{vi!nz!-loj}nUV|HrRWj6BjohP| z${=LJ${M-rP>f+OF5iy=I}836>Cf%NTO`R14Nl)^0AcIq@V~@$2L(+sUyVp!l&TyJ1Iea?~jZFgG^RHQhh_by-+LAh1 zRdLrnG5HxeezI=nXdK!xVH{fG$R3vU?C}QGU*13vQWH@22M1*S5!t9ARr{W|;Q>0K z4E&+&kI+Lb;#k2c660T87PKH+k}o z%mJ~EFS-w=)a%kmVvkb5rbh%m!5W|-*zkU-;sJaZK-Z-Ef$*Qd$Szw0iJfa`_qBv# zxDGAFcQVm1D#K}?Lv4D73_)7vJ@hSgp^J>v9c* zY>u(h!d6bTrKDIcrvd@%(E&b{f>=_gACk8ol`~8W7fhn9?5;Dj&9suaudo%KW0ii5 z;ru!)^m!z6FQ9qw9&8Y7MLD`tp>9%UC2~wA0BY&8HKG@M)*)#*B3mDnnb|7M;+3;# z`TNYc-(W0mhy@31JIe_3#&F-kdKk%lC+UWAZ;J}|K8HT)#+MQ^Z(^6~-&hJNigavf z={&cKcJBgp$3g6ja@B4as@e@hjn<65)aV7((=k%33F-)urh2DiWTZY3@N&F~>(yAd z{=&As+td_T;FDmTFl=Rh6x>0Dj*Bkey1SDjI5q4$YE9 z&AZp{+qQFgTkEdvhJ&Y?1JeVftHfd=e$Nz{8=$vBZd|nnc!UKv9o56kunW3qRzz$y zEn*CNuG?a|v%@Pig5`N5T7rM&ed-u;n&Q{pR#lgil@69HTC!o~+GdP7`5{6OgPx z^9+JVc_@$4%&m{($6kKpT2bCn*{e@PJCbS_>4UTv<= z8gzFc$B}AY)uM17N4H=Gi#$%W(^aTj#j)4gf;ocvb<|Kv29NhzWES=%F)#r=PBf5` zD%Pv+Ct8|-6m^`=8*p_f_sI*ZfhMzL&cf=fW|soCD_d(m@&Z6j|E80%4~sEZV?jDA$rH{a<$%P(NMjs$~Q!NvzgHR zjgzMm)z>YM**m3O*FKEaMJJuKX62Gac6<4Cn-;CvxNJSnDf_%d-@x`RDoOWEp=7 zwr+LnU{2#Dj4^-;&~Q+kL*gD4VnQf5tKn)fp#9L)qfI(ZUn`a&Q8obl?})_Feearq z$lK^U*?-dmXd?gZ~Yek zAYq#yOzW`MP@%yyVMvq%!-rzvI37FU^Ph!}o$|G34EELN;BzQ!zSU)}URSgI9Dx ziTVs#_qKGva7PjwS@s#uWl77ny-2>%Y$)EuhHN3+gBz-qtH@P*<;dvnZuY1!4$+OqxPnysynLN%=ywQj=~({^7@4lpI%5Wpj& z$vG~m(!(&&Zibrn-Fs_V+H2CEqjI;0Pg1G3<&JO3Yxws!9AXxX-Ru@MIM<%BNoDX6CtR>eZv=Jl-|>Q%o7 zv3w*YaHLm6;W5+4hPuwBuDsE}XO3&ml$6dSPbqU?>z(;D?^w)mPjFZ=Rb1#~}>D}_q}n?Z z8|@ZZ>TQ$N-gepOwaMw;IdZnwF6Vmq;_&v!KJR?F)YDKfa4^E29Y?v0BOQ-W+LC;qU7)hvxU zvdYu*evT4oxdhq0ul($(mq0f3ZxRI*-STDw8_;#qk~Mj>*uEr{PA%mE0X9OWt2NE9!loFY`+oIS`M8Mz|TS5pIUd!h{C7A2X;~tSaHB6P^|DGY ztM#%bAx(O3PRLqcPV!})FVIaHo1C5zlMFf1oqnix*ogd!(nu9)r>oY+0;k6zTD}!P z)j2ioc+&oAOfpP)WqTE_0EmI_9GLpH6k@=~D@6fr5z_B&=cFfGq<}SbmQ|({%$Pv! z(e-JgFPj3K6uIFEsO6^_HFw221+d$SH-& zGOGw(Lr$mk^0E%#a?z!Xaz><~_8a@8_56J;yWj>E6Va;%Saa)z;r!(moWZekRnLkn z1dGw7O^x0-PdWG0gQ{<_7Dm&jp%}~N>vksRovQxOq5~4fZtK5oA5kIO_ikuAueGUd z*RD3khIznHDqa4JUX{N}vlLQa%kokNp&SySSh7lNsk7TUGC^+B;3P+TBb?Gm?ldux zMKHrJU6k6>{WxCxF}$PoJbXH&myfM1yGGNBdWBYBI+X!q=|X2tn+D-O+_;d(13hM0 zfD=xVI7brDh10B-w_;{liiManHTF$Xjy31ErhkdtIO>oq!9%>JR2x^9bGN6y3EUF~u8dqp~9Kane9uTmahEgY5e!BG@TnQ5BPWSVe9 z`X7@FO1E@r( zjL%I)zgDZmBCktphvREQ?R%RbmGo{~U(eYtvES1A-_iQt)A~QkX#5b(zz@+}{12_d z|4dq$3-skb6Y^jB{<^OM zcf8!{#nnp;StYrR{YKKkeuLyrA|co7y)+>==sl@!Zv%ZQ_nitth&uf=Po=MM1V%cJ z{*p?nu?8%1r^}kfiLjzg5Ry5C7AYu*Pd0B#5=na4iK1Vy!b$FQZKOfLf;} z>5O*9l%jfHm*j#3vDO)2G!Y(Aw6iqZW;eaUC zI@3$h_diZA$Ls4%y?`&)IosCy_}(!Gxc(oURt#Jt@^ahceW>-)}*x4Z+U0ZIosKx zE!ycjZN76(R}9a0U`QZnh?2q?Ov87OpXKw;dS{$h){>R8c59m#q2-}nD?qZ0L7zrT zZo4R#!=hrb_wp&JGzwZ9}3@wAWS`#M`YmbA3?l$m{m4*h z-EldiB^Ss4q6HW3YjSDpo{m-`Qz%JLy>4IoUS>5^>Rr?hYt1Orvt|cH@@MnV-0sFY zr7J)s=*5i07RNh7<`6oTp|ElKBSy8yKd*H_h=c z#S#y=yG!?YY3DwO$5vvX7NLYq!#GN`l(IIpW$f68{~wa4**s=wS7v|7@bpcdlCxVd z8Yg+WbtZYP+I-?{V0GtCP1O&x1Kl0zQw(N$Hvl!7($qGimx(inqjoE0Nx^7g%r6rk z{lci%cTuuw$s!k1qlwuyGkTpk(-!35A^oefoE`xx9;Tm{?q1ox7!#fy9UAm7^6Mb| z6_qVJjrl*=GnN^M{4hjwL}o1V(*XTxxtX%a*DCWhoTSM2!TMdrCoqxNwUJ+xH50`m zdJt3T;N@c->WDnIh^alyZf*%WwQ1_S53G@NmOgMh(pij)mtHYb(>V+)BK-mj0ce}G zm_KFyhQ`P-8?rSv75KIw_slQChl(t;qN2W6<+(sUAn(&qD?GhlpKg>71?Vzg?K|fM zPP?;P4fL-9Ynu`{dmLg~7d6AjGU2(v>2L@)vR4iUa=&~!hxKH8@XtMBrTET-g(U)_49Xkk2?Y_1)Uu59$!zBp(iN2Yh9K+xGnd zzS=(;$j9ZLzAWj&-tD}H66ApZD}jf6=lZ~TuR}k3C1$P0 zvPqAWnhtev)mU?0TYJrd6Kc+DxrqMDLy#Eh1SH$n8aOvN?+@fD=K}z{^e-C90(Kvm zd*FP~xzTrS3Y-r+HwVssE?5?PDR6F)#{=vUn8RD0j|5mHd_5h`x+u&Kuv>T$E53q5 zpE=$$M?~O!)VVFdY5F$<3>{c-pOX7%{n*YLdu%k;jAilMu8ZSsa=Gt(EO72{?)06z z0_Se$rEoQT=$}9MpmQqVr(jJj6x5rkAhk55_0u-Zv~#cODMVS~?gwhn*vEoN`dmA^@Z}GTlDOzWXM`jxw$H zb{O<1FS<(tw5fA*n}&NSh=_u>y&0DAxq}r&0wS0*xD{uhMS=575JSAU5FQ$V_Zjp$ z+V38j^-FpSi`D4sPVGBVj#^b4>&8{x zDg~x3N^_Qbkr`x{zv%*rhzevA(F{S!0on)}C#*7Z6YI(@|Hw+yI?#VjE7z=9xqjJ_ zwarV{vmGO3Kq1-)UJRcvXzf5ptaiex9#X#n!A;yCrErQMWw1LBZS9Q~luA%R#EjH1 z6s8@Qud!4F-b5s*%thaYuKr2OnwJ{O>pT~=y{a#(9ADPfnL(qOF}8J4l~wKXk{0+| zphfgD%jC#XJiwr|!Fh3`@sjn+HZ5DX3?@uu(sB&T8Ik`cn{m5lGgKubU~I#4AZ%LZ zrV!SWQ>=nc<9yxUyW1dI%5*3M@(O%&q&v63dce+$3~jn@@clQ>NE(0IMPWqdgf}#2 z#r7P^EXM^-2-mXyTHcrebS-jwL^Vjcz=n8ADZ0sBEgP>gchGcm2cJbAbe(yA2fwRT znx+F=HFr>La|hctcW`WT2k|y{5Px%r6fk!XTyuvOFn4fW?z1QnT$h?|;q6aI(PG^1 zG@-yTL{tlA{}xPzQ;CPA^nf%>N~uMLTCbt~#D}4n#06L~mXokTe^Y~Hej!B1iS-Tj zsmeprA4A3zhR6@gK>bn`{W3_uV1k@YqY}CyRE&Cc^~0w4VaCpQICjX?HIlkUJuEd= z@99?BYLu;n08cnNti&d|nt;n=z{u;d*UU&@P_(H1jCt>?OG!S?n1CjkEKw` zQmDZ6aR7}U%2F7`LKp|VH5qbi1_ah@rg=WT7#Cqoe4_kLK1NQVgSO3ga@RKB6}I_i z?mE$H-gukUQ?iB=%5OK4x?Z;%8Q~&~kykrGk#^r5PLS%M>jX8uN$pN03N(CR9~xdo z!v~=lJ{XlZD8;auQAIk;W_9c{tH&8eQi#+rX$^Ok@2i`FDXhBu)HrV@YS+DC>S)3B z`AMfrjZ$A>(sSvBDjqb{Z)x;YAYb34i>zLsnyWpe@@0ga@>DmVJ~dTc1x!;xeB6lO zRjfRUzjVv3+4vm%9Lz=f*(m*-c?6Q5j|$QWg#1~6@E*=USn(7}(K*5if`JTFSPEQV zODSx~Z}eJ50~)$O|3Z%q*?~Kqq4=y|s$sTWbSMVqm)cI}9~6x&pgXk7Pz*FfF_4xG z>@pOUy&8&Z7>a8dit8APcQ6$1WGLRnP`sNqK3uSkgYw(>5!!gDcpC?qHV&eVgSxb_ zs#k6N3~hXXHhz{i;{U;UkTx>;fEx!miFKYCoZFdlvm;G;#9PG0o$Xa8%o-`> zUS0Y`4Up_Zw@7K}?2-dAe2`~f%Yo80t@^2Skf#p~rGrY2$n=9UgD7VAoAqQ2bkOP@ zW;A>4PB5ww&KGImmuTRZrPBEd!u{uDoby$ggmv>wtefBBe1kYw-=dk16dcenQeAd^ zyvBgSi3$64LAd^Bq}TrtGoa>tX%0`9{B1BeGOi<;N!7|2XYRJyW6-d`{!!v?e@VJ2 z>E{lRf$l&V=2po>7vB-?V2*(rmqa=;;(iDrzx zhIZH56XW^7ok|VUq|}`*L*3(Llsk*^K9`xX^V!$Bv*)IJHgr*LN5Z-CIiG`4Gebrn<`+g2!-w(RJfZyf2g09F6Wq z`uhYYu1%_HLE6&k>6T7Bp?;X2dk@LP1-WgmPt6*BL}riRuV+Z)o=XdN(ZchXf_A20 zw~TT3$TatSTKZ(cmd5j2dNn}nDe!?bh_DLe^M;SOLzTtTE;WV?ze5tkM%*2Ko2MQt zh7I3-?Fjzb=Xmvlz0~}3j+p3m*>l@KOZP*x@kZMBVX1a+mRk1~nd06mjqXQG#h(u= z)+M6Qvaa^}ikokhWbO6+K73nP+gpr(jM@S<7{>+fJyPKwV2(aX>GzsazYvzXi*qtg zsq^c`-iSxj8s7ao*F_vHA+$ZS^kIdVN)EZ5>9$c$#kK1Nj5ATo`(s!Q>N32Ddm&wt$IHM08|oZ5%RTOROGK z#fJ4q_9e=ExtC>bDp2Nj%50^~9hA8< zQs#GY4P@Inox66Y8faOgf5x%NV{!@(O*HD^sd)%RtRpPu`(%Xs9~N^8W2B4y_($cm zEsCt4-ee0ygT0vmlqkdRmueo)bSU%`^(aHs!?RpD@cOIy197n=W0%N;*rjq@>}_Ff zU#1=;P2n6cRQxY8!_d8!yY|PJ#g>eK##T+?9qp*>t=eipB~*x9moa3I*x6Q zwioQt9n^9sJ-Uk?eVkelJiMV=S_{_lDQdZoTJEQo&rl1JjW;k}XBQl=hp7W6Vc0)U zk3B*iNH)Vd?&M9Oh6au`0Q|7*NCWW0veN?a!_t-k;D_ZLD8_S9hgQn*+(|}@Hy=DJ zI9A&uY<|q$f?um1eH1O;C)uuFkU_EM*s{;c(AXDc6whN~UzXa~SEM2KyiAR~fOhZm zX!pLz@%9?Ny}F`IyYjZJ7bEG<!J=NY@T_ARO1hh@(bX&h>o!DDx^ zt#)-G49?FWu!B&KR`uWpjH>i*J7Xh=4&qFPVD&JK_lC(ZZ@AQWBV?>MQjYUR$vm$H zRpMXDufi6o4ZA{ezkV3*it_O&xq`&MhVfu~Ix2+ce*LJo;@jx{OwHy!o8uhhC_4*?27ki~pIkmWbb73{r%4G|yof#-5e<*4luVFfGzI5@t zC5);5O`xEj`INc(*y{QtCTKu=Qsv7aABS5KH-cgW9$opW2(s3d9 zR%R12^K*5=LS8I5LNR0Q_=Jh3h>Tw2F|vB`*xPN#=Y>j3lM8B5EQqkxjj%S;@Ep~M ziVUmQ_*5ctuasz{%^|%K03(IW%E2SGHA$LBV8LO&angktA?+;1 z8iN&GPhHsWz31FV*ELo~-8SD$Ul^dnNGCV#ZrhG-wQ3n<74sJKp_Gh3#j5GqqN4hE zlX_Jc%iLzoXxjCq5qa8pVIv|6g1I_>D|^+X2n>m6nPrqd!uCeA{?0A9M+<8@WkqV? zzUhA6(#x)x@>74^;ps%#O^&CpZkIzkM|w502*P#ER$SSRGKvxXsJpR<_};d-w@N91 z1KqPqZ&{1=EJ(-#04&z+fdg+T1OOV5+`9O$1dJ}LN z%n=M}l43Nh_HZf91&H$-Ut6#RD64;pHuY)=k(EI;C(3}58 zfSzhpL}J!=g<`!0G6msN-(X*f%Agd-2g~<3xIr4g5P2i~D(}h2dl~NATxaMp?Y&F7S|VxbcS%J$D#_9qkoj~1bTcICH>xM2E8%8qCA(NSv{jZI@C;X z$}_S!($o;XI>re$mqq*W|7K7J*u}@@Nb|E~H;WshAm2l#A^;(>;i!uhl>jM$qC|qn z{!}Am8@m%9l!m??2rg3_rDJt3{^Ih6xkKSr=-*s2^lC3gRQ|epU^zKSE5?*^sZ|%4 zbxAd)$2ne7SGB&>`|2!ntV}9ntR|OX1U*$R)BMm|OOoNk2)A5j`Z5cjn7+&fprr$y z_h5S%u6M}e4DHgLJzHc^8!DnWg@fG>+S+@=c*a@Fn?lYB44p!G__!;y?N06iflv3L zd!#zEvrWUh2Rs2Fd2j$wtkNfSeG|wDLO{S^FCZ#3ZtKMRULXr)QJ~@r&V(%8teAFy z+w~;@$~epM!WSSDAx_vTP1ilh2n+XRbs%e`DZr)c+JFG0YaKc67#17N%3Xa0&(7X> zK$F0}KAZ=KK?CHh+fo`B%=8L43Cz zDCEl`+F6%D)1zl}=D-=QymxrY{khMUJRcSnA3bU7yBQGuQ&xSiV1R<@t|tv%4hBno zDHqO@Au`DqkU~xqWvb%Tftc(1bevw){ixn?=`dYp<76YrY1e36fYqt@uYShysFt?~ z*P{tpqUo0UvJ6<~3vm|zr_Y~$YUOA2x6>|p`ogk}8UY{ML54y(>miS;@;`Bq;oV0) z84nAf{PA3|1v# ziXFM?Ow=5?P6g@NIbr&l9JsI+jM{~(-n1Jg;T9^6%c?jYyg7L7fvDMyo{>|DEU7=4 ztv6Dw@_7nS8SXncrxQ(FN{>pCf9mS1jKlgF*)v&BWITP{Bdz+R2YSlr(C?QGn(C`3 z&_YLg?qE)!CKvCRKZMqlcD4H+{^aQKA3R9_}STN>`f|MYo%Ob$6g zU|1agD~{Pp>8~=@egTYyDTK5zacVdO_cC1&iZcR)jmK+1npp)1#o2~#H<1i_OXcM* z1Io<*5P^kh3C{nc@_K#XrKz&x)xAMa0>to`49&YTBGNAJ6E^{L%h;DHyHa{dCEW_! zTo#SOQ{_b!1K!G^vh>n_ky0rwn|>4dRoP9|H?#E8NCmGs^pZkGVh0|Pq=k)Io{h4F zzek4!cKkN(w@u{U^Hp1|oq62;QfOS$0FZJL~+PiN}oEMHnQNWxZq+2+gkBpku5zO*W2 zG#}6QWk-^sH+gm@v64DRFXxu2|JgDjDyLrd_;S9+Vc@q~>CnqL#?$ds=}f5SW1?R|4#|IN>jgGKVPj+*Z6pI zkn4PTMhiHSb7!1xQ8M}iW&6w&YCXKAy)I>|$XOqF4^)m}Yrcc1p6kpjrsoV%|2qEwj@v)z z*vJ!{c9D~XaVF- zeAF~B%05rSl>q#CzAx`qKRef}R~>xDzfWE5yx*4(s0$k!`ysipr*l1Qo4UK9ln=|z zzB;?PMQ&A(H*0)#dUKmb{1}jJL&VR{61hE$2-1`P^N-0L0g;5b+$DFbBORJ|r@S+b z2L`fIRt4Bh-WXv20GBM6_>919oRopvDWQ}F~ zUKHmnBc~0)LAS@2?q}sgL>3_MCQlNBj1k94IOp5l!TFT~+E#|&IeBc*6$7HuUpWJJ zwsbZd7aiTlA@70JUXXwCMtp=HBlE~_>s)r;p1l|6D0wc~jaxkvNQt2ll3C_17%6y_~A2fPt#TlLr(iMHE-++{GqqM?5 z-9x}@M`1+R{GH^|=1-&cxJG_1o5WMOy7$JyIj&#T*s@G3=4*9*YN+bP56jkWMHxF8 zhBj(H2YO={=qxIYH5^mO5&D5!PP=XB_*-fBN0`!&(%##cYT`3- z)rmcava~d{@~+&nM$LKgz%Y_1c1L7X!_yBuDm7d5+hG~aTT{c+lb#?wD&$Di)%iM_ zB;3a`3wpO5wE8%Akm^px=`K>;!>^wRm)gmsT9>Im+wsFPCVz|`&SY^l6qIc;5u??g zy2x*03s{X~8a*t6SV&fuWtI|w>BF^D+gLKGy5te5Qwyp7+7S)!K6XFsxdjJae}N^J zn{#3`XFB<~TcjfWJYMsCL*Ix(AE~l7n$bk3CziH;tB0#jmX}BDhrOoFEvZj6*eLVE zD8}25_XlFuKT9xjTPkDkI|tY_rjE%+)uF_c>_Q|~Hhq7Lv3MMPhNG~|o`gB}6eIOC z`}T`+7Is{_h%CPk^TkWBfObqGUSe0jj9vG4Dd7jC z{2`LMABQ8p5#kduM7ZR#lLQr5 z!<5*?(|f6mFiPk(z6wMtv5csTy3h9UZ=Ua}JWn{)NB`QrWf+EC;DF62m~8BZ5Q`k- zJ$e0#`q}KBT8nk;Bc!0!OX#Zj5w@_X##vp!<{zg@N)>@>D`_O@fdK%Mt=c`tm6<+6 z(ih;!S)9Y@v*WkAy=CjJR$nGKeGA6cJrWtV0i@`>mW#KxnqZDP$Z+p_m9~9U zUwdBx7ghH5f5?Gh5X1s=P%IQsRBUM!5JbR^b$}t1lD5UJwYvkmb;VX}*Y56)RdJBi1|72 zh%-=%LbI15K6_~epWTcUxg1IAUQ;1;H)DvVfE^AU>RSLQr0$qJD7F^D97F03vuV0e z%H5%Y)KWwWNdLiSSCn3tqGE-AO8<~g@2-ofRTTZ9EKuQkUCRE@5iN@&loijp&zqwA z+9{N~Y?%iFrz&)}IJvQi`EdGL3gWErA2*3==5SWX`w5W?)~?ayZ&|3G`|;Z zY&0}Oq!b$am4>;{C3Z1aLuF`KEJ^(O0YM3tB2q}>MJ=={kiuEPq85^<^Q%xZY(N6jIzDCJ?n`C`2tJZ2`$;K8JBJnILHC(#%v5778M8TDfo!YHJesM&Jm<9Ym_p z#vBjGGnMHf@=zth*-pfVj1O01#0sQv9R% zD73^sd(BDcLy{i|ed@7ZqTp?ppD2hm%qiErz|R26r6X8f6d)-l4TBA2#OvA_b~J;|1aX;gP0L zr@q$XqPgv;N*cdQf((FvyNFp$MI#_2O94tv+%#NBeTXGkpjx_s0i~hkr*?KJG!0iZ zuS#sf#W(=BP{2l1wAMcpoBXqlxrpy?@XR#CE@r8*>1k0(nJQ|>LU|)NDU%PNqULZ@ zp*4+zl@tgkhZ7@)hbAn@-Xw9U;qsJ(I9Sz_s-3kgQ-)wgiaT7;c0Jtn0*+W^R13uj zu?#NlJ=W&Ke?$7XbIE z7)j7Sx~8Ca4>@Fa^pcrAbHvW)Wa*VOycoMK`2i?Q4W?mnW(sw@ctr zZ<_?33b#w(fo>-SJJ`(`aERC*vf)7Z1BGP!i<0Q5=4O2*I{Y@AV-V=F zN;hEt6Esv&4!uRi@a64~fI&t1@YYyYG)9tO3~@whzJ2z;C(bCG*5j!i;V$<7>!y}- zN)Ew2W_#_Bf-YEy3Kjb~C>t`20ANioSVYHdz6IH_lm@p8_JwDGVj|&;F|%AaeOi~W zOw0ux(&FOd(&EUwKpBXkGBEe~#HA+7(jsH|X$BjSi1?&PaHjsJ7=-W(k&)C#-WCfO zB!T5Lw-~C$kG2n*!L0skVbcnCSpKg_PXBe<&q3#!pjGIygn0WQhJfow zT*1pRXeh84&56Y*=A?Ps8Dx1z_|uXT?3Dojk!K`&0vP9?6=b3@2t_jxK{g-}$%CsD z8~y4ag&ZLzYlJH=jy%HhtSC%lPzr{4zk*|(-OQ?sskK-P4d&gfB;wGBdx8*helSGz5&{Bl-#2de=0lDMw8l9k-eDs^#$6s2;o6NGW%_jXU!##}nMqC|wK)?l`audZUHoQHLbFkql9FUvzW|+AkG*g*33K(^)hK!#*Ip zDK<3;QI&nG9tJ3&_AnTr>Wq@K{ZMp94V(>7C^40FC#z$CqAEL;A8kX>#6vOKf>mg$ z3g3Zkx;C!i3S8u>8XFu1F0J>Vd(8`a)kAeO@?$TgJHK=O{Ns_Imy3F!)DCDw5^_tZ zE>*xOaWT$^VG;!7G;6_iuDa>a&DzzmM%FGmvg&AM%Gwnj%+EBMP!&F_ z)RiSyQ^bHVg!rKW&Lj%7#5d%MJLGYNep%@0r4T#x$9aXOEjhWOT6=$j7yMe1Z_6S? zfzUwl8;*MR*4{ZbN1GATNchCQ(OPWjl}E^Pay?Rs&Je*>fe0H;KJ=7z zsqy~njhO#UTq3lwlXK&MqDzg;Y#HHke%0rHCRUGozDLPSC8_EEE)I1Z6VVWJ@QTMAK-vgHC) z4TiC&gF+%9n6%UVh8}QfYloxY9H*wp2j^zy7Y#VQH8Gl4`*9u=3yE7R4JfTO`%-|905DFrKy zia!T&mU7mDnTNl{@x(Y5{=a01tKN^OZ8`qe>+AgYCp7vjLHKcW7O$B|mbYrn#DaEF zYNf%&Blts#u{hF#+&0}0v&#)!$dkvqQhi5kZBgR^>n(JA!0Mh#&s55 zkl7WfTyPh23F15vn=eKUW|3u#BGN&|k~xu!1gO1l*df zqQh!Btf2>MEx7qL?!gv(|+rUq>$gkbUzY-4qkR1_#T4gFTAcqYHy6^NDc^qPu#(L!Zk&~8(? z9|8bt0RV2I(@(?0DnXXgN1np-Rs!WPMbfFxV(Fcf2(LbgX$lu^63y4JYZ4TZ2ZmBv zka4Ror$-HPoeYM>k?Cow88W2bU~WYtoW`Y!JtuathHH=+L*mG;%RakHv(;&xF1bEgQ?IkF; z<_!~Wv_m~LE;U3j?yZnhc*m6g5+LV>A)fG~utsLBTS`vH|8~v^zJ5)A)7%8i_d%+H zJqF(o63CgNBw*p6f%5_NC`xtKoNyEd*02(iKsta!H#&5u1MJ$0K^|iJ(Sw~;cu4zC z30;D9PGOn+&o-D{)@N}fTf)Auz7qD8eUp&?5E1(hKa>Ur5(7g6 zBP>e%$K{lWc};I&g1!nuJj=5r*J2-I5S(`eb`o?*Q%OKqlH{?$*wOu$m|Zcym3 zx*?@3E?^4An{ubcf#PlgOSoo%_?x;)Sm3;?F|t*a1z6)g@_wX5MSkG9F4Zi*QYVzY zMQ+McxOpL~49$HM_dfqRW%cY7&gBMp>JN=w7V#=Wz>H2rgO%kTrKW|-BbEEHaBPFP ztylo;G&Jk0=m39>A}V%T0si!LaHfyKj=}arrY$+vgH`$ueIfkI%1@AFkhwmC$kv84 zb|+Gw%+%=&|Eb)WbRD4HhQBEkiw)FBI&puK`7zzY-hw-0d%>Nt!@!wbDffWlhynWv zR|~;9An|TjNP>+VZH&qM-)I{%rGtnLW^^zo-6vk{3Hek$49L3yc`<+zR7p+=%4Qp9 z)m?fwE5luv&1L0yW<@XHaK(NFF$YoB24F8V0-M4OQk=$MK{NqJvpJX-Eug65j-v+@ zbv#j8FO&@%0YzCg^(Vxe0Lp+7SYB%Fi>%mtDY8m&23rxO-_C5IPNbdNAGxtl(Ve@z zcJ3fRis7VQ)MUAM7pz{^v(5ohPqM9($0~3>8ejuokxX$AnYt2WV0cy?@~;ZPj!sZo zqJsfFMXoA}T+)@S0yLh=IU-D>Jsqmjp&A_=)XnAx9tT5Li_q1y*CXAgifEi_=-^t| zJ33+O=!!0FhEDZDr~0B*{LyT|Xt*%6NoTZ557gC=oRcYPyaZRI8xRL>Q+4t=Q3HzJ zJ1L;WB1OF6R(Mj&%^2-hdJC(a%N*TI9N|mB-m#4F9A*LYeUb4>gkYsIYu|C;?nuf& z2&l|dOoZW!23!!goFKwkw>O4%r=HjPFl$DpalqJY@WDT+G=4NkUFGjz0^kzB}bsvd=5-REXvVb;hIqp)A{I!1yH0| z2y}~~6}SWfu4TB7ou2|tPgI>M?@E==Wp2R;@!gm@riq*BZq~3%osve+>y}0JwzHeB zry5}pTRPGxwrR1QwN$qi&v+z1C|fWnw?bmF4Qgo!t_N_bNNK4Uod(cJ%fOK!Yks^U zbwltUFFp<0s4KE+y7UlnEVFwM()KCE1NxWe@mrdjY4PK2`G6 zQ6N%%3mc@?J6BZCJ-QsteRCa<*Ed~7hZ}gnd+|636$g{ zs&oofIt@4j^{JA#jw+o8pbMzdMO5jsc9lpuZII3?nQ2$)Dzbu-(orQdu1aQ(Ha?0f zflIBcmu{dWH&LZqsM2k~8KO^>e05amJ^(#Hm0qAqziC&Ato?`TtdhBQl_-)b=t3P; zGUuvf?r7tus1iKD=&I5mD9Kw?=^d){9_oI>Fr{e^50z$MFMJ66h<8EO5t#M(ti-0L zRhSFJuF$m7$ctyA@YE2qV*t*i8dwth1AnafC2$VJxeU&2aVB*R{M1?y1dRVQb9TS~IrJj0KA^F@op6k!_-SY!zs64+7 zU{KCiaeF`YR=Iug}fH3&WG2`Qj(Ie zqb6@x<+xHQH^Bj+iU-JK9;sBt0#{0yOU1QEHW4hy(!FG;An%q_1uBTYPw3KMy@f2G z7>cm(RP5eWX70RgpbBI-4~Mf3v{VA6dc>o+VBDsqr*b7xanTE7N@$Qa9C=kB`<%=# zG&1uxj0JuT8aYw*|B&@gs5QE-2-F%mftvi;E4wp4O{>|{aPVgV2nIEC~0NxnOM z)Lu+PY%5taMk_o|1~FJH!Vo}DgH&tI{#0XjBs{Q;dIjYY=g3aJwE?kHN&cXJJSe^u~y$;gW?l2~EZ!jI?sAf0TU4ot7QI6#- zVaMRVpILH6O~V`&fw@jXq@t1)!i^LScb@IoaS42koe;5;5^U8@OV}B9R*SEu{wTCK z6#?9gCxK70^CEVEc4(J4ezwAYscKj&`k0D-)d0@l5x86Jtq~uRrccL$4B!h^&(N4tv4m(doD_V^cv&hC z>1Kl!k97;pSv8J)Of&vQAr_CqY1kZ}Q@dnYUvgZnke<=3t})l5-<2~8@xkZ`yjsdr z@D20MxdeAH2zk!3+IolWN$jcp-97^JaQLrK*U)X^CC+Y6Z@zJE$WM zusS@!TJZvF#TzzZKG^yPgJsqZe8={vVkip4^HHfdnoJ5yo(o7|d6KOMvUULfjW4;H z!w9cCxy(vCcOT^A4ax{`D-?hEam!!eoHw zmkIW0KfH^mR|?GDct)_Q;1#1|tSq>%rC}aghT08xz#A(|XEiXMS2GsC#-SU>qf38A zqhsAu01N~`6AmEQwXnj33(=#@+|C$0AUi0ciS3NZh_Sr7nR#x~6g2TvaFeE?iD!VV zJQG~4*#OBim$jSISX~~zIhP>oPheH@&FPN@BF2+Fif$Z4>}F#}GDwHss%!&2uJYJL!0$ZAY1PIp(ysk+#Rn3DSFn1cKj@WLD1!*+ z1*%bAl^f+?3J0N^9lF4dwE7NKgT4qj4h}ZpJVAlk2ga@OVrEkZ#=Er)Sstt14BS~? z$~R!A$@^=C;pH4wo_w>yq%x1Wz%!IFxrM6NY326^jtKH$Q=jVr zSLy+9PBmJAw^lucLZ8N`b{6B{9GDa5!NI%$!uujVu}k>ouiyi_iotdbe5UJQ?%o0` z^)_hoJBlVC@4?v0pdqLUnxZLaMyQ0wuhtg!vZ&fz-UY0xb}s7HQbI1`vk-3DF*g44D9FV)|VLAzhUlp z1-MC^)I_^$6&BX&Vf8z*ChHv7=G?G?4LMrU+@>+ttWCHJL9_A$`#nnX0R#I_4D62> z*q^|>g+nPt{i@?kKTB=`X}W_o+sc}6#tj2bIK+Qq30&jWsquQld4{p;)RU3D7iF- zOt7Go)lZt9DvwT&$Ln#4WY)=XOQdPBvNUO=EKwRErwmbeD5VG<^f;1xjRydaLDHx= z85XV7y3(|yWPpi>{5@WZBEVmfG%YSIUXDldv`A+vU#eWAEC3c63siDxKycd-P5b}@ zZKVXz1$Ctfhzuf?MPMJzmnv4CB5#Hav1w_^&0Jh))pbryPjrqw^GSwv}9d&i-otf$qMWv$1#i{(g)PC3yW;w^ECB#?LsJK|H(QM9K>05TocS{Pr zFllPCJd#w_qIwA4f$~9AQi|{;{$62k#U>@llVve-;d{!vg}oUiPmN58OC~Kf=hRq` zBSH}Q9_tCKKq%VpqbGdi(X#aTG>x9cK$2>@RjHYZZj6$}E5?enwpgrmN8-bc9=@ri zEu~)Rsr|H{VH`%vMSO`R%iZgWmUb?Youx3j*Co61S&eEx={9H6iVYY3OpRo zhebd(ZcN~*w6kU&?yiq|=pbWIQih!S7!?lXq^W?yiS6-W^AD6Ns3;|>p;gQpq@*Z# zAS@`o!If0w)dGs@EdZmVtb|JMvU07hU06;}QY?5RC@%km$}7lIK!H07bLl8ChmS8Z zDGev4rAm{cg{>;ZluMJLvi%W+0fE->T}zXbFowlqe!ytg#D^P3S`P(vQM^Z$5rPob zMkgR0RpIlgim4hVVlfS6t>XFC$cjr7JP;z~;M4GIFUX_=tuFv^?ITlj>&0T>YP#B~ z!c8#E7^V99t)`04NHIFch6J~^6qgs{T}%HqKD3pvreucYnN@lBU51l+a+@w#%PkNo z&7u^xMDr02Tih2{ap8ntAk}=(U1*_!$gcFuO(=TWrjFos5il$U>Z0ubM-iTx3HRe3~=BfOVU0 zU&dhZ6ecYtALGMfo3nLiJC5^Zo$ImE(Se7XvTIJPo`0w4Hf+;&mUqV5Vhb}v@Wl3G zM%HG=16Z%))v9H&5wNN8d)ukSA9XfZKXpV)&Dqjor$bZU>~3aGeMOx*IajWA#=l&h z(rz+C*<26jgZ<{%xn?)Cy7%rBV~eMhL2ciC%{o(J!G!G;5f8sT zKD##AgR%Q*gU^4f>$0`{(B`H?6TU62{@OUUr^Vv*kK?1aSb1z?i=W@<^e&m&#&^l& zo!75Fe)Q;Cz+ta$vJ;&KeTzBxQ(mX9@t+5``10|?y?qZHvR!**w>JKC@8GHTZI@R( zJJozd#H}ZjT7TJY@%>Asi&YQ%N9^qESo&dmo4yl$=1zF$y1eRGuc@_cQQ* ze;c*B+lX!I-O6CV6sJ`^B8~nGtZCgjE$_ic#@_BuI&i6$nbFYL;rGvGet+Zswe{la zY-qIq)V|Pe@o{htKe5#v1}V&Eakk?o&kFAk*w)Q6t$4uX)&Ywzz1&84IUGLdW$h)7 z8d@=>SM7DfylRa)y{VF8FPBRml^W)?j~z8FxrFn<4m(P;Ot$(d( z=hntr-I&zM`&hlaXSSjShvVxxA3fx?xk+8~X^|&4S6wGrd$jzfj_ZtekNpsNc5|k& zO}1~IxtnpOmsD2SvrXr=orABnStYIL(6HnU^Ty^zBibLVS#{c~z+Yz6 z(Vg4Wt#-v`QYjbf0b{%mH+HZY>+M=|=VU|w%{k#0Y%cg+NDQs^yzc6G4cnBjHtp9N zQGK?Enoh4Pt#f?jqXmx+J!mRch4EzDi4A*Vuge`oIjw-Lt<|+C9GIn$=61dN&AY&}nPvyXn#HqKC8lewv>a zZ2q*?%31FQkGoU)P3;G@E39bPc4vKg_daW8&YIbIZJV|2*S3DS*KNbAl?ztv_1)d& z@9Ywu#{z10uh}|g(ixKlPs6T<-CikPSvII;vkx1b*YsIlVaaZv%>9WgU(a5+ujkN( zZIf2s>9_akUlj+rd=3Aa@Yl)@ttFjJw@MsLZ%WSCwm8zLZ&Hg&F2`>E(cZhqArJ56 zvKg{j_eV_GdA`N6%YHxmSxtyL+Wn!!!-S&=N0-$wtzlmyFz7|l>$#ypJ6F_R(Y$T* zw#QDNI=%Gtl{t;(wXA>D_36B)^RCXjyS-P}tgdx)SLN=I`)_&Fbw!ugxqA{vc5T{a zdFKvYdu;VvzhHg!E4$b4URiSG&fsamYZqRLIkf4I++TB7Z@1g@_?qd>4Y>)qgJdxg zLFfw7t^??Hcs+*Ons7(br3?t<<&B;M8AIN;m7+?9SXX^Iy9UI`V$zbC;OT zXRXg(Yw$3))XCb1;}(R?yVyFWT+GhY;qS(b9~(IAr1Pv+&hsi9y?uP|qkyyzeS6&R zf8oP|d0Xcle6{N4#@BAI<6e)xwdhpCMYR`pX^?vE&~fj3?H`|aw^%fzUZ1<`2DkHW zmmRb1X*HKfmnogrU){8QdFGmrvNz>g{n;vI(Zf~`TQt7odZ)`zryA#NKfe8ByF*C} ze;Rq$dq}nKQQ!N2KhIVUt!@xwG&b8ayPM(PpZX89-#^j4*-iPMTdu$IoUnMOXBp2) z!`2Ubd|p0F$~lqjiER9#!IGLIN000@%6($;n9k+T z*PqoW^ZYIE)Vn@^`ONgWGcTroyQUkR0vb8CTI1Zi#f`?h8t!sG>KxtW)aA|#I{U{5 zR&46*xWO$ke&5|MuilM*u;78k!vW=HyBzP4+;7R5gYQSKEW6s-rf##D9&N`2H<|vs zr?um%vJY+uQS+Ei5}XLz>{)vf1AH!>3M}+}CT3ebBVrm=`fmqBBmelaJi;?MUMI z?JkvPh<@#}b@DosE>7ZHuNBY2yVknuDB2Pu?-afN^w<+kXRcjX=k%*nTTk4a+<5+* zMc-f99JIMVHFx=_z)nt;IzQRFu6NTdE&BGmDSlYSq|T_0rt4bl7?6_(d zl^Jra)~&3^6-FnW*gxmi^j}u5J-jpOZ1jcb=TGi9E$mie=9*bgW|c^^ynf>s%ex(a zdG+B>-s?ARk;`Rkd}&E3Ou!rzDg74G)>!tHIctDh3jzpgM~ z&z}u{yL_|To{wK!{krdF2ZIhlm!mJs|MvfT>qqwuW7j`o215biwGW zs9K5Kt2-}Uu5_Vgc<7`nbzZhEajeR|ZwuaJI$63|cKCVz(FfI%efm9m{?YvI$I6z? zZe&atuUfk*brS;7!R`VOS=+ZCX zUHGqYH=Yl>I4$Pw*$E3~EEqJ{`SYz_H<#uPcp7=;Zf~okH@mFU?qBfzKK^O*J!>AlA*&+Fc| zM_+dD_U+cSH@AlL&dXfCLjcR_z`Qln$x&J@)hF4`Jt4{yoiZ+O+S+Mzi02iEQ9> z6Z=i$7|$S2L^_)|@bC81a4n5&6AC@p{?!>9I-If3-*JATbUec$E^^;vgCV#Uj74Y`unVp$84wt!qr;q5)k@*eYrJPB%%^28sW3N&- zpPP*O?o(r4C-|cI)+ts6Yx=HpogMrgejAQhwFI zs|laYRtL6mtr#7Q@l$tW)o@7{qZmoum={0x7cmq!}tA_VI z8&)(v=()CIRvj-})B7*p-b%kZceH1zic{L;)OqtxBuz}MS%2l|v5(W@`Z{N~3d&n+ z-mR5a?G72PZ4xT=s#-Sn>BB3{N*-9-ulvikwVzr#G|uS%&7_mj(Q8>Q!|%dQp!^?p ziP1Ie&}YowH`r%`SFd40dL`S$Z>BId~&?`LI|ASolL9YBDSALKyKgg9I zcGU%A3lbjVx!M-FQh@ zjXSf;-EK4Od0KsI$DBW- zWh^;U#W%`j{`fQRlWgYQwx1T(Ai}j<ppXc;S z_U6mM>SIs8wtn+v*lb)b{$i85tyG9?bH?!p7q;x_-?dKgaKEy%W*&)nWxe=8@19LZ z+O7&}WEeEm;9g)x^|P-xF4;Zr*PHF#2MlN(S{T5 z@7|1R5=tH3@zEXZ+HuTrLMaw80E#CJ9YVT?C0O>PdHoU&GYXS-?;D3 z+Oc(m-}q_gDte?f_8T`YAl<=vZFaA9d;V(g#xC9ou_-&niTSqw>#Re&7wx{)ww_RO z?8!BMef}bQ`SRhb#@ib;zijvX#ob=47ZH|bid$sb(?jdm$r(zE^aQ0d;E$S^*c|0g->f2J1?D$TJ%#_i|31PcAvN9 z)S7p9lP4d!-sDOAwg=-P>=&Hy_~V}E3 z!KvE*H`6L_ZZvhWPx<~a_qsaUobTFV!`y_xiOc3D^l_@(2{$C&1HIqWsJ7a5!as2bddb9l#r_?jq*DZN(>7N5W&;R9pNZ^#M zb1l+)?^^Ajx+K`i_sof)Thre*ow2n2?O`v&?oBg(n0&C=pnxr9myaCq;YOG09v(5} z_GFr$x%=kLtOjLgt@G`&vRz!o;5Nf#V`glWw3(B$E@|IP*I)aOH~HFSRzt(}dfywje%tiHCi}R4(fC*1VTgZ|K92wlUAL&xJgdC0G2^XJ+Pab-j;1t2x_cMT2`y8ro(V4rc94 ze!G!Ud5!ZOkD;Df=PZ`E-+7U}#$s#jiLHLI9buUyWgTYSNLeVp#umEfR5|N1^yQYd zyDdbmrZ&7TT5@goo8d3Lde605@0*dHnf>x@*5GW{9D^1nw!hY>(f+c$UAuB;-;Z{& zw0-wjo|P=|GO98{woJUeX4d1a4_CdI8q_v)Xvv|YIvyVo+TcJ-m){1xejd~O+8_OE zw)>npx^ef9ZGVzIUc2CcQIp)ClkT1jahA1fTYumm$2M0;zW*@w=}gOWS*ESrfBE?N zO^G3Y{W|_zW3Sm)+^+2_HFvwG+%mv-`q$CFEhyc1$oM<$>ed}R#9;7@@CH-s9sOng zh=(o*%C$Z?=ys)zCf8oy4Z4^cy{1o@fa}%Q-K>3dV~L5khc6m3^yJhNEc)HgM`zF6 ze*bb~``?Cp_k>vRRioqUy`PkHNX)z3e)jDV-)nwqcfss8)^%v~V)IpoWfn`;1*}OJ z^lrZq^E;R)slGh-aKTh!oemQNuR@)Wl526OM3n~V4@x>Hh6d51P;Rlk|cSdr_v{C$qkA6KKNojNtRWqgpE z{gVTEl6Q9=?)|*PXGG5(&1Up8PJH7uYurY0>fWpXpV_QZ2Z|zN;c53;X`4yXw zusrypgom}+t}ch>HoF@AIyLf1*_(^DH+x&TsoT-LqIUzk4VajC#cZjmn|0cuLk$Oo zwhn92f6L+Tzb}8jtU}Ki!#+l77Z1f&Szh{UuU{r5_U~MQ)muD0^on@Jec852rNRb{ zzC8H&wkgI(#a*w~TQRXzg=dTME_rWw)x>DiuIxs0v+7>CnpeJ3$|2JM+j|)pTR#Yn z8FqDf$}HdG7LC@6U%m3#)4+3E*T*L=HQeyh)wPAs>*o!Y&h0pIh;2ZVN~WIHliSv? ztCZgF^QV+g-^%nTQ`OYG<`sFbCRurpj~oga_$7JjDnEl44{F~zTgZ#81DBqy75;c$f819f!bzyHn0I2v;aiL2qRK(PLRlBB~awqpPy;>Tk z;00Q67Y3%9vH)Cz1r`QIWy>w7l6JXj3jj{g4y4w1jYJz!fH}=MW0ax)zfbCm*cC}55fmesjdK3R0Vxe>8`}2s*e-j zq5_nJkpD&>6kG|lX=yg(Nsh(?>!i zc@5L<>z0k2vdu6xQ$pEQzntvjXhuD;q{T(|g9$#@WV$Oe;e*xphE*7&(7O5Xx-l)HzVYRGnN=A1y4JBdoyoo9nd`z=h{R;fz ze%`;k_Z(-&C~>MWihAT^FO4e>8r_X_#9unW1VfclBV`NWoRd8kMk)F{Y+$hk)~Vmz9u?ItDG4#mxL8aKt_9Q~S`zgbeN9P-Ve8dmVrZZnL$fQryDh@nOi760#qDBZ zXs8>5e;cO__0d9P%ATy011pN`s4@(|!Nc=?~;(_Opnu8A9mIWR^^IJ>`lT|8{M z;Y%hw?lTuIBq-Gq)aD30NEA3R`qAD`?Fn3aNsasDb!HFRhmw;3>hki1Ky8}@4^Cle zNh#Q=>1ux31kMPs_TG-JrX=j{b(Qr6?HPd$zHe5foO4c8_#Wi11Jf3o0~aOXM(2>d zzWCaPsgULG1*+7N`V}q?gA)ZxLL~MMdLyC6^MhLt|8}~OEOyJj-WMH0Nw{cyf}^(r zyIRmG+(khMlKYOG8fwfKCE*gjtDe3H+J*T?=`Q}E;qHMqK$lPwes^6P=?gssUbf(I zR1U9AVe*uWxX1z<>sidqTIbMEl!QGp)kj}!2wH@AqVVw<;-kAt2jtq)Tr%zHk{POCF_1S^pjj917rPczhJ4X}?t6zMQOy&B%h~^#gW&IN= zmMcoaUiBPPETsOy=?M|?6g}&DH}dnb8K@^EA=={;i-#6ow!n$d6Rqvtc`I}ero&T< zg|=-{VvL?RXZP5-CJ^f#rFMdD-88dUID_Eu4r@6W3h=0?C-RP6{$AG&dumF;ksLdx zSjdajyJauTVR67nDNs=5+KY;XHD4`@So&{0Z8T*Ph7={?C-iqtu@Hxmi^Y7{bE zOWU0b1VT!_g4(`WRV<_(LjCnb8CuTOBpB!@2~jRsS4@;`^+ajs_4VihaL_3U_aniZ zii1*j8}85P`PjNVSouicS*U(F***5@0bRk5)D64hpcl^&VTPj2XtDBkUegP9vID%X zC#CRSyo;DmcMRNA=L>p;lDDcFM2sK2uO~7exSC8%g+oBS5dL=6ePn4Y$&`dcp~@3I z5pw)VucVYHxqfw=cge-#A!aj5LgviO7kZ-fgLg!|syA6a=G0BJB_$!Q%dhmrlA&cV&3TBid>NlkHjQur$j*_>a9eu6zM5!Xv^s1w%Td$s{fr*lk zjen|?eh76>`QHtFruGH#LkZ#<)er3BPKCe?!w!p^cX_6XQk1D1N0Zw&m!eDN^mRk8P->y74lOG&Km7w_6r)J4yIQ|SM0h;{7D^rhOdlil z#}qD$h>%B7u${u|RWBj@?ge~Jl!Q{qrg8rSTOk#jHO+HHDySSv5O}G6IoZ((#X-GX0ze$JeX`cM)I+Fa9$iKvk3ylIywTW5}44U%v~z>uKeIZtbdcjElF7ha!f zC9Ze9i9t?DD6^_Othks8sZgN!-EEOAW0Zt!&X6&BqsiYR#K9RNUo_tWQvoF*|7q#s z!a#E|WjOAvhTKyZFO`s3EXs@xKoe0ClC*|Pi;slE>Xnq97?oNuOO?=iM)eHohml4} zNb+o5S$woXaDL9AE*xp2FY63Tu1h(M;5gQguP?rk*MUMYt@rkFLQ%AcxAO^UnhuC7}qg z&-tQ2DqZM@!5e!4llINhZ|YYAc|%D^oQ=Oy6nISmpziiqbN|V~Yk*8isAcixPEpXg z557Rr>(emHRRQ?eDOD4+bmeD7K+a!O`Wj{r@M`W2vV>AC0l?K|j0&C=sp|^RHXx`C zd$0K^4?Rao$n1BuEdr`?Fgtig$&w4y?`hHOajP!)fGG)C%-)ssM3BEdIHv8@5;N=djX^mWh^vc{xZ;0P(PAS7bEh%rh+k=+AF zJ<(9-76`v%oyKuX(5;k&)!6G=4EPEGNP%dcjq7H&4YC$W!j0~>#`>Yr-M_CoT6POS z-%=9x?-suz;3|q-Q2+M4-Zi2!8l94`;r)QDK>Cy#TM4<)hOfJI-lYT9;3pvoTjZDa z`oYiNBJ{1Uy+6SV`)@``*dlu)3WEzNjY6iPt3?QM6T7LAwiqUqggdTYay=n?D?@82 zrv=@0u=4pm+5lK6`3h#S3vqg4BB_%qZlSjW3(Qo6$_C9dcXQY&qH2#uWlrx7eKusvWXP4eHh68B~pyb2fWIYt7Pe+?c8*p+$G^J$m=n_Z;%iJW5Re9N5D?aX>LzS#>|*0$ZRl*_ z3RHHnwKX)d1~M318#+1JDBCDZDx&*-ohvO~+-wX(6&)M47~X@{el6=j?72wRr+V=u9Ekz)LOEraHzsGE@W4AR z!-k~ox8=M(O{t-s|?fZbUC8K@~=~b4SY}>Ve8ne*BgV`(J|iQ>KTL>&LiPhFIRU8 zZ@gsbMElB3>C03BUOjuz)A_lUM4+ntn>qU_P^oqIgSo^c>bcJhImrdTwoP=;oU?9l z>Fl@yb?x5P-8)0M%!~xEVQ>dRW#X6yIVr3L&J!rt!SH>d$mTQt4}^m|Xx&4X(VsvaC$G91)O-q`M~9-|18 zAkFCpA%U!ea5yGR)K@rFDU!1vtTV4|C}x|bkS-p;{}l2MnYkwty)ys>0m*ul8t*^J zOy#>$6&>yDfsW1|BIbs+WM=L=c|io>Rj3py z#z(CujRX*@B-T@Ds z&!Q>Q^}(Nx10KDi*jS}EI+`Sau*o;M7e%CK+{t*x!a*Z&Bt0HfV_c?mFC4MFS4R{J zeztHgXj3KlG+33OU}nouFy0>xk$)UV`qTD+1;x3urxWrsWjRfnyu3&;{#aqTebG|wc z{>CWX3ES^n<@lLmkXXkQwH@j2hMVo>`$qX_wD*tu1NZ;UaDSR@Q~^yG{XV$v{i1Eq ztFiD9^0FN>CJ}mb>jV|tt0$HrSlL4V3%#KEiD=%r%kkOZ5N&WGd~G2? z`M|^kq>>xvsxi%jK1ClR%!|5#8a(t<16Q6#SIGoWqTHi#tb`ELIBf9ojZOKOCQ1*r)!q1 z**YW@Mw$KEwUXi`y4y6uX`R|dLcBe@m|nuj1^2(KTjLDUlfT1e?aC>$)o@UW5HfGa zxFS_8aFPMAd+W~5e~xI-2fD{G>Sq@RJseY#o6z#D05fn$V)+F@M^lxrn?NZEYkRUl zA+BRP)!-Gup?I|*MS1|?>(1iQ6DfJt*vtUXLHe#agF^!Wk-jr%tyji11q%Q?pRbot zUA}RBbA-DvMYykLJ2-Pkke6*qkGtDtvH2(O5`*WXot!!gA$%O3t z(G_91W4d`v$$+VjDISq}537YqPG~noN<^{-7j^k#*T97MIXZ7-21F?5u|H8;3?S_V zO#bsMgWdhpb+4}@ZWrs@^@kvy?qj0hGrC-wcG&@V408<@^)C=P@U3|o2FI8RlFu)& zU87aw#Mec4!j#P71V&9f%fiE`va>$v%biJdeI7M2Ma1sN#3ko5Exb@8!Cq*e*Gx;6 zl{zqpSb4k6-KmXC7Z2%v{ufcO z{MdzFD=R7B3M1dEQ)!H#AzKwiVRWnbb8xR<7^P%J$<_Gyjpf?yRkkM#>NzzPw3f(4 zb%or6yi9b!l=~u*@GjMQD|Ip1&UAZzzA04@WUg>Ku9FrIQ{5)Ej{twq(W^VK1Hm$O121vrOx?SE`^qZ} z5ilB)o6%JnJGXbrv{8_?!yyho>&u2|(i2CPP?TDIOZ--{EJ5?_)_wmmHS>%}S$^|s zMU%oX22vi8czAdQv=3$!y){>blKl~?M?f(mM3SlB64#dfN`QS(OprysSGk1SV?1_lpTqi!Zl{ zc>&F3VE#@0W}U^A*Bs;N0#6i5e@f0vdDPd^;?@AT8XG#?acH%^(O6yUmR2w;--ezJ zg}1M}QDI|*@z6zCau#k8I7E8h>i3Nttv%-mSW9aN-1IQTq1EI(c24T-ERLE1cquJjr{z7F*56)Mz*nI|@)lc`w_g zP~V@|tm~@cXjivPzJo6O(oay@J6`>n@D8diS3m#uLPF>1=}bKli0imjM|W_D+QP|L z_rXc!?wb77W?6(610gc5Yqo5K$Y)%WE3OoZiL}qwn8{9gz1v#8B&X(@Yi<}zX5Ic1 z1B9ratB&zY%vrvPEg%vbWkOQB?C-93aG3)x0>yH`NR}PNv4aKb9 z8aWr!c?E6mRX>=y6ZUpn#KD$~Y@`CLn58!tLdHC^*4bEPeaR2VWOVqx9Yg!aQ1B61 z?OeJ?CdJV6J%6uZsGQY1PDD!d9L&}x8S)iB81%hNDbP0&);RY$U$d$`pkhernVWi~ zGLRzB_R{c(^Kx%owdXW{QR`jzggvA}7diB~t2>x$=hEiD1|dNlS;MGP-2kb?fvXk~ zX~+z&_x&zyG`w>G4j+?H1>P{&kEiL)12U>^dR@dpmUd z9F-l==00N0_XMGwxqQH^a{l4aGTn$z8KBfVzTU8UHU2!FT!wB#-g-lNw6`*p9nNj!*a8KuX*d1Ea+V$aUye9?uMwiHL(d zBkR&UI-l(6F8vACs)n1QK8 z3G`nuF;A%$PGQ8ElHYKEB$o#SdsK5ey5jT3wSWE!0Wc-+_FV~ymNhD7eJK6>cBEH+ zXMR!9F~gmcRa1{F;6MnqK3SgVp|wDwEY+IQSOSvwRtz%A-!0m82|N{af?slO#EJ%=ks{}h*lH}+zhvSQk7Lx8v$TRulT zxuypoP@-k&^Q?qAsS1f5YH+R>zi1s;&A(C5=Hcnc1&#qHDxhnE;)D+RnsIE(j_RYj z_8mWZ0mvqZBj)x_>4ZazU#f+~v+-)!Td=>2>=^nGiR^KU`TThQw#e9jh%8EJP`*P5 zogWJ}Xii#e&1*HASrO|~5N#BmiD5RgQqCTLQqkO0vY^i``BGBn64W>G7%`;*7~kl$ zwJ?*+tG`T?0TNxQ5fa1>g~3L^R$-e1G@k6i%+iijCcsBQEK?PT>D49=MXNvcW4JPY95;h;G8{mL%t>)7@`# zGR)b^wjE>OQ?HYKvl0mM%8NR$uSx5D?bx%I_=4R^n1hDSlSC21!shoX^{mwEzC9ma z0kx6$MWx1@8n!R61APdsf|}HYTkK3uYh*s|*}v5_tZ8@ae#P0wg;xdW*PhoYcRVsU_Z&1I>}g|2Hl z>N5>qgGHuM=0L1lHy-cFS<=CdP_r48pv^o~9frkdeVPzYoHD?2Z}S~$9j_2Ob`O!~ z#AFQi#Op7JBBrGF5F+y7wy#VIRZ$gM=wD1nNJ@LnU`2fyGkis6rjj~w959wpYLj&V zHX{^ejN=mR^p>Nd!vPMn7abN~Y=~)ykk`Ue>|<}-b`ya4$n;HJIan(F*Lsq))D54dnsR7(I!$8Ab@>gM0AAF$%fpmYwyTih=M!>tmyC^Bv>x za1UkJpUz$aSQ51Xl&%d|@?0o!sQ?1^=uld1{ugOzlEpg<`VNWW4w*M$tva_Vp(@w& z>Cv%drvnv3Vc4W3F*}pkMaxPB*omjCH}pWw_i6d4lqCUO6xQy6>D;tOh+Dg;vhmg) zxIqoToupiIMauDhk1B?VcZm??1g}1|5TD`f#)J}Ky3K&p2D3G{DIcIs4C=>888+P$ z7?&e43)p;%Exm}%$%Cp63A+wy2|#BUOKYct&>|U(yeW7fzrp*vl*pY6EFK=E)DH1K zEL_f?Qc^uoo_t)mG9oI{6@EmTVi02VppjIOu%UpUWS+J{s>pkmd@Cs!JPIOoAG8d= z9su<<9s?KmW%#8M!{~js^ z*_Sr~3?ZPqc!`~gN%@>){pQ_r@G{Oa+4D5ws*%NyCx$B9V1Y+pTn*Bv%+mI)IQHjg z!^RF+{+Nh1d-HM5cMcuzsKIb}M`B^bMCn;|c*8ZE#byz$H6)`C1f{t4F+^yWH`KAo z3JNH#4WSa?t%8(yk5^v^)X!YPh2si(duS6{L<~&EhTIA=k81GQCYj06+Bdtc-^svB zkJCFrvMY6tsf89rQ`7WRD?*<}b>=7zd*j*Lwfi9s<%y(d#AmBf{@P5Lt2cZw_V&K7_am&M4GopJbem~(s5 z!v#t9Aw2($m*j!a_?Zn=llhXcc0r?OMdB6om0^`i{G!0P#juH$=)it9`BvRdIGH$C zQ&8R2GsLfZ2eZ00U%5fr0BT;k$>jj&YxDeHyeBn+Nh}%ZMZiR z6I|#7hk$LR^hF!JX1s-Gj+9rr=?iw~KGj!Ib<6@zWqTZrJ}46@35lBct0!)jg{9~> zm&b*)_2_0v{O<8u)K104$i~9?KL>q7tkL#3KUXV=B*{Yf_qmRs=ksf zB?b-&?k2)J9psv{KZ2>(O?fnj2c3dl?t`)nxHE%;+%QNtF}pvtXqi0}6nqK66op0Y zY{VAC5Sbs>JDb{_UjR4#YXYCYjbBzX^mf3lv zaY93<7()+9G||xn8FYrxZ=A4|kZp$1eJMbxs(&rPO*H zU3s>9%tUx{!+M1f9D67}u_Lv_X&FmEWQnFpZ+NK?_N7YWi@Mf>zPY+R8`x33>2LF~ zBl00vC1!Y7w68N!rWzen)+)ZR@^CjRE`G?IfTe(BGGA#4gL+(vabdmgM%jKi!7T}( zayo6qBIVQjHPThfTnMYXTnchSYsq2TJVtR@pVg~RzdCYlPkI_PIJS76w2F5zIeuls z?wM;M3NBk`GLqzxcGqb{H|ZN75Z2li(&NJ4VWti^es>dLBoP#|-@OQ76C-IU$m{~|ioTm!Ej{W`=)Wq(f*eNV0Y zl{op=Y{(z+=AVHh6=y?dpt_;8%g@9{`eOuX;xVzofUYgwOF^8#t zIXn!wF=g;&QI)iTrY!aRvLl^?c+RZZ%f#aa88odbr0DsLj|&3Z+>95u_osJQeyFa- zu@$9jppGmoEQCdpE6i9P5G6EsboQc3!r^-a5Ef+_eeMIqtsF1T#P=`)EU)Sgo%hG5 z499TG_S|dZ_>;NNf|Z^2&(2n}Sh-JnGtT<^+YT}!n@M{o>1|ulT)z?p2oFsQpAGwK znZ*d$lztP9)f51Yz{I5T7o#jWdV#7;e+9dM7qC^ z*bdk#V@HhAMLqZs4*(29L72u@WCLshAAK$0dA~4q;4b*a+Q=4nb^(~ZhTm7=cAt2s zI-g>{j4w!%i@Ep$A&$Xs?Nz0?99GRyR^N-hjK_L6KH!{(v!>3;(i!do>54nWq)-)v z_YpywWcB$p&1~!6wbW`|yZr_l1cVX;1cc@{we-W;NY@P0N58;$K%^UDiG+10p?Ygs zMMskDE*gr^fvSNL8j;dn)E!hswyNQT38BRA+9xC$9lh-r(q&CLh8w zj-JMwMq6;~S^46xy{QSsBC|F=U|h>{T!<*R+TDA9i$|iDG&V4zOW(HO7)^#E3t>1<8Y|0e}#mro`n%9WgY)Fcxj-!mw#aS0ex)^MuqO8Q! zOu3To6Y8-DsB)LO^;tu_bEu9>?z+DU<#jY6A^|_U{gHWL4lRigG@RzN#ZFS`P8}Rw zzEkY8OO^aMCbdCSMWae|PBzJJ2$tX2j#mz1GfhVpEJ9!?)~i$pa` zp9|;9;j}ka&I@DxJB~87H`})1jL4?i9wZl}+){63hXNxsrH#W0RT3fzt+B~!OkFlgyozzsrI@?2 zY}>Mi++@AJetnS;D4E}LKo*^+!QIMEnaN&Z;>(G~oet0icZM{Pg)|PbZ87wJFBq}R zEEVQpYG*s!qT6O*C|24|P7Q5VP*l za-dIDeo~(zo7%7O445V)+C%ED*u#4yk3)G(tefx(@R<-?MAjImT}?0C!#5FdyX{hVa&4U19Ge5*=VxydbVVuIU-Gd_(W4Ed}fMH+MX zicWg|SG$b^@?6J^$<+WQLd_Y%Do=@C3;qyh7&u9cXS_;l%?i-Jd9c0=$^2{wvtVo`Y+d;9M8B`gR+M_)qM;5#FC@sSj`2qEH zZoej5B}JInfQ2dpIQFy!6G2V$+F{yq6)B3v>$SR|dqq2qh6{mrrduhCes?Nnj(2bB&6Z5Z7!$5;&;MBZH0I6Ot(7_JSa4z8v!5E{c8Qa zA?64sKU{l?fK9VrS@&EF;oy@dr<4|1XP5@{NZGzI%|qwqU|}*UmQI6W^;y7H^P=je zD&<^o^dqq&$r@?VyO&d|?|V;fK!=RT$Pu?mxoqE#)n>APsBVOw?PQCN3x_YQ4Sg^# z+A4pM16`iKTmsaLLH_W3Mc`UXGcbUwjXdQYl0A5KbNCC?aW7p@qGlCihm4~7As?d5 zhhE2}n1ls1?n5M@wJNF6y;CyXV)l`l2_B?KqbcRACc(b7-<6k&t+1D;WSS zd(;W^f!$Pmglp3ZYuLen4s`V+WoF@_qpKtBG!o(Rbu83UcgqmPe%(=(x^UMogGW#q zBC~8Xx_W66k7B|Zms=2+3uxbodjnIuhFc_+6F#ghMN_ZW4E^$Ox`e!VSU@W3$qQ1RGFCrB3221KnOB{KdAWAsAQIig|@R3JOX zu~`LO)Ti-cc2>>7p7$jluq`mY4V0R!K`!kUsv4yk;=^dS+;f9VswN+cfOZ^;`zd{R z78&KcmbEcrs)--{qQ=+ew5G(eTX2{7pe~dlza^ab@@)Ig)JR7PU&xkx>(K6)%Q)-n zNmH?s1WCx#nu01fP(h*KgRiwjs!n0TPh}BEPO(^s!w`w$dsuCziKxH=SdWmHKNE5g z=t`1dc$JW!I2E%lD?c|jwBk^D91k!m`;c$3;E_J6fW5`L?a=a$%VO;9HfA~`a8Ork zlt>R5wGUt}c}#4@f49kCLW?sN3o%aodK)=O|D?~ZbDrO^RHBR+ryUNNa@;*Eb`!@0 zWwf5KD%$xtBUGK`O0puZT<_C#sfqSvAe67kS8F`zfoVHQV>~l8r%}2(eZK{F2sTjt zz|UL}1UPlXF&2dLgvGqpa_eNSdYxNr+BvMBUAUqa$Jbz`Qo7!;olj7f!QLlByqFK|pf-Y5%qBzZ6*g zEBW`LtUr5v|NLGA_2c?Z-t_qRqa^B2&VOGN^{dC?&mMny`u{}sy-4cE^_>j*@v!h8 z4++0u{(aHZpFp|(g8u~bpHIwB37(eA{36JI44?kTasKz>nWscg%R7D%=|2Y7{trYy z9N(ujPxJV{XcnRV8_nOd`cILcCWC(=pTPVk^3T-pQ{<-^$Y01aaKDNCGavaB`Du3R z7xJgaBENs0^q;fwGuQPL`Dsey7cvj>ZzBIpa6CnR8ovL9T!`|U$UlSmPm!NSbAKV9 zqWvcF&)Du$jkz(ThCh5~S#8Zl=2hv{@M) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + + + + + + java -cp "${run.classpath.with.dist.jar}" ${main.class} + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + diff --git a/apps/desktopgui/nbproject/genfiles.properties b/apps/desktopgui/nbproject/genfiles.properties new file mode 100644 index 000000000..1b326007c --- /dev/null +++ b/apps/desktopgui/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=c4b345cd +build.xml.script.CRC32=9785bb9a +build.xml.stylesheet.CRC32=be360661 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=c4b345cd +nbproject/build-impl.xml.script.CRC32=74d3fda2 +nbproject/build-impl.xml.stylesheet.CRC32=487672f9 diff --git a/apps/desktopgui/nbproject/project.properties b/apps/desktopgui/nbproject/project.properties new file mode 100644 index 000000000..5e888e698 --- /dev/null +++ b/apps/desktopgui/nbproject/project.properties @@ -0,0 +1,68 @@ +application.desc=An anonymous communication network. +application.homepage=http://www.i2p2.de +application.title=I2P Desktop GUI +application.vendor=I2P Developers +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/desktopgui.jar +dist.javadoc.dir=${dist.dir}/javadoc +excludes= +file.reference.i2p.jar=../../core/java/build/i2p.jar +file.reference.router.jar=../../router/java/build/router.jar +includes=** +jar.compress=false +javac.classpath=\ + ${libs.swing-app-framework.classpath}:\ + ${file.reference.router.jar}:\ + ${file.reference.i2p.jar} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.source=1.5 +javac.target=1.5 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir}:\ + ${libs.junit.classpath}:\ + ${libs.junit_4.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +main.class=desktopgui.Main +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value +# or test-sys-prop.name=value to set system properties for unit tests): +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/apps/desktopgui/nbproject/project.xml b/apps/desktopgui/nbproject/project.xml new file mode 100644 index 000000000..09409a64c --- /dev/null +++ b/apps/desktopgui/nbproject/project.xml @@ -0,0 +1,19 @@ + + + org.netbeans.modules.java.j2seproject + + + desktopgui + 1.6.5 + + + + + + + + + + + + diff --git a/apps/desktopgui/src/META-INF/services/org.jdesktop.application.Application b/apps/desktopgui/src/META-INF/services/org.jdesktop.application.Application new file mode 100644 index 000000000..6cd2ac1fb --- /dev/null +++ b/apps/desktopgui/src/META-INF/services/org.jdesktop.application.Application @@ -0,0 +1 @@ +desktopgui.Main \ No newline at end of file diff --git a/apps/desktopgui/src/desktopgui/Main.java b/apps/desktopgui/src/desktopgui/Main.java new file mode 100644 index 000000000..9d9708c8d --- /dev/null +++ b/apps/desktopgui/src/desktopgui/Main.java @@ -0,0 +1,109 @@ +package desktopgui; + +/* + * Main.java + */ + + + +import gui.Tray; +import gui.SpeedSelector; +import java.awt.SystemTray; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; +import org.jdesktop.application.Application; +import org.jdesktop.application.SingleFrameApplication; +import persistence.PropertyManager; + +/** + * The main class of the application. + */ +public class Main extends SingleFrameApplication { + + /** + * At startup create and show the main frame of the application. + */ + @Override protected void startup() { + Properties props = PropertyManager.loadProps(); + + //First load: present screen with information (to help choose I2P settings) + if(props.getProperty(FIRSTLOAD).equals("true")) { + props.setProperty(FIRSTLOAD, "false"); + PropertyManager.saveProps(props); + new SpeedSelector(); //Start speed selector GUI + } + + if(SystemTray.isSupported()) { + tray = new Tray(); + } + else { //Alternative if SystemTray is not supported on the platform + } + } + + /** + * This method is to initialize the specified window by injecting resources. + * Windows shown in our application come fully initialized from the GUI + * builder, so this additional configuration is not needed. + */ + @Override protected void configureWindow(java.awt.Window root) { + } + + /** + * A convenient static getter for the application instance. + * @return the instance of Main + */ + public static Main getApplication() { + return Application.getInstance(Main.class); + } + + /** + * Main method launching the application. + */ + public static void main(String[] args) { + System.setProperty("java.awt.headless", "false"); //Make sure I2P is running in GUI mode for our application + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (ClassNotFoundException ex) { + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); + } catch (InstantiationException ex) { + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); + } catch (IllegalAccessException ex) { + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); + } catch (UnsupportedLookAndFeelException ex) { + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); + } + + Main main = getApplication(); + main.launchForeverLoop(); + main.startup(); + } + + /** + * Avoids the app terminating because no Window is opened anymore. + * More info: http://java.sun.com/javase/6/docs/api/java/awt/doc-files/AWTThreadIssues.html#Autoshutdown + */ + public void launchForeverLoop() { + Runnable r = new Runnable() { + public void run() { + try { + Object o = new Object(); + synchronized (o) { + o.wait(); + } + } catch (InterruptedException ie) { + } + } + }; + Thread t = new Thread(r); + t.setDaemon(false); + t.start(); + } + + private Tray tray = null; + ///Indicates if this is the first time the application loads + ///(is only true at the very start of loading the first time!) + private static final String FIRSTLOAD = "firstLoad"; +} diff --git a/apps/desktopgui/src/desktopgui/resources/Main.properties b/apps/desktopgui/src/desktopgui/resources/Main.properties new file mode 100644 index 000000000..f79fe9a00 --- /dev/null +++ b/apps/desktopgui/src/desktopgui/resources/Main.properties @@ -0,0 +1,11 @@ +# Application global resources + +Application.name = desktopgui +Application.title = I2P Desktop GUI +Application.version = 0.7.1 +Application.vendor = I2P Developers +Application.homepage = http://www.i2p2.de +Application.description = An anonymous communication network. +Application.vendorId = I2P +Application.id = ${Application.name} +Application.lookAndFeel = system diff --git a/apps/desktopgui/src/gui/SpeedSelector.form b/apps/desktopgui/src/gui/SpeedSelector.form new file mode 100644 index 000000000..b256265de --- /dev/null +++ b/apps/desktopgui/src/gui/SpeedSelector.form @@ -0,0 +1,160 @@ + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/desktopgui/src/gui/SpeedSelector.java b/apps/desktopgui/src/gui/SpeedSelector.java new file mode 100644 index 000000000..19f487534 --- /dev/null +++ b/apps/desktopgui/src/gui/SpeedSelector.java @@ -0,0 +1,176 @@ +/* + * ProfileSelector.java + * + * Created on 3 april 2009, 13:57 + */ + +package gui; + +import java.awt.Dimension; +import java.awt.Point; +import java.util.Properties; +import javax.swing.JTextField; +import persistence.PropertyManager; +import util.IntegerVerifier; + +/** + * + * @author mathias + */ +public class SpeedSelector extends javax.swing.JFrame { + + /** Creates new form ProfileSelector */ + public SpeedSelector() { + this.props = PropertyManager.getProps(); + initComponents(); + initComponentsCustom(); + initSpeeds(props); + this.setVisible(true); + } + + public SpeedSelector(Point point, Dimension dimension) { + this(); + this.setLocation(point); + this.setSize(dimension); + } + + public void initComponentsCustom() { + ((JTextField)uploadChoice.getEditor().getEditorComponent()).setInputVerifier(new IntegerVerifier()); + ((JTextField)downloadChoice.getEditor().getEditorComponent()).setInputVerifier(new IntegerVerifier()); + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + nextButton = new javax.swing.JButton(); + uploadLabel = new javax.swing.JLabel(); + downloadLabel = new javax.swing.JLabel(); + uploadChoice = new javax.swing.JComboBox(); + downloadChoice = new javax.swing.JComboBox(); + kbps1 = new javax.swing.JLabel(); + kbps2 = new javax.swing.JLabel(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(desktopgui.Main.class).getContext().getResourceMap(SpeedSelector.class); + setTitle(resourceMap.getString("Form.title")); // NOI18N + setName("Form"); // NOI18N + + nextButton.setText(resourceMap.getString("nextButton.text")); // NOI18N + nextButton.setName("nextButton"); // NOI18N + nextButton.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + nextButtonMouseClicked(evt); + } + }); + + uploadLabel.setText(resourceMap.getString("uploadLabel.text")); // NOI18N + uploadLabel.setName("uploadLabel"); // NOI18N + + downloadLabel.setText(resourceMap.getString("downloadLabel.text")); // NOI18N + downloadLabel.setName("downloadLabel"); // NOI18N + + uploadChoice.setEditable(true); + uploadChoice.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "100", "200", "500", "1000", "2000", "4000", "8000", "10000", "20000", "50000", "100000" })); + uploadChoice.setSelectedIndex(3); + uploadChoice.setName("uploadChoice"); // NOI18N + + downloadChoice.setEditable(true); + downloadChoice.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "100", "200", "500", "1000", "2000", "4000", "8000", "10000", "20000", "50000", "100000" })); + downloadChoice.setSelectedIndex(3); + downloadChoice.setName("downloadChoice"); // NOI18N + + kbps1.setText(resourceMap.getString("kbps1.text")); // NOI18N + kbps1.setName("kbps1"); // NOI18N + + kbps2.setText(resourceMap.getString("kbps2.text")); // NOI18N + kbps2.setName("kbps2"); // NOI18N + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGap(49, 49, 49) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(downloadLabel) + .addComponent(uploadLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(downloadChoice, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(kbps2)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addGroup(layout.createSequentialGroup() + .addComponent(uploadChoice, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(kbps1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(nextButton) + .addGap(34, 34, 34)))) + .addGap(40, 40, 40)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGap(67, 67, 67) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(uploadLabel) + .addComponent(uploadChoice, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(kbps1)) + .addGap(18, 18, 18) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(downloadLabel) + .addComponent(downloadChoice, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(kbps2)) + .addContainerGap(173, Short.MAX_VALUE)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap(271, Short.MAX_VALUE) + .addComponent(nextButton) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + +private void nextButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_nextButtonMouseClicked + props.setProperty(SpeedSelectorConstants.MAXUPLOADCAPABLE, uploadChoice.getSelectedItem().toString()); + props.setProperty(SpeedSelectorConstants.MAXDOWNLOADCAPABLE, downloadChoice.getSelectedItem().toString()); + PropertyManager.saveProps(props); + new SpeedSelector2(this.getLocationOnScreen(), this.getSize()); + this.dispose(); +}//GEN-LAST:event_nextButtonMouseClicked + +private void initSpeeds(Properties props) { + String up = props.getProperty(SpeedSelectorConstants.MAXUPLOADCAPABLE); + String down = props.getProperty(SpeedSelectorConstants.MAXDOWNLOADCAPABLE); + + if(up == null) + props.setProperty(SpeedSelectorConstants.MAXUPLOADCAPABLE, "1000"); + if(down == null) + props.setProperty(SpeedSelectorConstants.MAXDOWNLOADCAPABLE, "1000"); + + uploadChoice.setSelectedItem(props.getProperty(SpeedSelectorConstants.MAXUPLOADCAPABLE)); + downloadChoice.setSelectedItem(props.getProperty(SpeedSelectorConstants.MAXDOWNLOADCAPABLE)); +} + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JComboBox downloadChoice; + private javax.swing.JLabel downloadLabel; + private javax.swing.JLabel kbps1; + private javax.swing.JLabel kbps2; + private javax.swing.JButton nextButton; + private javax.swing.JComboBox uploadChoice; + private javax.swing.JLabel uploadLabel; + // End of variables declaration//GEN-END:variables + + Properties props; +} diff --git a/apps/desktopgui/src/gui/SpeedSelector2.form b/apps/desktopgui/src/gui/SpeedSelector2.form new file mode 100644 index 000000000..2f0abc786 --- /dev/null +++ b/apps/desktopgui/src/gui/SpeedSelector2.form @@ -0,0 +1,116 @@ + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    diff --git a/apps/desktopgui/src/gui/SpeedSelector2.java b/apps/desktopgui/src/gui/SpeedSelector2.java new file mode 100644 index 000000000..a02ef9b53 --- /dev/null +++ b/apps/desktopgui/src/gui/SpeedSelector2.java @@ -0,0 +1,174 @@ +/* + * ProfileSelector2.java + * + * Created on 3 april 2009, 14:36 + */ + +package gui; + +import java.awt.Dimension; +import java.awt.Point; +import java.util.Enumeration; +import java.util.Properties; +import javax.swing.AbstractButton; +import persistence.PropertyManager; + +/** + * + * @author mathias + */ +public class SpeedSelector2 extends javax.swing.JFrame { + Properties props; + + /** Creates new form ProfileSelector2 */ + public SpeedSelector2(Point point, Dimension dimension) { + this.props = PropertyManager.getProps(); + initComponents(); + this.setLocation(point); + this.setSize(dimension); + loadButtonSelection(); + this.setVisible(true); + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + buttonGroup1 = new javax.swing.ButtonGroup(); + nextButton = new javax.swing.JButton(); + returnButton = new javax.swing.JButton(); + questionLabel = new javax.swing.JLabel(); + browseButton = new javax.swing.JRadioButton(); + downloadButton = new javax.swing.JRadioButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(desktopgui.Main.class).getContext().getResourceMap(SpeedSelector2.class); + setTitle(resourceMap.getString("Form.title")); // NOI18N + setName("Form"); // NOI18N + + nextButton.setText(resourceMap.getString("nextButton.text")); // NOI18N + nextButton.setName("nextButton"); // NOI18N + nextButton.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + nextButtonMouseClicked(evt); + } + }); + + returnButton.setText(resourceMap.getString("returnButton.text")); // NOI18N + returnButton.setName("returnButton"); // NOI18N + returnButton.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + returnButtonMouseClicked(evt); + } + }); + + questionLabel.setText(resourceMap.getString("questionLabel.text")); // NOI18N + questionLabel.setName("questionLabel"); // NOI18N + + buttonGroup1.add(browseButton); + browseButton.setText(resourceMap.getString("browseButton.text")); // NOI18N + browseButton.setName("browseButton"); // NOI18N + + buttonGroup1.add(downloadButton); + downloadButton.setText(resourceMap.getString("downloadButton.text")); // NOI18N + downloadButton.setName("downloadButton"); // NOI18N + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap(406, Short.MAX_VALUE) + .addComponent(returnButton) + .addGap(18, 18, 18) + .addComponent(nextButton) + .addGap(74, 74, 74)) + .addGroup(layout.createSequentialGroup() + .addGap(42, 42, 42) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(questionLabel) + .addGroup(layout.createSequentialGroup() + .addGap(12, 12, 12) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(downloadButton) + .addComponent(browseButton)))) + .addContainerGap(32, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGap(54, 54, 54) + .addComponent(questionLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(browseButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(downloadButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 120, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(nextButton) + .addComponent(returnButton)) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + +private void returnButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_returnButtonMouseClicked + saveButtonSelection(); + PropertyManager.saveProps(props); + new SpeedSelector(this.getLocationOnScreen(), this.getSize()).setVisible(true); + this.dispose(); +}//GEN-LAST:event_returnButtonMouseClicked + +private void nextButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_nextButtonMouseClicked + saveButtonSelection(); + PropertyManager.saveProps(props); + new SpeedSelector3(this.getLocationOnScreen(), this.getSize()).setVisible(true); + this.dispose(); +}//GEN-LAST:event_nextButtonMouseClicked + +private void loadButtonSelection() { + Enumeration elements = buttonGroup1.getElements(); + while(elements.hasMoreElements()) { + AbstractButton button = elements.nextElement(); + if(button == null) + continue; + if(props.getProperty(SpeedSelectorConstants.USERTYPE) == null) + break; + String type = button.getText().split(":")[0]; + if(type.equals(props.getProperty(SpeedSelectorConstants.USERTYPE))) { + button.setSelected(true); + break; + } + } +} + +private void saveButtonSelection() { + Enumeration elements = buttonGroup1.getElements(); + while(elements.hasMoreElements()) { + AbstractButton button = elements.nextElement(); + if(button == null) + continue; + if(button.isSelected()) { + String type = button.getText().split(":")[0]; + props.setProperty(SpeedSelectorConstants.USERTYPE, type); + break; + } + } +} + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JRadioButton browseButton; + private javax.swing.ButtonGroup buttonGroup1; + private javax.swing.JRadioButton downloadButton; + private javax.swing.JButton nextButton; + private javax.swing.JLabel questionLabel; + private javax.swing.JButton returnButton; + // End of variables declaration//GEN-END:variables + +} diff --git a/apps/desktopgui/src/gui/SpeedSelector3.form b/apps/desktopgui/src/gui/SpeedSelector3.form new file mode 100644 index 000000000..419a22fc1 --- /dev/null +++ b/apps/desktopgui/src/gui/SpeedSelector3.form @@ -0,0 +1,223 @@ + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    diff --git a/apps/desktopgui/src/gui/SpeedSelector3.java b/apps/desktopgui/src/gui/SpeedSelector3.java new file mode 100644 index 000000000..e226f7422 --- /dev/null +++ b/apps/desktopgui/src/gui/SpeedSelector3.java @@ -0,0 +1,286 @@ +/* + * ProfileSelector3.java + * + * Created on 3 april 2009, 15:17 + */ + +package gui; + +import java.awt.Dimension; +import java.awt.Point; +import java.util.Properties; +import persistence.PropertyManager; +import router.configuration.SpeedHandler; +import router.configuration.SpeedHelper; + +/** + * + * @author mathias + */ +public class SpeedSelector3 extends javax.swing.JFrame { + Properties props; + + /** Creates new form ProfileSelector3 */ + public SpeedSelector3(Point point, Dimension dimension) { + this.props = PropertyManager.getProps(); + initComponents(); + this.setLocation(point); + this.setSize(dimension); + initSpeeds(); + this.setVisible(true); + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + finishButton = new javax.swing.JButton(); + previousButton = new javax.swing.JButton(); + jLabel1 = new javax.swing.JLabel(); + uploadLabel = new javax.swing.JLabel(); + downloadLabel = new javax.swing.JLabel(); + uploadBurstLabel = new javax.swing.JLabel(); + downloadBurstLabel = new javax.swing.JLabel(); + uploadUsageLabel = new javax.swing.JLabel(); + downloadUsageLabel = new javax.swing.JLabel(); + uploadField = new javax.swing.JTextField(); + uploadBurstField = new javax.swing.JTextField(); + downloadField = new javax.swing.JTextField(); + downloadBurstField = new javax.swing.JTextField(); + uploadMonth = new javax.swing.JLabel(); + downloadMonth = new javax.swing.JLabel(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(desktopgui.Main.class).getContext().getResourceMap(SpeedSelector3.class); + setTitle(resourceMap.getString("Form.title")); // NOI18N + setName("Form"); // NOI18N + + finishButton.setText(resourceMap.getString("finishButton.text")); // NOI18N + finishButton.setName("finishButton"); // NOI18N + finishButton.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + finishButtonMouseClicked(evt); + } + }); + + previousButton.setText(resourceMap.getString("previousButton.text")); // NOI18N + previousButton.setName("previousButton"); // NOI18N + previousButton.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + previousButtonMouseClicked(evt); + } + }); + + jLabel1.setText(resourceMap.getString("jLabel1.text")); // NOI18N + jLabel1.setName("jLabel1"); // NOI18N + + uploadLabel.setText(resourceMap.getString("uploadLabel.text")); // NOI18N + uploadLabel.setName("uploadLabel"); // NOI18N + + downloadLabel.setText(resourceMap.getString("downloadLabel.text")); // NOI18N + downloadLabel.setName("downloadLabel"); // NOI18N + + uploadBurstLabel.setText(resourceMap.getString("uploadBurstLabel.text")); // NOI18N + uploadBurstLabel.setName("uploadBurstLabel"); // NOI18N + + downloadBurstLabel.setText(resourceMap.getString("downloadBurstLabel.text")); // NOI18N + downloadBurstLabel.setName("downloadBurstLabel"); // NOI18N + + uploadUsageLabel.setText(resourceMap.getString("uploadUsageLabel.text")); // NOI18N + uploadUsageLabel.setName("uploadUsageLabel"); // NOI18N + + downloadUsageLabel.setText(resourceMap.getString("downloadUsageLabel.text")); // NOI18N + downloadUsageLabel.setName("downloadUsageLabel"); // NOI18N + + uploadField.setText(resourceMap.getString("uploadField.text")); // NOI18N + uploadField.setMinimumSize(new java.awt.Dimension(77, 27)); + uploadField.setName("uploadField"); // NOI18N + uploadField.addKeyListener(new java.awt.event.KeyAdapter() { + public void keyReleased(java.awt.event.KeyEvent evt) { + speedFieldKeyReleased(evt); + } + }); + + uploadBurstField.setText(resourceMap.getString("uploadBurstField.text")); // NOI18N + uploadBurstField.setMinimumSize(new java.awt.Dimension(77, 27)); + uploadBurstField.setName("uploadBurstField"); // NOI18N + + downloadField.setText(resourceMap.getString("downloadField.text")); // NOI18N + downloadField.setMinimumSize(new java.awt.Dimension(77, 27)); + downloadField.setName("downloadField"); // NOI18N + downloadField.addKeyListener(new java.awt.event.KeyAdapter() { + public void keyReleased(java.awt.event.KeyEvent evt) { + speedFieldKeyReleased(evt); + } + }); + + downloadBurstField.setText(resourceMap.getString("downloadBurstField.text")); // NOI18N + downloadBurstField.setMinimumSize(new java.awt.Dimension(77, 27)); + downloadBurstField.setName("downloadBurstField"); // NOI18N + + uploadMonth.setText(resourceMap.getString("uploadMonth.text")); // NOI18N + uploadMonth.setName("uploadMonth"); // NOI18N + + downloadMonth.setText(resourceMap.getString("downloadMonth.text")); // NOI18N + downloadMonth.setName("downloadMonth"); // NOI18N + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(jLabel1) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(uploadLabel) + .addComponent(uploadBurstLabel) + .addComponent(uploadUsageLabel)) + .addGap(18, 18, 18) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(uploadBurstField, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(uploadField, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(uploadMonth, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGap(46, 46, 46) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(downloadLabel) + .addComponent(downloadBurstLabel) + .addComponent(downloadUsageLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(downloadField, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(downloadMonth, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(downloadBurstField, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addGap(18, 18, 18)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(previousButton) + .addGap(18, 18, 18) + .addComponent(finishButton) + .addGap(33, 33, 33))) + .addGap(400, 400, 400)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(81, 81, 81) + .addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(uploadLabel) + .addComponent(downloadLabel) + .addComponent(uploadField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(downloadField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(uploadBurstLabel) + .addComponent(downloadBurstLabel) + .addComponent(downloadBurstField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(uploadBurstField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(uploadUsageLabel) + .addComponent(downloadUsageLabel) + .addComponent(uploadMonth) + .addComponent(downloadMonth)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 48, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(previousButton) + .addComponent(finishButton)) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + +private void previousButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_previousButtonMouseClicked + saveSpeeds(); + PropertyManager.saveProps(props); + new SpeedSelector2(this.getLocationOnScreen(), this.getSize()).setVisible(true); + this.dispose(); +}//GEN-LAST:event_previousButtonMouseClicked + +private void finishButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_finishButtonMouseClicked + saveSpeeds(); + PropertyManager.saveProps(props); + + int maxDownload = Integer.parseInt(props.getProperty(SpeedSelectorConstants.MAXDOWNLOAD)); + int maxUpload = Integer.parseInt(props.getProperty(SpeedSelectorConstants.MAXUPLOAD)); + int maxUploadBurst = Integer.parseInt(props.getProperty(SpeedSelectorConstants.MAXUPLOADBURST)); + int maxDownloadBurst = Integer.parseInt(props.getProperty(SpeedSelectorConstants.MAXDOWNLOADBURST)); + + //Working in kB, not kb! + SpeedHandler.setInboundBandwidth(maxDownload/8); + SpeedHandler.setOutboundBandwidth(maxUpload/8); + SpeedHandler.setInboundBurstBandwidth(maxDownloadBurst); + SpeedHandler.setOutboundBurstBandwidth(maxUploadBurst/8); + + this.dispose(); +}//GEN-LAST:event_finishButtonMouseClicked + +private void speedFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_speedFieldKeyReleased + try { + initUsage(uploadField.getText(), downloadField.getText()); + } + catch(NumberFormatException e) { + return; + } +}//GEN-LAST:event_speedFieldKeyReleased + + protected void initSpeeds() { + String up = "" + SpeedHelper.calculateSpeed( + props.getProperty(SpeedSelectorConstants.MAXUPLOADCAPABLE), props.getProperty(SpeedSelectorConstants.USERTYPE)); + String upBurst = "" + SpeedHelper.calculateSpeed( + props.getProperty(SpeedSelectorConstants.MAXUPLOADCAPABLE), props.getProperty(SpeedSelectorConstants.USERTYPE)); + String down = "" + SpeedHelper.calculateSpeed( + props.getProperty(SpeedSelectorConstants.MAXDOWNLOADCAPABLE), props.getProperty(SpeedSelectorConstants.USERTYPE)); + String downBurst = "" + SpeedHelper.calculateSpeed( + props.getProperty(SpeedSelectorConstants.MAXDOWNLOADCAPABLE), props.getProperty(SpeedSelectorConstants.USERTYPE)); + String userType = props.getProperty(SpeedSelectorConstants.USERTYPE); + + uploadField.setText(up); + uploadBurstField.setText(upBurst); + downloadField.setText(down); + downloadBurstField.setText(downBurst); + + initUsage(up, down); + } + + protected void saveSpeeds() { + props.setProperty(SpeedSelectorConstants.MAXUPLOAD, uploadField.getText()); + props.setProperty(SpeedSelectorConstants.MAXUPLOADBURST, uploadBurstField.getText()); + props.setProperty(SpeedSelectorConstants.MAXDOWNLOAD, downloadField.getText()); + props.setProperty(SpeedSelectorConstants.MAXDOWNLOADBURST, downloadBurstField.getText()); + } + + protected void initUsage(String upload, String download) { + uploadMonth.setText(SpeedHelper.calculateMonthlyUsage(Integer.parseInt(upload)/8) + " GB"); + downloadMonth.setText(SpeedHelper.calculateMonthlyUsage(Integer.parseInt(download)/8) + " GB"); + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JTextField downloadBurstField; + private javax.swing.JLabel downloadBurstLabel; + private javax.swing.JTextField downloadField; + private javax.swing.JLabel downloadLabel; + private javax.swing.JLabel downloadMonth; + private javax.swing.JLabel downloadUsageLabel; + private javax.swing.JButton finishButton; + private javax.swing.JLabel jLabel1; + private javax.swing.JButton previousButton; + private javax.swing.JTextField uploadBurstField; + private javax.swing.JLabel uploadBurstLabel; + private javax.swing.JTextField uploadField; + private javax.swing.JLabel uploadLabel; + private javax.swing.JLabel uploadMonth; + private javax.swing.JLabel uploadUsageLabel; + // End of variables declaration//GEN-END:variables + +} diff --git a/apps/desktopgui/src/gui/SpeedSelectorConstants.java b/apps/desktopgui/src/gui/SpeedSelectorConstants.java new file mode 100644 index 000000000..ea5e32427 --- /dev/null +++ b/apps/desktopgui/src/gui/SpeedSelectorConstants.java @@ -0,0 +1,25 @@ +package gui; + +/** + * + * @author mathias + */ +public class SpeedSelectorConstants { + ///Maximum upload speed for the internet connection + public static final String MAXUPLOADCAPABLE = "maxUploadCapable"; + ///Maximum download speed for the internet connection + public static final String MAXDOWNLOADCAPABLE = "maxDownloadCapable"; + + //User profile type: what behaviour does this user have while using IP2? + public static final String USERTYPE = "userType"; + + //Maximum upload speed for I2P + public static final String MAXUPLOAD = "maxUpload"; + //Maximum upload burst speed for I2P + public static final String MAXUPLOADBURST = "maxUploadBurst"; + + //Maximum download speed for I2P + public static final String MAXDOWNLOAD = "maxDownload"; + //Maximum download burst speed for I2P + public static final String MAXDOWNLOADBURST = "maxDownloadBurst"; +} diff --git a/apps/desktopgui/src/gui/Tray.java b/apps/desktopgui/src/gui/Tray.java new file mode 100644 index 000000000..6ed5ea0d4 --- /dev/null +++ b/apps/desktopgui/src/gui/Tray.java @@ -0,0 +1,138 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package gui; + +import desktopgui.*; +import java.awt.AWTException; +import java.awt.Desktop; +import java.awt.Image; +import java.awt.MenuItem; +import java.awt.Menu; +import java.awt.PopupMenu; +import java.awt.SystemTray; +import java.awt.Toolkit; +import java.awt.TrayIcon; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.logging.Level; +import java.util.logging.Logger; +import router.RouterHandler; + +/** + * + * @author mathias + */ +public class Tray { + + public Tray() { + tray = SystemTray.getSystemTray(); + loadSystemTray(); + } + + private void loadSystemTray() { + + Image image = Toolkit.getDefaultToolkit().getImage("desktopgui/resources/logo/logo.jpg"); + + PopupMenu popup = new PopupMenu(); + + //Create menu items to put in the popup menu + MenuItem browserLauncher = new MenuItem("Launch browser"); + browserLauncher.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent arg0) { + if(Desktop.isDesktopSupported()) { + Desktop desktop = Desktop.getDesktop(); + try { + desktop.browse(new URI("http://localhost:7657")); + } catch (URISyntaxException ex) { + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); + } catch(IOException ex) { + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + }); + MenuItem howto = new MenuItem("How to use I2P"); + howto.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent arg0) { + if(Desktop.isDesktopSupported()) { + Desktop desktop = Desktop.getDesktop(); + try { + File f = new File("desktopgui/resources/howto/howto.html"); + desktop.browse(new URI("file://" + f.getAbsolutePath())); + } catch (URISyntaxException ex) { + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); + } catch(IOException ex) { + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + }); + Menu config = new Menu("Configuration"); + MenuItem speedConfig = new MenuItem("Speed"); + speedConfig.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent arg0) { + (new SpeedSelector()).setVisible(true); + } + + }); + MenuItem advancedConfig = new MenuItem("Advanced Configuration"); + advancedConfig.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent arg0) { + if(Desktop.isDesktopSupported()) { + Desktop desktop = Desktop.getDesktop(); + try { + desktop.browse(new URI("http://localhost:7657/config.jsp")); + } catch (URISyntaxException ex) { + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); + } catch(IOException ex) { + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + }); + MenuItem shutdown = new MenuItem("Shutdown I2P"); + shutdown.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent arg0) { + RouterHandler.setStatus(RouterHandler.SHUTDOWN_GRACEFULLY); + } + + }); + + //Add menu items to popup menu + popup.add(browserLauncher); + popup.add(howto); + + config.add(speedConfig); + config.add(advancedConfig); + popup.add(config); + + popup.add(shutdown); + + //Add tray icon + trayIcon = new TrayIcon(image, "I2P: the anonymous network", popup); + try { + tray.add(trayIcon); + } catch (AWTException ex) { + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); + } + } + + private SystemTray tray = null; + private TrayIcon trayIcon = null; + +} diff --git a/apps/desktopgui/src/gui/resources/SpeedSelector.properties b/apps/desktopgui/src/gui/resources/SpeedSelector.properties new file mode 100644 index 000000000..1a476d874 --- /dev/null +++ b/apps/desktopgui/src/gui/resources/SpeedSelector.properties @@ -0,0 +1,7 @@ + +Form.title=I2P Configuration +nextButton.text=Next +uploadLabel.text=What is your maximum upload speed? +downloadLabel.text=What is your maximum download speed? +kbps1.text=kbit/second +kbps2.text=kbit/second diff --git a/apps/desktopgui/src/gui/resources/SpeedSelector2.properties b/apps/desktopgui/src/gui/resources/SpeedSelector2.properties new file mode 100644 index 000000000..704d9909e --- /dev/null +++ b/apps/desktopgui/src/gui/resources/SpeedSelector2.properties @@ -0,0 +1,6 @@ +returnButton.text=Previous +Form.title=I2P Configuration +questionLabel.text=Which of these descriptions fits you best? +browseButton.text=Browsing: I want to use I2P to browse websites anonymously, no heavy usage. +downloadButton.text=Downloading: I want to use I2P for downloads and filesharing, heavy usage. +nextButton.text=Next diff --git a/apps/desktopgui/src/gui/resources/SpeedSelector3.properties b/apps/desktopgui/src/gui/resources/SpeedSelector3.properties new file mode 100644 index 000000000..49ee18ba0 --- /dev/null +++ b/apps/desktopgui/src/gui/resources/SpeedSelector3.properties @@ -0,0 +1,16 @@ +Form.title=I2P Configuration +jLabel1.text=The profile information your entered, indicates that these are your optimal settings: +previousButton.text=Previous +finishButton.text=Finish +uploadLabel.text=Upload Speed: +uploadBurstLabel.text=Burst Upload Speed: +downloadLabel.text=Download Speed: +downloadBurstLabel.text=Burst Download Speed: +uploadUsageLabel.text=Monthy upload usage: +downloadUsageLabel.text=Monthy Download Usage: +uploadField.text=jTextField1 +uploadBurstField.text=jTextField2 +uploadMonth.text=jLabel8 +downloadMonth.text=jLabel9 +downloadField.text=jTextField4 +downloadBurstField.text=jTextField5 diff --git a/apps/desktopgui/src/persistence/PropertyManager.java b/apps/desktopgui/src/persistence/PropertyManager.java new file mode 100644 index 000000000..bacbf348a --- /dev/null +++ b/apps/desktopgui/src/persistence/PropertyManager.java @@ -0,0 +1,72 @@ +package persistence; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author mathias + */ +public class PropertyManager { + + public static void setProps(Properties props) { + PropertyManager.props = props; + } + + public static Properties getProps() { + return props; + } + + public static Properties loadProps() { + Properties defaultProps = new Properties(); + defaultProps.setProperty("firstLoad", "true"); + + // create application properties with default + Properties applicationProps = new Properties(defaultProps); + + // now load properties from last invocation + FileInputStream in; + try { + in = new FileInputStream(PROPSLOCATION); + applicationProps.load(in); + in.close(); + } catch (FileNotFoundException ex) { + //Nothing serious, just means it's being loaded for the first time. + } catch(IOException ex) { + Logger.getLogger(PropertyManager.class.getName()).log(Level.INFO, null, ex); + } + props = applicationProps; + return applicationProps; + } + + public static void saveProps(Properties props) { + FileOutputStream out; + try { + File d = new File(PROPSDIRECTORY); + if(!d.exists()) + d.mkdir(); + File f = new File(PROPSLOCATION); + if(!f.exists()) + f.createNewFile(); + out = new FileOutputStream(f); + props.store(out, PROPSLOCATION); + } catch (FileNotFoundException ex) { + Logger.getLogger(PropertyManager.class.getName()).log(Level.SEVERE, null, ex); + } catch(IOException ex) { + Logger.getLogger(PropertyManager.class.getName()).log(Level.SEVERE, null, ex); + } + } + + private static Properties props; + + ///Location where we store the Application Properties + public static final String PROPSDIRECTORY = "desktopgui"; + public static final String PROPSFILENAME = "appProperties"; + public static final String PROPSLOCATION = PROPSDIRECTORY + File.separator + PROPSFILENAME; +} diff --git a/apps/desktopgui/src/router/RouterHandler.java b/apps/desktopgui/src/router/RouterHandler.java new file mode 100644 index 000000000..0752f877b --- /dev/null +++ b/apps/desktopgui/src/router/RouterHandler.java @@ -0,0 +1,38 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package router; + +import java.util.logging.Level; +import java.util.logging.Logger; +import net.i2p.router.RouterContext; + +/** + * + * @author mathias + */ +public class RouterHandler { + public static final int SHUTDOWN_GRACEFULLY = 0; + public static void setStatus(int status) { + if(status == SHUTDOWN_GRACEFULLY) { + Thread t = new Thread(new Runnable() { + + public void run() { + RouterContext context = RouterHelper.getContext(); + context.router().shutdownGracefully(); + while(context.router().getShutdownTimeRemaining()>0) + try { + Thread.sleep(context.router().getShutdownTimeRemaining()); + } catch (InterruptedException ex) { + Logger.getLogger(RouterHandler.class.getName()).log(Level.SEVERE, null, ex); + } + System.exit(0); + } + + }); + t.start(); + } + } +} diff --git a/apps/desktopgui/src/router/RouterHelper.java b/apps/desktopgui/src/router/RouterHelper.java new file mode 100644 index 000000000..c5fc78e95 --- /dev/null +++ b/apps/desktopgui/src/router/RouterHelper.java @@ -0,0 +1,13 @@ +package router; + +import net.i2p.router.RouterContext; + +/** + * + * @author mathias + */ +public class RouterHelper { + public static RouterContext getContext() { + return (RouterContext) RouterContext.listContexts().get(0); + } +} diff --git a/apps/desktopgui/src/router/configuration/SpeedHandler.java b/apps/desktopgui/src/router/configuration/SpeedHandler.java new file mode 100644 index 000000000..235790792 --- /dev/null +++ b/apps/desktopgui/src/router/configuration/SpeedHandler.java @@ -0,0 +1,34 @@ +package router.configuration; + +import net.i2p.router.RouterContext; +import net.i2p.router.transport.FIFOBandwidthRefiller; +import router.RouterHelper; + +/** + * + * @author mathias + */ +public class SpeedHandler { + + public static void setInboundBandwidth(int kbytes) { + context.router().setConfigSetting(FIFOBandwidthRefiller.PROP_INBOUND_BANDWIDTH, "" + kbytes); + context.router().saveConfig(); + } + + public static void setOutboundBandwidth(int kbytes) { + context.router().setConfigSetting(FIFOBandwidthRefiller.PROP_OUTBOUND_BANDWIDTH, "" + kbytes); + context.router().saveConfig(); + } + + public static void setInboundBurstBandwidth(int kbytes) { + context.router().setConfigSetting(FIFOBandwidthRefiller.PROP_INBOUND_BURST_BANDWIDTH, "" + kbytes); + context.router().saveConfig(); + } + + public static void setOutboundBurstBandwidth(int kbytes) { + context.router().setConfigSetting(FIFOBandwidthRefiller.PROP_OUTBOUND_BURST_BANDWIDTH, "" + kbytes); + context.router().saveConfig(); + } + + private static final RouterContext context = RouterHelper.getContext(); +} diff --git a/apps/desktopgui/src/router/configuration/SpeedHelper.java b/apps/desktopgui/src/router/configuration/SpeedHelper.java new file mode 100644 index 000000000..1cce2f9c0 --- /dev/null +++ b/apps/desktopgui/src/router/configuration/SpeedHelper.java @@ -0,0 +1,28 @@ +package router.configuration; + +/** + * + * @author mathias + */ +public class SpeedHelper { + public static final String USERTYPE_BROWSING = "Browsing"; + public static final String USERTYPE_DOWNLOADING = "Downloading"; + + public static int calculateSpeed(String capable, String profile) { + int capableSpeed = Integer.parseInt(capable); + int advisedSpeed = capableSpeed; + if(capableSpeed > 1000) { + if(profile.equals(USERTYPE_BROWSING)) //Don't overdo usage for people just wanting to browse (we don't want to drive them away due to resource hogging) + advisedSpeed *= 0.6; + else if(profile.equals(USERTYPE_DOWNLOADING)) + advisedSpeed *= 0.8; + } + else + advisedSpeed *= 0.6; //Lower available bandwidth: don't hog all the bandwidth + return advisedSpeed; + } + + public static int calculateMonthlyUsage(int kbytes) { + return (kbytes*3600*24*31)/1000000; + } +} diff --git a/apps/desktopgui/src/util/IntegerVerifier.java b/apps/desktopgui/src/util/IntegerVerifier.java new file mode 100644 index 000000000..74f87961d --- /dev/null +++ b/apps/desktopgui/src/util/IntegerVerifier.java @@ -0,0 +1,32 @@ +package util; + +import javax.swing.InputVerifier; +import javax.swing.JComponent; +import javax.swing.JTextField; + +/** + * + * @author mathias + */ + +public class IntegerVerifier extends InputVerifier { + + @Override + public boolean verify(JComponent arg0) { + JTextField jtf = (JTextField) arg0; + return verify(jtf.getText()); + } + + @Override + public boolean shouldYieldFocus(JComponent input) { + return verify(input); + } + + public static boolean verify(String s) { + for(int i=0;i '9' || s.charAt(i) < '0') + return false; + return true; + } + +} \ No newline at end of file diff --git a/build.xml b/build.xml index ef84b2aab..26254cd15 100644 --- a/build.xml +++ b/build.xml @@ -37,6 +37,7 @@ + @@ -104,6 +105,7 @@ + @@ -214,6 +216,13 @@ + + + + + + + diff --git a/installer/resources/wrapper.config b/installer/resources/wrapper.config index 4d09ba12c..42958ba77 100644 --- a/installer/resources/wrapper.config +++ b/installer/resources/wrapper.config @@ -54,6 +54,10 @@ wrapper.java.classpath.17=lib/systray.jar wrapper.java.classpath.18=lib/systray4j.jar # BOB wrapper.java.classpath.19=lib/BOB.jar +# desktopgui +wrapper.java.classpath.20=lib/appframework.jar +wrapper.java.classpath.21=lib/swing-worker.jar +wrapper.java.classpath.22=lib/desktopgui.jar # Java Library Path (location of Wrapper.DLL or libwrapper.so) wrapper.java.library.path.1=. From 9e7dd238a44140ae388637d6ce025ed4893a2636 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 6 Apr 2009 19:43:54 +0000 Subject: [PATCH 101/688] prevent NPE --- .../net/i2p/router/transport/ntcp/EstablishState.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/router/java/src/net/i2p/router/transport/ntcp/EstablishState.java b/router/java/src/net/i2p/router/transport/ntcp/EstablishState.java index 942d6cbc9..f8ccf0814 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/EstablishState.java +++ b/router/java/src/net/i2p/router/transport/ntcp/EstablishState.java @@ -462,7 +462,6 @@ public class EstablishState { if (!_verified) { _context.statManager().addRateData("ntcp.invalidSignature", 1, 0); fail("Signature was invalid - attempt to spoof " + _con.getRemotePeer().calculateHash().toBase64() + "?"); - return; } else { if (_log.shouldLog(Log.DEBUG)) _log.debug(prefix() + "signature verified from Bob. done!"); @@ -472,10 +471,12 @@ public class EstablishState { byte nextReadIV[] = new byte[16]; System.arraycopy(_e_bobSig, _e_bobSig.length-16, nextReadIV, 0, nextReadIV.length); _con.finishOutboundEstablishment(_dh.getSessionKey(), (_tsA-_tsB), nextWriteIV, nextReadIV); // skew in seconds - _transport.setIP(_con.getRemotePeer().calculateHash(), - _con.getChannel().socket().getInetAddress().getAddress()); - return; + // if socket gets closed this will be null - prevent NPE + InetAddress ia = _con.getChannel().socket().getInetAddress(); + if (ia != null) + _transport.setIP(_con.getRemotePeer().calculateHash(), ia.getAddress()); } + return; } } } From f5614c8a416ed6749fe212ca4a6fee7cfc0f84f7 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 6 Apr 2009 19:44:47 +0000 Subject: [PATCH 102/688] synchronize datagram maker in I2PSinks --- .../i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSink.java | 8 +++++--- .../java/src/net/i2p/i2ptunnel/udp/I2PSinkAnywhere.java | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSink.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSink.java index 6a32801b7..f7a1bf541 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSink.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSink.java @@ -37,9 +37,11 @@ public class I2PSink implements Sink { //System.out.print("w"); // create payload byte[] payload; - if(!this.raw) - payload = this.maker.makeI2PDatagram(data); - else + if(!this.raw) { + synchronized(this.maker) { + payload = this.maker.makeI2PDatagram(data); + } + } else payload = data; // send message diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSinkAnywhere.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSinkAnywhere.java index 8707d9779..54d5a7d97 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSinkAnywhere.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSinkAnywhere.java @@ -35,9 +35,11 @@ public class I2PSinkAnywhere implements Sink { public synchronized void send(Destination to, byte[] data) { // create payload byte[] payload; - if(!this.raw) - payload = this.maker.makeI2PDatagram(data); - else + if(!this.raw) { + synchronized(this.maker) { + payload = this.maker.makeI2PDatagram(data); + } + } else payload = data; // send message From 37667247c3d2c0b7a5d26bb6d136497cddd05294 Mon Sep 17 00:00:00 2001 From: sponge Date: Mon, 6 Apr 2009 22:40:22 +0000 Subject: [PATCH 103/688] 2009-04-06 sponge SimpleScheduler SimpleTimer2 debugging added. Fix build files for desktopgui. --- apps/desktopgui/build.xml | 5 ++++ apps/desktopgui/nbproject/build-impl.xml | 25 ++++++++++++++----- apps/desktopgui/nbproject/genfiles.properties | 8 +++--- apps/desktopgui/nbproject/project.properties | 4 +-- .../src/net/i2p/util/SimpleScheduler.java | 7 +++++- core/java/src/net/i2p/util/SimpleTimer2.java | 7 +++++- history.txt | 4 +++ .../src/net/i2p/router/RouterVersion.java | 2 +- 8 files changed, 47 insertions(+), 15 deletions(-) diff --git a/apps/desktopgui/build.xml b/apps/desktopgui/build.xml index c7ba1be11..dbcbab67c 100644 --- a/apps/desktopgui/build.xml +++ b/apps/desktopgui/build.xml @@ -2,6 +2,11 @@ + + + + + Builds, tests, and runs the project desktopgui. diff --git a/apps/desktopgui/nbproject/build-impl.xml b/apps/desktopgui/nbproject/build-impl.xml index f8fea458d..039f8788f 100644 --- a/apps/desktopgui/nbproject/build-impl.xml +++ b/apps/desktopgui/nbproject/build-impl.xml @@ -152,7 +152,7 @@ is divided into following sections: - + @@ -218,13 +218,13 @@ is divided into following sections: - + - + @@ -255,6 +255,12 @@ is divided into following sections: + + + + + + @@ -264,7 +270,7 @@ is divided into following sections: - + @@ -311,6 +317,13 @@ is divided into following sections: =================== --> + + + + + + + @@ -331,7 +344,7 @@ is divided into following sections: - + @@ -345,7 +358,7 @@ is divided into following sections: - + From 91de396821334b9c88fc5cfbd1b4f76df778ddad Mon Sep 17 00:00:00 2001 From: amiga4000 Date: Tue, 7 Apr 2009 12:05:00 +0000 Subject: [PATCH 108/688] added echelon.i2p to readme of I2P router --- readme.html | 1 + readme_de.html | 3 ++- readme_fr.html | 1 + readme_sv.html | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/readme.html b/readme.html index 0e4891b5d..c35991335 100644 --- a/readme.html +++ b/readme.html @@ -16,6 +16,7 @@ you can:

  • ugha.i2p: ugha's eepsite, a wiki that anyone can edit, and lots of links
  • fproxy.tino.i2p: Freenet proxy
  • +
  • echelon.i2p: software archive and information for I2P
  • There are many more eepsites - just follow the links from the ones you see, bookmark your favorites, and visit them often! diff --git a/readme_de.html b/readme_de.html index 1534585da..e6fd91618 100644 --- a/readme_de.html +++ b/readme_de.html @@ -12,7 +12,8 @@
  • eepsites.i2p: Eine anonym gehostete Suchseite für Eepsites
  • ugha.i2p: Ugha's Eepsite, ein öffentliches Wiki mit vielen Links
  • fproxy.tino.i2p: Freenet proxy
  • - +
  • echelon.i2p: Software Archive und Informationen zu I2P
  • + Es gibt viel mehr Eepsites - folge einfach den Links die du findest, bookmarke Deine Favoriten und besuche sie oft!
  • Im Internet surfen - Es gibt einen HTTP "outproxy" in I2P in Deinem diff --git a/readme_fr.html b/readme_fr.html index 5e619cff2..f2accf826 100644 --- a/readme_fr.html +++ b/readme_fr.html @@ -11,6 +11,7 @@
  • eepsites.i2p: un moteur de recherche d'eepsites
  • ugha.i2p: l'eepsite d'ugha, un wiki que chaucun peut éditer ainsi que
  • fproxy.tino.i2p: un proxy Freenet 0.5
  • +
  • echelon.i2p: archive
  • Il y a bien plus d'eepsites - suivez juste les liens au départ de ceux sur lesquels vous êtes, mettez-les dans vos favoris et visitez-les souvent!
  • Parcourez le web - Il y a pour l'instant un outproxy HTTP sur I2P attaché à votre propre proxy HTTP sur le port 4444 - vous devez simplement configurer le proxy de votre navigateur pour l'utiliser (comme expliqué ci-dessus) et aller sur n'importe quel URL normale - vos requêtes seront relayées par le réseau i2p.
  • diff --git a/readme_sv.html b/readme_sv.html index b1f59fa04..eab177cd1 100644 --- a/readme_sv.html +++ b/readme_sv.html @@ -27,6 +27,7 @@ sökmotor wiki som alla kan förändra, innehåller många länkar
  • fproxy.tino.i2p: Freenet proxy
  • +
  • echelon.i2p: software archive and information for I2P
  • Det finns många fler eepsidor - följ bara länkarna från dom du ser, spara dina favoriter och besök dom ofta! From 0764e19441a43651c144ec9d0d23c0440f34eed2 Mon Sep 17 00:00:00 2001 From: mathiasdm Date: Tue, 7 Apr 2009 17:55:59 +0000 Subject: [PATCH 109/688] New build file for desktopgui. Should work fine for anyone using java 1.6. --- apps/desktopgui/build.xml | 113 +++++++----------- apps/desktopgui/nbproject/build-impl.xml | 25 +--- apps/desktopgui/nbproject/genfiles.properties | 8 +- apps/desktopgui/nbproject/project.properties | 15 ++- .../src/desktopgui/resources/Main.properties | 2 +- 5 files changed, 67 insertions(+), 96 deletions(-) diff --git a/apps/desktopgui/build.xml b/apps/desktopgui/build.xml index dbcbab67c..cf262b2ec 100644 --- a/apps/desktopgui/build.xml +++ b/apps/desktopgui/build.xml @@ -1,74 +1,51 @@ - - - - - - - - - - - Builds, tests, and runs the project desktopgui. - - + + + + + + + + + + - There exist several targets which are by default empty and which can be - used for execution of your tasks. These targets are usually executed - before and after some main targets. They are: + + + + + + + - -pre-init: called before initialization of project properties - -post-init: called after initialization of project properties - -pre-compile: called before javac compilation - -post-compile: called after javac compilation - -pre-compile-single: called before javac compilation of single file - -post-compile-single: called after javac compilation of single file - -pre-compile-test: called before javac compilation of JUnit tests - -post-compile-test: called after javac compilation of JUnit tests - -pre-compile-test-single: called before javac compilation of single JUnit test - -post-compile-test-single: called after javac compilation of single JUunit test - -pre-jar: called before JAR building - -post-jar: called after JAR building - -post-clean: called after cleaning build products + + + + + + + + + - (Targets beginning with '-' are not intended to be called on their own.) + + + - Example of inserting an obfuscator after compilation could look like this: + + + - - - - - - - For list of available properties check the imported - nbproject/build-impl.xml file. - - - Another way to customize the build is by overriding existing main targets. - The targets of interest are: - - -init-macrodef-javac: defines macro for javac compilation - -init-macrodef-junit: defines macro for junit execution - -init-macrodef-debug: defines macro for class debugging - -init-macrodef-java: defines macro for class execution - -do-jar-with-manifest: JAR building (if you are using a manifest) - -do-jar-without-manifest: JAR building (if you are not using a manifest) - run: execution of project - -javadoc-build: Javadoc generation - test-report: JUnit report generation - - An example of overriding the target for project execution could look like this: - - - - - - - - Notice that the overridden target depends on the jar target and not only on - the compile target as the regular run target does. Again, for a list of available - properties which you can use, check the target you are overriding in the - nbproject/build-impl.xml file. - - --> + + + + + diff --git a/apps/desktopgui/nbproject/build-impl.xml b/apps/desktopgui/nbproject/build-impl.xml index 039f8788f..f8fea458d 100644 --- a/apps/desktopgui/nbproject/build-impl.xml +++ b/apps/desktopgui/nbproject/build-impl.xml @@ -152,7 +152,7 @@ is divided into following sections: - + @@ -218,13 +218,13 @@ is divided into following sections: - + - + @@ -255,12 +255,6 @@ is divided into following sections: - - - - - - @@ -270,7 +264,7 @@ is divided into following sections: - + @@ -317,13 +311,6 @@ is divided into following sections: =================== --> - - - - - - - @@ -344,7 +331,7 @@ is divided into following sections: - + @@ -358,7 +345,7 @@ is divided into following sections: - + I2PSnark | - My Eepsite
    + My Eepsite
    I2PTunnel | Tunnels | Profiles | diff --git a/apps/susimail/src/src/i2p/susi/webmail/WebMail.java b/apps/susimail/src/src/i2p/susi/webmail/WebMail.java index a80fdc591..6e9fad1c5 100644 --- a/apps/susimail/src/src/i2p/susi/webmail/WebMail.java +++ b/apps/susimail/src/src/i2p/susi/webmail/WebMail.java @@ -76,7 +76,7 @@ public class WebMail extends HttpServlet private static final int BUFSIZE = 4096; - private static final String DEFAULT_HOST = "localhost"; + private static final String DEFAULT_HOST = "127.0.0.1"; private static final int DEFAULT_POP3PORT = 7660; private static final int DEFAULT_SMTPPORT = 7659; diff --git a/apps/systray/java/src/net/i2p/apps/systray/SysTray.java b/apps/systray/java/src/net/i2p/apps/systray/SysTray.java index 4a635fd08..652ff6677 100644 --- a/apps/systray/java/src/net/i2p/apps/systray/SysTray.java +++ b/apps/systray/java/src/net/i2p/apps/systray/SysTray.java @@ -132,7 +132,7 @@ public class SysTray implements SysTrayMenuListener { public void iconLeftClicked(SysTrayMenuEvent e) {} public void iconLeftDoubleClicked(SysTrayMenuEvent e) { - openRouterConsole("http://localhost:" + _portString + "/index.jsp"); + openRouterConsole("http://127.0.0.1:" + _portString + "/index.jsp"); } public void menuItemSelected(SysTrayMenuEvent e) { @@ -153,7 +153,7 @@ public class SysTray implements SysTrayMenuListener { if (!(browser = promptForBrowser("Select browser")).equals("nullnull")) setBrowser(browser); } else if (e.getActionCommand().equals("openconsole")) { - openRouterConsole("http://localhost:" + _portString + "/index.jsp"); + openRouterConsole("http://127.0.0.1:" + _portString + "/index.jsp"); } } diff --git a/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java b/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java index 5487f5faf..c7524054e 100644 --- a/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java +++ b/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java @@ -163,7 +163,7 @@ public class UrlLauncher { if (args.length > 0) launcher.openUrl(args[0]); else - launcher.openUrl("http://localhost:7657/index.jsp"); + launcher.openUrl("http://127.0.0.1:7657/index.jsp"); } catch (Exception e) {} } } diff --git a/core/java/src/net/i2p/client/I2PSessionImpl.java b/core/java/src/net/i2p/client/I2PSessionImpl.java index 5b7603fdd..9e42eef5f 100644 --- a/core/java/src/net/i2p/client/I2PSessionImpl.java +++ b/core/java/src/net/i2p/client/I2PSessionImpl.java @@ -162,7 +162,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa protected void loadConfig(Properties options) { _options = new Properties(); _options.putAll(filter(options)); - _hostname = _options.getProperty(I2PClient.PROP_TCP_HOST, "localhost"); + _hostname = _options.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1"); String portNum = _options.getProperty(I2PClient.PROP_TCP_PORT, LISTEN_PORT + ""); try { _portNum = Integer.parseInt(portNum); diff --git a/core/java/src/net/i2p/util/EepGet.java b/core/java/src/net/i2p/util/EepGet.java index 4abdc6fd4..5f0e8d5e9 100644 --- a/core/java/src/net/i2p/util/EepGet.java +++ b/core/java/src/net/i2p/util/EepGet.java @@ -18,7 +18,7 @@ import net.i2p.I2PAppContext; import net.i2p.data.DataHelper; /** - * EepGet [-p localhost:4444] + * EepGet [-p 127.0.0.1:4444] * [-n #retries] * [-o outputFile] * [-m markSize lineLen] @@ -123,11 +123,11 @@ public class EepGet { } /** - * EepGet [-p localhost:4444] [-n #retries] [-e etag] [-o outputFile] [-m markSize lineLen] url + * EepGet [-p 127.0.0.1:4444] [-n #retries] [-e etag] [-o outputFile] [-m markSize lineLen] url * */ public static void main(String args[]) { - String proxyHost = "localhost"; + String proxyHost = "127.0.0.1"; int proxyPort = 4444; int numRetries = 5; int markSize = 1024; @@ -212,7 +212,7 @@ public class EepGet { } private static void usage() { - System.err.println("EepGet [-p localhost:4444] [-n #retries] [-o outputFile] [-m markSize lineLen] [-t timeout] url"); + System.err.println("EepGet [-p 127.0.0.1:4444] [-n #retries] [-o outputFile] [-m markSize lineLen] [-t timeout] url"); } public static interface StatusListener { diff --git a/installer/resources/ahelper-conflict-header.ht b/installer/resources/ahelper-conflict-header.ht index 3b80e582f..7f34d21cc 100644 --- a/installer/resources/ahelper-conflict-header.ht +++ b/installer/resources/ahelper-conflict-header.ht @@ -6,7 +6,7 @@ Proxy-Connection: close Destination key conflict - + - - - -
    -The addresshelper link you followed specifies a different destination key -than a host entry in your host database. -Someone could be trying to impersonate another eepsite, -or people have given two eepsites identical names. -

    -You can resolve the conflict by considering which key you trust, -and either discarding the addresshelper link, -discarding the host entry from your host database, -or naming one of them differently. -

    + +Destination key conflict + + + + +

    +
    +The addresshelper link you followed specifies a different destination key +than a host entry in your host database. +Someone could be trying to impersonate another eepsite, +or people have given two eepsites identical names. +

    +You can resolve the conflict by considering which key you trust, +and either discarding the addresshelper link, +discarding the host entry from your host database, +or naming one of them differently. +

    diff --git a/installer/resources/dnf-header.ht b/installer/resources/dnf-header.ht index 2293d938c..8de2824bf 100644 --- a/installer/resources/dnf-header.ht +++ b/installer/resources/dnf-header.ht @@ -6,33 +6,12 @@ Proxy-Connection: close Eepsite not reachable - - + +

    diff --git a/installer/resources/dnfb-header.ht b/installer/resources/dnfb-header.ht index 51f9c63ad..890a1910f 100644 --- a/installer/resources/dnfb-header.ht +++ b/installer/resources/dnfb-header.ht @@ -6,33 +6,12 @@ Proxy-Connection: close Invalid eepsite destination - - + +
    diff --git a/installer/resources/dnfh-header.ht b/installer/resources/dnfh-header.ht index eea94252e..307621aaa 100644 --- a/installer/resources/dnfh-header.ht +++ b/installer/resources/dnfh-header.ht @@ -6,33 +6,12 @@ Proxy-Connection: close Eepsite unknown - - + +
    diff --git a/installer/resources/dnfp-header.ht b/installer/resources/dnfp-header.ht index 40358c3eb..b579e64b9 100644 --- a/installer/resources/dnfp-header.ht +++ b/installer/resources/dnfp-header.ht @@ -4,47 +4,25 @@ Cache-control: no-cache Connection: close Proxy-Connection: close - -Outproxy Not Found - - - - - - -
    -The WWW Outproxy was not found. -It is offline, there is network congestion, -or your router is not yet well-integrated with peers. -You may want to -retry -as this will randomly reselect an outproxy from the pool you have defined -here -(if you have more than one configured). -If you continue to have trouble you may want to edit your outproxy list -here. -

    Could not find the following destination:

    + +Outproxy Not Found + + + + + +
    +The WWW Outproxy was not found. +It is offline, there is network congestion, +or your router is not yet well-integrated with peers. +You may want to +retry +as this will randomly reselect an outproxy from the pool you have defined +here +(if you have more than one configured). +If you continue to have trouble you may want to edit your outproxy list +here. +

    Could not find the following destination:

    From 256c5356fb84a56cc5857a2bd8afb042c51aa0c0 Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 28 Jun 2009 17:40:17 +0000 Subject: [PATCH 397/688] Add router log location to logs.jsp --- .../java/src/net/i2p/router/web/LogsHelper.java | 3 ++- core/java/src/net/i2p/util/LogManager.java | 4 ++++ core/java/src/net/i2p/util/LogWriter.java | 5 ++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java index 9aee053f5..e7fb539ec 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java @@ -9,7 +9,8 @@ public class LogsHelper extends HelperBase { public LogsHelper() {} public String getLogs() { - return formatMessages(_context.logManager().getBuffer().getMostRecentMessages()); + String str = formatMessages(_context.logManager().getBuffer().getMostRecentMessages()); + return "Location: " + _context.logManager().currentFile() + "
    " + str; } public String getCriticalLogs() { diff --git a/core/java/src/net/i2p/util/LogManager.java b/core/java/src/net/i2p/util/LogManager.java index c07445d9b..73178b0d6 100644 --- a/core/java/src/net/i2p/util/LogManager.java +++ b/core/java/src/net/i2p/util/LogManager.java @@ -202,6 +202,10 @@ public class LogManager { loadConfig(); } + public String currentFile() { + return _writer.currentFile(); + } + /** * Used by Log to add records to the queue * diff --git a/core/java/src/net/i2p/util/LogWriter.java b/core/java/src/net/i2p/util/LogWriter.java index c9f2cb756..b6302c3c4 100644 --- a/core/java/src/net/i2p/util/LogWriter.java +++ b/core/java/src/net/i2p/util/LogWriter.java @@ -93,7 +93,10 @@ class LogWriter implements Runnable { } } - + public String currentFile() { + return _currentFile != null ? _currentFile.getAbsolutePath() : "uninitialized"; + } + private void rereadConfig() { long now = Clock.getInstance().now(); if (now - _lastReadConfig > CONFIG_READ_ITERVAL) { From 9e1181900be79b925c0d71ea2cf1f557bb396d32 Mon Sep 17 00:00:00 2001 From: complication Date: Mon, 29 Jun 2009 01:07:51 +0000 Subject: [PATCH 398/688] * Update versions, package release * Remove the last reference to my eepsite as a "news.xml" source, and likewise stop my public key from being included among valid release signing keys. --- .../router/configuration/UpdateHelper.java | 3 +- core/java/src/net/i2p/CoreVersion.java | 2 +- .../src/net/i2p/crypto/TrustedUpdate.java | 1 - history.txt | 8 ++++ initialNews.xml | 4 +- installer/install.xml | 2 +- news.xml | 45 ++++++++++++------- .../src/net/i2p/router/RouterVersion.java | 2 +- 8 files changed, 43 insertions(+), 24 deletions(-) diff --git a/apps/desktopgui/src/net/i2p/desktopgui/router/configuration/UpdateHelper.java b/apps/desktopgui/src/net/i2p/desktopgui/router/configuration/UpdateHelper.java index 86db6f708..6e28db108 100644 --- a/apps/desktopgui/src/net/i2p/desktopgui/router/configuration/UpdateHelper.java +++ b/apps/desktopgui/src/net/i2p/desktopgui/router/configuration/UpdateHelper.java @@ -9,7 +9,7 @@ import net.i2p.desktopgui.router.RouterHelper; public class UpdateHelper { public static final String PROP_NEWS_URL = "router.newsURL"; - public static final String DEFAULT_NEWS_URL = "http://complication.i2p/news.xml"; + public static final String DEFAULT_NEWS_URL = "http://echelon.i2p/i2p/news.xml"; public static final String PROP_REFRESH_FREQUENCY = "router.newsRefreshFrequency"; public static final String DEFAULT_REFRESH_FREQUENCY = 24*60*60*1000 + ""; @@ -31,7 +31,6 @@ public class UpdateHelper { public static final String DEFAULT_UPDATE_URL = "http://echelon.i2p/i2p/i2pupdate.sud\r\n" + "http://stats.i2p/i2p/i2pupdate.sud\r\n" + - "http://complication.i2p/i2p/i2pupdate.sud\r\n" + "http://www.i2p2.i2p/_static/i2pupdate.sud\r\n" + "http://update.postman.i2p/i2pupdate.sud" ; diff --git a/core/java/src/net/i2p/CoreVersion.java b/core/java/src/net/i2p/CoreVersion.java index 972b87966..fecc6c9a8 100644 --- a/core/java/src/net/i2p/CoreVersion.java +++ b/core/java/src/net/i2p/CoreVersion.java @@ -15,7 +15,7 @@ package net.i2p; */ public class CoreVersion { public final static String ID = "$Revision: 1.72 $ $Date: 2008-08-24 12:00:00 $"; - public final static String VERSION = "0.7.4"; + public final static String VERSION = "0.7.5"; public static void main(String args[]) { System.out.println("I2P Core version: " + VERSION); diff --git a/core/java/src/net/i2p/crypto/TrustedUpdate.java b/core/java/src/net/i2p/crypto/TrustedUpdate.java index 06e37544b..33a7e8cb7 100644 --- a/core/java/src/net/i2p/crypto/TrustedUpdate.java +++ b/core/java/src/net/i2p/crypto/TrustedUpdate.java @@ -141,7 +141,6 @@ D8usM7Dxp5yrDrCYZ5AIijc= } else { _trustedKeys.add(DEFAULT_TRUSTED_KEY); _trustedKeys.add(DEFAULT_TRUSTED_KEY2); - _trustedKeys.add(DEFAULT_TRUSTED_KEY3); } if (_log.shouldLog(Log.DEBUG)) _log.debug("TrustedUpdate created, trusting " + _trustedKeys.size() + " keys."); diff --git a/history.txt b/history.txt index 33381eb02..dfd639beb 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,11 @@ +* 2009-06-29 0.7.5 released + +2009-06-29 Complication + * Update versions, package release + * Remove the last reference to my eepsite as a "news.xml" source, + and likewise stop my public key from being included + among valid release signing keys. + 2009-06-25 sponge * Summary frame layout change so it makes sense. diff --git a/initialNews.xml b/initialNews.xml index 0a2c5b12b..9a1d4bb78 100644 --- a/initialNews.xml +++ b/initialNews.xml @@ -1,5 +1,5 @@ - - +

    Congratulations on getting I2P installed!

  • eepsites.i2p: an anonymously hosted search engine of eepsites