Changeset 8f7b31a


Ignore:
Timestamp:
Oct 23, 2013 8:20:54 PM (8 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
35a86e60
Parents:
0f5a0b6
Message:
  • I2PTunnel standard and IRC clients:
    • Allow host:port targets; set defaults in i2ptunnel.config (ticket #1066)
    • Don't fail start if hostname is unresolvable; retry at connect time (ticket #946)
    • Output IRC message on connect fail
    • Update target list on-the-fly when configuration changes
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java

    r0f5a0b6 r8f7b31a  
    55
    66import java.net.Socket;
     7import java.net.UnknownHostException;
    78import java.util.ArrayList;
    89import java.util.List;
     10import java.util.Properties;
    911import java.util.StringTokenizer;
    1012
    1113import net.i2p.client.streaming.I2PSocket;
     14import net.i2p.client.streaming.I2PSocketAddress;
    1215import net.i2p.data.Destination;
    1316import net.i2p.util.EventDispatcher;
     
    1619public class I2PTunnelClient extends I2PTunnelClientBase {
    1720
    18     /** list of Destination objects that we point at */
     21    /**
     22     * list of Destination objects that we point at
     23     * @deprecated why protected? Is anybody using out-of-tree? Protected from the beginning (2004)
     24     */
    1925    protected List<Destination> dests;
     26
     27    /**
     28     * replacement for dests
     29     */
     30    private final List<I2PSocketAddress> _addrs;
    2031    private static final long DEFAULT_READ_TIMEOUT = 5*60*1000; // -1
    2132    protected long readTimeout = DEFAULT_READ_TIMEOUT;
    2233
    2334    /**
    24      * @param destinations comma delimited list of peers we target
     35     * @param destinations peers we target, comma- or space-separated. Since 0.9.9, each dest may be appended with :port
    2536     * @throws IllegalArgumentException if the I2PTunnel does not contain
    2637     *                                  valid config to contact the router
     
    3344              tunnel, pkf);
    3445
     46        _addrs = new ArrayList(1);
    3547        if (waitEventValue("openBaseClientResult").equals("error")) {
    3648            notifyEvent("openClientResult", "error");
     
    3850        }
    3951
    40         StringTokenizer tok = new StringTokenizer(destinations, ", ");
    4152        dests = new ArrayList(1);
    42         while (tok.hasMoreTokens()) {
    43             String destination = tok.nextToken();
    44             Destination destN = _context.namingService().lookup(destination);
    45             if (destN == null)
    46                 l.log("Could not resolve " + destination);
    47             else
    48                 dests.add(destN);
    49         }
     53        buildAddresses(destinations);
    5054
    51         if (dests.isEmpty()) {
     55        if (_addrs.isEmpty()) {
    5256            l.log("No valid target destinations found");
    5357            notifyEvent("openClientResult", "error");
     
    5559            // Maybe figure out where to put a waitEventValue("openClientResult") ??
    5660            // In the meantime, let's do this the easy way
    57             // Note that b32 dests will often not be resolvable at instantiation time;
    58             // a delayed resolution system would be even better.
    5961
    6062            // Don't close() here, because it does a removeSession() and then
     
    7375    }
    7476
     77    /** @since 0.9.9 moved from constructor */
     78    private void buildAddresses(String destinations) {
     79        if (destinations == null)
     80            return;
     81        StringTokenizer tok = new StringTokenizer(destinations, ", ");
     82        synchronized(_addrs) {
     83            _addrs.clear();
     84            while (tok.hasMoreTokens()) {
     85                String destination = tok.nextToken();
     86                try {
     87                    // Try to resolve here but only log if it doesn't.
     88                    // Note that b32 _addrs will often not be resolvable at instantiation time.
     89                    // We will try again to resolve in clientConnectionRun()
     90                    I2PSocketAddress addr = new I2PSocketAddress(destination);
     91                    _addrs.add(addr);
     92                    if (addr.isUnresolved()) {
     93                        String name = addr.getHostName();
     94                        if (name.length() == 60 && name.endsWith(".b32.i2p"))
     95                            l.log("Warning - Could not resolve " + name +
     96                                  ", perhaps it is not up, will retry when connecting.");
     97                        else
     98                            l.log("Warning - Could not resolve " + name +
     99                                  ", you must add it to your address book for it to work.");
     100                    } else {
     101                        dests.add(addr.getAddress());
     102                    }
     103                } catch (IllegalArgumentException iae) {
     104                     l.log("Bad destination " + destination + " - " + iae);
     105                }
     106            }
     107        }
     108    }
     109
    75110    public void setReadTimeout(long ms) { readTimeout = ms; }
    76111    public long getReadTimeout() { return readTimeout; }
    77112   
    78113    protected void clientConnectionRun(Socket s) {
    79         Destination destN = pickDestination();
    80114        I2PSocket i2ps = null;
    81115        try {
    82             i2ps = createI2PSocket(destN);
     116            I2PSocketAddress addr = pickDestination();
     117            if (addr == null)
     118                throw new UnknownHostException("No valid destination configured");
     119            Destination clientDest = addr.getAddress();
     120            if (clientDest == null)
     121                throw new UnknownHostException("Could not resolve " + addr.getHostName());
     122            int port = addr.getPort();
     123            i2ps = createI2PSocket(clientDest, port);
    83124            i2ps.setReadTimeout(readTimeout);
    84125            new I2PTunnelRunner(s, i2ps, sockLock, null, mySockets);
     
    96137    }
    97138   
    98     private final Destination pickDestination() {
    99         int size = dests.size();
    100         if (size <= 0) {
    101             if (_log.shouldLog(Log.ERROR))
    102                 _log.error("No client targets?!");
    103             return null;
     139    private final I2PSocketAddress pickDestination() {
     140        synchronized(_addrs) {
     141            int size = _addrs.size();
     142            if (size <= 0) {
     143                if (_log.shouldLog(Log.ERROR))
     144                    _log.error("No client targets?!");
     145                return null;
     146            }
     147            if (size == 1) // skip the rand in the most common case
     148                return _addrs.get(0);
     149            int index = _context.random().nextInt(size);
     150            return _addrs.get(index);
    104151        }
    105         if (size == 1) // skip the rand in the most common case
    106             return dests.get(0);
    107         int index = _context.random().nextInt(size);
    108         return dests.get(index);
     152    }
     153
     154    /**
     155     *  Update the dests then call super.
     156     *
     157     *  @since 0.9.9
     158     */
     159    @Override
     160    public void optionsUpdated(I2PTunnel tunnel) {
     161        if (getTunnel() != tunnel)
     162            return;
     163        Properties props = tunnel.getClientOptions();
     164        // see TunnelController.setSessionOptions()
     165        String targets = props.getProperty("targetDestination");
     166        buildAddresses(targets);
     167        super.optionsUpdated(tunnel);
    109168    }
    110169}
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java

    r0f5a0b6 r8f7b31a  
    522522     */
    523523    public I2PSocket createI2PSocket(Destination dest) throws I2PException, ConnectException, NoRouteToHostException, InterruptedIOException {
     524        return createI2PSocket(dest, 0);
     525    }
     526
     527    /**
     528     * Create a new I2PSocket towards to the specified destination,
     529     * adding it to the list of connections actually managed by this
     530     * tunnel.
     531     *
     532     * @param dest The destination to connect to
     533     * @param port The destination port to connect to 0 - 65535
     534     * @return a new I2PSocket
     535     * @since 0.9.9
     536     */
     537    public I2PSocket createI2PSocket(Destination dest, int port)
     538                throws I2PException, ConnectException, NoRouteToHostException, InterruptedIOException {
    524539        verifySocketManager();
    525         return createI2PSocket(dest, getDefaultOptions());
     540        I2PSocketOptions opts = getDefaultOptions();
     541        opts.setPort(port);
     542        return createI2PSocket(dest, opts);
    526543    }
    527544
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java

    r0f5a0b6 r8f7b31a  
    11package net.i2p.i2ptunnel;
    22
     3import java.io.IOException;
    34import java.net.Socket;
     5import java.net.UnknownHostException;
    46import java.util.ArrayList;
    57import java.util.List;
     8import java.util.Properties;
    69import java.util.StringTokenizer;
    710
    811import net.i2p.I2PException;
    912import net.i2p.client.streaming.I2PSocket;
     13import net.i2p.client.streaming.I2PSocketAddress;
    1014import net.i2p.data.Base32;
     15import net.i2p.data.DataHelper;
    1116import net.i2p.data.Destination;
    1217import net.i2p.i2ptunnel.irc.DCCClientManager;
     
    2934   
    3035    /** list of Destination objects that we point at */
    31     protected List<Destination> dests;
     36    private final List<I2PSocketAddress> _addrs;
    3237    private static final long DEFAULT_READ_TIMEOUT = 5*60*1000; // -1
    3338    protected long readTimeout = DEFAULT_READ_TIMEOUT;
     
    4247
    4348    /**
     49     * @param destinations peers we target, comma- or space-separated. Since 0.9.9, each dest may be appended with :port
    4450     * @throws IllegalArgumentException if the I2PTunnel does not contain
    4551     *                                  valid config to contact the router
     
    5864              "IRC Client on " + tunnel.listenHost + ':' + localPort + " #" + (++__clientId), tunnel, pkf);
    5965       
    60         StringTokenizer tok = new StringTokenizer(destinations, ", ");
    61         dests = new ArrayList(2);
    62         while (tok.hasMoreTokens()) {
    63             String destination = tok.nextToken();
    64             Destination destN = _context.namingService().lookup(destination);
    65             if (destN == null)
    66                 l.log("Could not resolve " + destination);
    67             else
    68                 dests.add(destN);
    69         }
    70 
    71         if (dests.isEmpty()) {
     66        _addrs = new ArrayList(4);
     67        buildAddresses(destinations);
     68
     69        if (_addrs.isEmpty()) {
    7270            l.log("No target destinations found");
    7371            notifyEvent("openClientResult", "error");
     
    7573            // Maybe figure out where to put a waitEventValue("openClientResult") ??
    7674            // In the meantime, let's do this the easy way
    77             // Note that b32 dests will often not be resolvable at instantiation time;
    78             // a delayed resolution system would be even better.
    7975
    8076            // Don't close() here, because it does a removeSession() and then
     
    9692    }
    9793   
     94    /** @since 0.9.9 moved from constructor */
     95    private void buildAddresses(String destinations) {
     96        if (destinations == null)
     97            return;
     98        StringTokenizer tok = new StringTokenizer(destinations, ", ");
     99        synchronized(_addrs) {
     100            _addrs.clear();
     101            while (tok.hasMoreTokens()) {
     102                String destination = tok.nextToken();
     103                try {
     104                    // Try to resolve here but only log if it doesn't.
     105                    // Note that b32 _addrs will often not be resolvable at instantiation time.
     106                    // We will try again to resolve in clientConnectionRun()
     107                    I2PSocketAddress addr = new I2PSocketAddress(destination);
     108                    _addrs.add(addr);
     109                    if (addr.isUnresolved()) {
     110                        String name = addr.getHostName();
     111                        if (name.length() == 60 && name.endsWith(".b32.i2p"))
     112                            l.log("Warning - Could not resolve " + name +
     113                                  ", perhaps it is not up, will retry when connecting.");
     114                        else
     115                            l.log("Warning - Could not resolve " + name +
     116                                  ", you must add it to your address book for it to work.");
     117                    }
     118                } catch (IllegalArgumentException iae) {
     119                     l.log("Bad destination " + destination + " - " + iae);
     120                }
     121            }
     122        }
     123    }
     124
    98125    protected void clientConnectionRun(Socket s) {
    99126        if (_log.shouldLog(Log.INFO))
    100127            _log.info("New connection local addr is: " + s.getLocalAddress() +
    101128                      " from: " + s.getInetAddress());
    102         Destination clientDest = pickDestination();
    103129        I2PSocket i2ps = null;
     130        I2PSocketAddress addr = pickDestination();
    104131        try {
    105             i2ps = createI2PSocket(clientDest);
     132            if (addr == null)
     133                throw new UnknownHostException("No valid destination configured");
     134            Destination clientDest = addr.getAddress();
     135            if (clientDest == null)
     136                throw new UnknownHostException("Could not resolve " + addr.getHostName());
     137            int port = addr.getPort();
     138            i2ps = createI2PSocket(clientDest, port);
    106139            i2ps.setReadTimeout(readTimeout);
    107140            StringBuffer expectedPong = new StringBuffer();
     
    111144            Thread out = new I2PAppThread(new IrcOutboundFilter(s,i2ps, expectedPong, _log, dcc), "IRC Client " + __clientId + " out", true);
    112145            out.start();
    113         } catch (I2PException ex) {
    114             if (_log.shouldLog(Log.ERROR))
    115                 _log.error("Error connecting", ex);
     146        } catch (Exception ex) {
     147            // generally NoRouteToHostException
     148            if (_log.shouldLog(Log.WARN))
     149                _log.warn("Error connecting", ex);
    116150            //l.log("Error connecting: " + ex.getMessage());
     151            try {
     152                // Send a response so the user doesn't just see a disconnect
     153                // and blame his router or the network.
     154                String name = addr != null ? addr.getHostName() : "undefined";
     155                String msg = ":" + name + " 499 you :" + ex + "\r\n";
     156                s.getOutputStream().write(DataHelper.getUTF8(msg));
     157            } catch (IOException ioe) {}
    117158            closeSocket(s);
    118159            if (i2ps != null) {
     
    121162                }
    122163            }
    123         } catch (Exception ex) {
    124             // generally NoRouteToHostException
    125             if (_log.shouldLog(Log.WARN))
    126                 _log.warn("Error connecting", ex);
    127             //l.log("Error connecting: " + ex.getMessage());
    128             closeSocket(s);
    129             if (i2ps != null) {
    130                 synchronized (sockLock) {
    131                     mySockets.remove(sockLock);
    132                 }
    133             }
    134164        }
    135165
    136166    }
    137167   
    138     private final Destination pickDestination() {
    139         int size = dests.size();
    140         if (size <= 0) {
    141             if (_log.shouldLog(Log.ERROR))
    142                 _log.error("No client targets?!");
    143             return null;
    144         }
    145         if (size == 1) // skip the rand in the most common case
    146             return dests.get(0);
    147         int index = _context.random().nextInt(size);
    148         return dests.get(index);
     168    private final I2PSocketAddress pickDestination() {
     169        synchronized(_addrs) {
     170            int size = _addrs.size();
     171            if (size <= 0) {
     172                if (_log.shouldLog(Log.ERROR))
     173                    _log.error("No client targets?!");
     174                return null;
     175            }
     176            if (size == 1) // skip the rand in the most common case
     177                return _addrs.get(0);
     178            int index = _context.random().nextInt(size);
     179            return _addrs.get(index);
     180        }
     181    }
     182
     183    /**
     184     *  Update the dests then call super.
     185     *
     186     *  @since 0.9.9
     187     */
     188    @Override
     189    public void optionsUpdated(I2PTunnel tunnel) {
     190        if (getTunnel() != tunnel)
     191            return;
     192        Properties props = tunnel.getClientOptions();
     193        // see TunnelController.setSessionOptions()
     194        String targets = props.getProperty("targetDestination");
     195        buildAddresses(targets);
     196        super.optionsUpdated(tunnel);
    149197    }
    150198
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java

    r0f5a0b6 r8f7b31a  
    425425
    426426    private void setSessionOptions() {
    427         _tunnel.setClientOptions(getClientOptionProps());
     427        Properties opts = getClientOptionProps();
     428        // targetDestination does NOT start with "option.", but we still want
     429        // to allow a change on the fly, so we pass it through this way,
     430        // as a "spoofed" option. Since 0.9.9.
     431        String target = getTargetDestination();
     432        if (target != null)
     433            opts.setProperty("targetDestination", target);
     434        _tunnel.setClientOptions(opts);
    428435    }
    429436   
  • apps/i2ptunnel/jsp/editClient.jsp

    r0f5a0b6 r8f7b31a  
    166166                </label>
    167167                <input type="text" size="30" id="targetDestination" name="targetDestination" title="Destination of the Tunnel" value="<%=editBean.getClientDestination(curTunnel)%>" class="freetext" />               
    168                 <span class="comment">(<%=intl._("name or destination")%>; <%=intl._("b32 not recommended")%>)</span>
     168                <span class="comment">(<%=intl._("name, name:port, or destination")%>
     169                     <% if ("streamrclient".equals(tunnelType)) { /* deferred resolution unimplemented in streamr client */ %>
     170                         - <%=intl._("b32 not recommended")%>
     171                     <% } %> )
     172                </span>
    169173            </div>
    170174         <% } %>
  • apps/i2ptunnel/jsp/wizard.jsp

    r0f5a0b6 r8f7b31a  
    269269                </label>
    270270                <input type="text" size="30" id="targetDestination" name="targetDestination" title="Destination of the Tunnel" value="<%=(!"null".equals(request.getParameter("targetDestination")) ? request.getParameter("targetDestination") : "" ) %>" class="freetext" />
    271                 <span class="comment">(<%=intl._("name or destination")%>; <%=intl._("b32 not recommended")%>)</span>
     271                <span class="comment">(<%=intl._("name, name:port, or destination")%>
     272                     <% if ("streamrclient".equals(tunnelType)) { /* deferred resolution unimplemented in streamr client */ %>
     273                         - <%=intl._("b32 not recommended")%>
     274                     <% } %> )
     275                </span>
    272276            </div><%
    273277                } else {
  • apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketAddress.java

    r0f5a0b6 r8f7b31a  
    1212 *  See InetSocketAddress for javadocs.
    1313 *
    14  *  Warning, this interface and implementation is preliminary and subject to change without notice.
    15  *
    1614 *  @since 0.9.1
    1715 */
     
    1917
    2018    private final int _port;
    21     private final Destination _dest;
     19    private Destination _dest;
    2220    private final String _host;
    2321
    24     // no constructor for port-only "wildcard" address
     22    /**
     23     *  Convenience constructor that parses host:port.
     24     *
     25     *  Does a naming service lookup to resolve the dest.
     26     *  May take several seconds for b32.
     27     *  @param host hostname or b64 dest or b32, may have :port appended
     28     *  @throws IllegalArgumentException for port < 0 or port > 65535 or invalid port
     29     *  @since 0.9.9
     30     */
     31    public I2PSocketAddress(String host) {
     32        int port = 0;
     33        int colon = host.indexOf(":");
     34        if (colon > 0) {
     35            try {
     36                port = Integer.parseInt(host.substring(colon + 1));
     37                host = host.substring(0, colon);
     38                if (port < 0 || port > 65535)
     39                    throw new IllegalArgumentException("bad port " + port);
     40            } catch (IndexOutOfBoundsException ioobe) {
     41                throw new IllegalArgumentException("bad port " + host);
     42            } catch (NumberFormatException nfe) {
     43                throw new IllegalArgumentException("bad port " + host);
     44            }
     45        }
     46        _port = port;
     47        _dest = I2PAppContext.getGlobalContext().namingService().lookup(host);
     48        _host = host;
     49    }
    2550
    2651    /**
    2752     *  Does not do a reverse lookup. Host will be null.
     53     *  @throws IllegalArgumentException for port < 0 or port > 65535
    2854     */
    2955    public I2PSocketAddress(Destination dest, int port) {
     56        if (dest == null)
     57            throw new NullPointerException();
     58        if (port < 0 || port > 65535)
     59            throw new IllegalArgumentException("bad port " + port);
    3060        _port = port;
    3161        _dest = dest;
     
    3666     *  Does a naming service lookup to resolve the dest.
    3767     *  May take several seconds for b32.
     68     *  @throws IllegalArgumentException for port < 0 or port > 65535
    3869     */
    3970    public I2PSocketAddress(String host, int port) {
     71        if (port < 0 || port > 65535)
     72            throw new IllegalArgumentException("bad port " + port);
    4073        _port = port;
    4174        _dest = I2PAppContext.getGlobalContext().namingService().lookup(host);
     
    4376    }
    4477
     78    /**
     79     *  @throws IllegalArgumentException for port < 0 or port > 65535
     80     */
    4581    public static I2PSocketAddress createUnresolved(String host, int port) {
    4682        return new I2PSocketAddress(port, host);
     
    4985    /** unresolved */
    5086    private I2PSocketAddress(int port, String host) {
     87        if (port < 0 || port > 65535)
     88            throw new IllegalArgumentException("bad port " + port);
    5189        _port = port;
    5290        _dest = null;
     
    5896    }
    5997
    60     public Destination getAddress() {
     98    /**
     99     *  Does a naming service lookup to resolve the dest if this was created unresolved
     100     *  or if the resolution failed in the constructor.
     101     *  If unresolved, this may take several seconds for b32.
     102     */
     103    public synchronized Destination getAddress() {
     104        if (_dest == null)
     105            _dest = I2PAppContext.getGlobalContext().namingService().lookup(_host);
    61106        return _dest;
    62107    }
  • history.txt

    r0f5a0b6 r8f7b31a  
    112013-10-23 zzz
     2 * I2PTunnel standard and IRC clients:
     3   - Allow host:port targets; set defaults in i2ptunnel.config (ticket #1066)
     4   - Don't fail start if hostname is unresolvable; retry at connect time (ticket #946)
     5   - Output IRC message on connect exception
     6   - Update target list on-the-fly when configuration changes
    27 * NetDB:
    38   - Increase RI publish interval to reduce the connection load on ffs
  • installer/resources/i2ptunnel.config

    r0f5a0b6 r8f7b31a  
    3636tunnel.1.interface=127.0.0.1
    3737tunnel.1.listenPort=6668
    38 tunnel.1.targetDestination=irc.postman.i2p,irc.freshcoffee.i2p,irc.echelon.i2p
     38tunnel.1.targetDestination=irc.postman.i2p:6667,irc.freshcoffee.i2p:6667,irc.echelon.i2p:6667
    3939tunnel.1.i2cpHost=127.0.0.1
    4040tunnel.1.i2cpPort=7654
     
    6464tunnel.2.interface=127.0.0.1
    6565tunnel.2.listenPort=8998
    66 tunnel.2.targetDestination=mtn.i2p2.i2p
     66tunnel.2.targetDestination=mtn.i2p2.i2p:4691
    6767tunnel.2.i2cpHost=127.0.0.1
    6868tunnel.2.i2cpPort=7654
     
    114114tunnel.4.option.outbound.lengthVariance=0
    115115tunnel.4.startOnLoad=true
    116 tunnel.4.targetDestination=smtp.postman.i2p
     116tunnel.4.targetDestination=smtp.postman.i2p:25
    117117tunnel.4.type=client
    118118tunnel.4.sharedClient=true
     
    136136tunnel.5.option.outbound.lengthVariance=0
    137137tunnel.5.startOnLoad=true
    138 tunnel.5.targetDestination=pop.postman.i2p
     138tunnel.5.targetDestination=pop.postman.i2p:110
    139139tunnel.5.type=client
    140140tunnel.5.sharedClient=true
  • router/java/src/net/i2p/router/RouterVersion.java

    r0f5a0b6 r8f7b31a  
    1919    public final static String ID = "Monotone";
    2020    public final static String VERSION = CoreVersion.VERSION;
    21     public final static long BUILD = 7;
     21    public final static long BUILD = 8;
    2222
    2323    /** for example "-test" */
Note: See TracChangeset for help on using the changeset viewer.