Changeset cc97a19


Ignore:
Timestamp:
Dec 21, 2013 12:21:48 AM (6 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
5219791
Parents:
38c02b44
Message:

I2CP:

  • Add support for hostname lookups over I2CP with new HostLookup? and HostReply? messages.
  • Move username / password from CreateSession? to GetDate? for early authentication; this is an incompatible chage. Outside router context with authentication enabled, new clients will not work with old routers. Early authentication is not yet enforced, enable with i2cp.strictAuth=true. Will change default to true in a later release.
  • Block all actions before authentication.
  • Better disconnect messages to clients for diagnostics
  • Improve lookup command, add auth command in i2ptunnel CLI for testing
  • Don't start ClientWriterRunner? thread in constructor
  • Don't flush in ClientWriterRunner? unless necessary
  • Send GetDate? even in SimpleSession? outside of RouterContext?
  • Improve SetDate? wait logic to reduce locks and break out when Disconnect received
  • Add Disconnect handler to SimpleSession?
  • I2Ping cleanups
  • Javadocs
Files:
3 added
15 edited

Legend:

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

    r38c02b44 rcc97a19  
    4747import java.util.List;
    4848import java.util.Locale;
     49import java.util.Map;
    4950import java.util.Properties;
    5051import java.util.Set;
     
    5859import net.i2p.client.I2PClientFactory;
    5960import net.i2p.client.I2PSession;
     61import net.i2p.client.I2PSessionException;
     62import net.i2p.client.I2PSimpleClient;
    6063import net.i2p.client.naming.NamingService;
    6164import net.i2p.data.Base64;
     
    6972import net.i2p.util.EventDispatcherImpl;
    7073import net.i2p.util.Log;
     74import net.i2p.util.OrderedProperties;
    7175
    7276/**
     
    8892    public boolean ownDest = false;
    8993
    90     /** the I2CP port */
     94    /** the I2CP port, non-null */
    9195    public String port = System.getProperty(I2PClient.PROP_TCP_PORT, "7654");
    92     /** the I2CP host */
     96    /** the I2CP host, non-null */
    9397    public String host = System.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
    9498    /** the listen-on host. Sadly the listen-on port does not have a field. */
     
    169173                BufferedReader r = new BufferedReader(new InputStreamReader(System.in));
    170174                while (true) {
    171                     System.out.print("I2PTunnel>");
     175                    System.out.print("I2PTunnel> ");
    172176                    String cmd = r.readLine();
    173177                    if (cmd == null) break;
     
    294298        } else if (cmdname.equals("owndest")) {
    295299            runOwnDest(args, l);
     300        } else if (cmdname.equals("auth")) {
     301            runAuth(args, l);
    296302        } else {
    297303            l.log("Unknown command [" + cmdname + "]");
     
    309315        l.log("Command list:");
    310316        // alphabetical please...
    311         l.log("client <port> <pubkey>[,<pubkey,...]|file:<pubkeyfile> [<sharedClient>]");
    312         l.log("clientoptions[ key=value]*");
    313         l.log("close [forced] <jobnumber>|all");
    314         l.log("config <i2phost> <i2pport>");
    315         l.log("connectclient <port> [<sharedClient>] [<proxy>]");
    316         l.log("genkeys <privkeyfile> [<pubkeyfile>]");
    317         l.log("gentextkeys");
    318         l.log("httpbidirserver <host> <port> <proxyport> <spoofedhost> <privkeyfile>");
    319         l.log("httpclient <port> [<sharedClient>] [<proxy>]");
    320         l.log("httpserver <host> <port> <spoofedhost> <privkeyfile>");
    321         l.log("ircclient <port> <pubkey>[,<pubkey,...]|file:<pubkeyfile> [<sharedClient>]");
    322         l.log("list");
    323         l.log("listen_on <ip>");
    324         l.log("lookup <name>");
    325         l.log("owndest yes|no");
    326         l.log("ping <args>");
    327         l.log("quit");
    328         l.log("read_timeout <msecs>");
    329         l.log("run <commandfile>");
    330         l.log("server <host> <port> <privkeyfile>");
    331         l.log("textserver <host> <port> <privkey>");
     317        l.log("  auth <username> <password>");
     318        l.log("  client <port> <pubkey>[,<pubkey,...]|file:<pubkeyfile> [<sharedClient>]");
     319        l.log("  clientoptions [key=value ]*");
     320        l.log("  close [forced] <jobnumber>|all");
     321        l.log("  config [-s] <i2phost> <i2pport>");
     322        l.log("  connectclient <port> [<sharedClient>] [<proxy>]");
     323        l.log("  genkeys <privkeyfile> [<pubkeyfile>]");
     324        l.log("  gentextkeys");
     325        l.log("  httpbidirserver <host> <port> <proxyport> <spoofedhost> <privkeyfile>");
     326        l.log("  httpclient <port> [<sharedClient>] [<proxy>]");
     327        l.log("  httpserver <host> <port> <spoofedhost> <privkeyfile>");
     328        l.log("  ircclient <port> <pubkey>[,<pubkey,...]|file:<pubkeyfile> [<sharedClient>]");
     329        l.log("  list");
     330        l.log("  listen_on <ip>");
     331        l.log("  lookup <name>");
     332        l.log("  owndest yes|no");
     333        l.log("  ping <args>");
     334        l.log("  quit");
     335        l.log("  read_timeout <msecs>");
     336        l.log("  run <commandfile>");
     337        l.log("  server <host> <port> <privkeyfile>");
     338        l.log("  textserver <host> <port> <privkey>");
    332339    }
    333340   
     
    346353     */
    347354    public void runClientOptions(String args[], Logging l) {
    348         _clientOptions.clear();
    349         if (args != null) {
    350             for (int i = 0; i < args.length; i++) {
     355        if (args != null && args.length > 0) {
     356            int i = 0;
     357            if (args[0].equals("-a")) {
     358                i++;
     359            } else if (args[0].equals("-c")) {
     360                _clientOptions.clear();
     361                l.log("Client options cleared");
     362                return;
     363            } else if (args[0].equals("-x")) {
     364                i++;
     365                for ( ; i < args.length; i++) {
     366                     if (_clientOptions.remove(args[i]) != null)
     367                        l.log("Removed " + args[i]);
     368                }
     369                return;
     370            } else {
     371                _clientOptions.clear();
     372            }
     373            for ( ; i < args.length; i++) {
    351374                int index = args[i].indexOf('=');
    352375                if (index <= 0) continue;
     
    354377                String val = args[i].substring(index+1);
    355378                _clientOptions.setProperty(key, val);
     379            }
     380        } else {
     381            l.log("Usage:");
     382            l.log("  clientoptions [key=value ]*     // sets current options");
     383            l.log("  clientoptions -a [key=value ]*  // adds to current options");
     384            l.log("  clientoptions -c                // clears current options");
     385            l.log("  clientoptions -x [key ]*        // removes listed options");
     386            l.log("Current options:");
     387            Properties p = new OrderedProperties();
     388            p.putAll(_clientOptions);
     389            for (Map.Entry<Object, Object> e : p.entrySet()) {
     390                l.log("  [" + e.getKey() + "] = [" + e.getValue() + ']');
    356391            }
    357392        }
     
    11481183     */
    11491184    private void runConfig(String args[], Logging l) {
     1185        if (args.length >= 2) {
     1186            int i = 0;
     1187            if (args[0].equals("-s")) {
     1188                _clientOptions.setProperty("i2cp.SSL", "true");
     1189                i++;
     1190            } else {
     1191                _clientOptions.remove("i2cp.SSL");
     1192            }
     1193            host = args[i++];
     1194            listenHost = host;
     1195            port = args[i];
     1196            notifyEvent("configResult", "ok");
     1197        } else {
     1198            l.log("Usage:");
     1199            l.log("  config [-s] <i2phost> <i2pport>");
     1200            l.log("  sets the connection to the i2p router.");
     1201            l.log("Current setting:");
     1202            boolean ssl = Boolean.parseBoolean(_clientOptions.getProperty("i2cp.SSL"));
     1203            l.log("  " + host + ' ' + port + (ssl ? " SSL" : ""));
     1204            notifyEvent("configResult", "error");
     1205        }
     1206    }
     1207
     1208    /**
     1209     * Specify the i2cp username and password
     1210     *
     1211     * @param args {username, password}
     1212     * @param l logger to receive events and output
     1213     * @since 0.9.10
     1214     */
     1215    private void runAuth(String args[], Logging l) {
    11501216        if (args.length == 2) {
    1151             host = args[0];
    1152             listenHost = host;
    1153             port = args[1];
    1154             notifyEvent("configResult", "ok");
    1155         } else {
    1156             l.log("config <i2phost> <i2pport>");
    1157             l.log("  sets the connection to the i2p router.");
    1158             notifyEvent("configResult", "error");
     1217            _clientOptions.setProperty("i2cp.username", args[0]);
     1218            _clientOptions.setProperty("i2cp.password", args[1]);
     1219        } else {
     1220            l.log("Usage:");
     1221            l.log("  auth <username> <password>");
     1222            l.log("  Sets the i2cp credentials");
    11591223        }
    11601224    }
     
    14161480        } else {
    14171481            try {
    1418                 Destination dest = destFromName(args[0]);
     1482                boolean ssl = Boolean.parseBoolean(_clientOptions.getProperty("i2cp.SSL"));
     1483                String user = _clientOptions.getProperty("i2cp.username");
     1484                String pw = _clientOptions.getProperty("i2cp.password");
     1485                Destination dest = destFromName(args[0], host, port, ssl, user, pw);
    14191486                if (dest == null) {
    1420                     l.log("Unknown host");
     1487                    l.log("Unknown host: " + args[0]);
    14211488                    notifyEvent("lookupResult", "unkown host");
    14221489                } else {
     
    14251492                }
    14261493            } catch (DataFormatException dfe) {
    1427                 l.log("Unknown or invalid host");
     1494                l.log("Unknown or invalid host: " + args[0]);
    14281495                notifyEvent("lookupResult", "invalid host");
    14291496            }
     
    16001667     */
    16011668    public static Destination destFromName(String name) throws DataFormatException {
     1669        return destFromName(name, null, null, false, null, null);
     1670    }
     1671
     1672    /**
     1673     *  @param i2cpHost may be null
     1674     *  @param i2cpPort may be null
     1675     *  @param user may be null
     1676     *  @param pw may be null
     1677     *  @since 0.9.10
     1678     */
     1679    private static Destination destFromName(String name, String i2cpHost,
     1680                                            String i2cpPort, boolean isSSL,
     1681                                            String user, String pw) throws DataFormatException {
    16021682
    16031683        if ((name == null) || (name.trim().length() <= 0)) throw new DataFormatException("Empty destination provided");
     
    16431723        } else {
    16441724            // ask naming service
     1725            name = name.trim();
    16451726            NamingService inst = ctx.namingService();
    1646             return inst.lookup(name);
     1727            boolean b32 = name.length() == 60 && name.toLowerCase(Locale.US).endsWith(".b32.i2p");
     1728            Destination d = null;
     1729            if (ctx.isRouterContext() || !b32) {
     1730                // Local lookup.
     1731                // Even though we could do b32 outside router ctx here,
     1732                // we do it below instead so we can set the host and port,
     1733                // which we can't do with lookup()
     1734                d = inst.lookup(name);
     1735                if (d != null || ctx.isRouterContext())
     1736                    return d;
     1737            }
     1738            // Outside router context only,
     1739            // try simple session to ask the router.
     1740            I2PClient client = new I2PSimpleClient();
     1741            Properties opts = new Properties();
     1742            if (i2cpHost != null)
     1743                opts.put(I2PClient.PROP_TCP_HOST, i2cpHost);
     1744            if (i2cpPort != null)
     1745                opts.put(I2PClient.PROP_TCP_PORT, i2cpPort);
     1746            opts.put("i2cp.SSL", Boolean.toString(isSSL));
     1747            if (user != null)
     1748                opts.put("i2cp.username", user);
     1749            if (pw != null)
     1750                opts.put("i2cp.password", pw);
     1751            I2PSession session = null;
     1752            try {
     1753                session = client.createSession(null, opts);
     1754                session.connect();
     1755                d = session.lookupDest(name);
     1756            } catch (I2PSessionException ise) {
     1757                if (log.shouldLog(Log.WARN))
     1758                    log.warn("Lookup via router failed", ise);
     1759            } finally {
     1760                if (session != null) {
     1761                    try { session.destroySession(); } catch (I2PSessionException ise) {}
     1762                }
     1763            }
     1764            return d;
    16471765        }
    16481766    }
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2Ping.java

    r38c02b44 rcc97a19  
    1919
    2020public class I2Ping extends I2PTunnelTask implements Runnable {
    21     private final static Log _log = new Log(I2Ping.class);
     21    private final Log _log = new Log(I2Ping.class);
    2222
    2323    private int PING_COUNT = 3;
     
    2929    private int MAX_SIMUL_PINGS = 10; // not really final...
    3030
    31     private boolean countPing = false;
     31    private boolean countPing;
    3232    private boolean reportTimes = true;
    3333
    34     private I2PSocketManager sockMgr;
    35     private Logging l;
    36     private boolean finished = false;
    37     private String command;
     34    private final I2PSocketManager sockMgr;
     35    private final Logging l;
     36    private boolean finished;
     37    private final String command;
    3838    private long timeout = PING_TIMEOUT;
    3939
    4040    private final Object simulLock = new Object();
    41     private int simulPings = 0;
    42     private long lastPingTime = 0;
    43 
    44     private final Object lock = new Object(), slock = new Object();
     41    private int simulPings;
     42    private long lastPingTime;
     43
     44    private final Object lock = new Object();
    4545
    4646    //public I2Ping(String cmd, Logging l,
     
    5353        this.l = l;
    5454        command = cmd;
    55         synchronized (slock) {
    56             if (ownDest) {
    57                 sockMgr = I2PTunnelClient.buildSocketManager(tunnel);
    58             } else {
    59                 sockMgr = I2PTunnelClient.getSocketManager(tunnel);
    60             }
     55        if (ownDest) {
     56            sockMgr = I2PTunnelClient.buildSocketManager(tunnel);
     57        } else {
     58            sockMgr = I2PTunnelClient.getSocketManager(tunnel);
    6159        }
    6260        Thread t = new I2PAppThread(this);
     
    188186
    189187    public class PingHandler extends I2PAppThread {
    190         private String destination;
     188        private final String destination;
    191189
    192190        public PingHandler(String dest) {
  • core/java/src/net/i2p/client/ClientWriterRunner.java

    r38c02b44 rcc97a19  
    99import java.util.concurrent.atomic.AtomicLong;
    1010
     11import net.i2p.I2PAppContext;
    1112import net.i2p.data.i2cp.I2CPMessage;
    1213import net.i2p.data.i2cp.I2CPMessageException;
    1314import net.i2p.internal.PoisonI2CPMessage;
    1415import net.i2p.util.I2PAppThread;
     16import net.i2p.util.Log;
    1517
    1618/**
     
    2628    private final BlockingQueue<I2CPMessage> _messagesToWrite;
    2729    private static final AtomicLong __Id = new AtomicLong();
     30    //private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(ClientWriterRunner.class);
    2831
    2932    private static final int MAX_QUEUE_SIZE = 32;
    3033    private static final long MAX_SEND_WAIT = 10*1000;
    3134   
    32     /** starts the thread too */
     35    /**
     36     *  As of 0.9.10 does not start the thread, caller must call startWriting()
     37     */
    3338    public ClientWriterRunner(OutputStream out, I2PSessionImpl session) {
    3439        _out = new BufferedOutputStream(out);
    3540        _session = session;
    3641        _messagesToWrite = new LinkedBlockingQueue<I2CPMessage>(MAX_QUEUE_SIZE);
     42    }
     43
     44    /**
     45     *  @since 0.9.10
     46     */
     47    public void startWriting() {
    3748        Thread t = new I2PAppThread(this, "I2CP Client Writer " + __Id.incrementAndGet(), true);
    3849        t.start();
     
    7788            try {
    7889                msg.writeMessage(_out);
    79                 _out.flush();
     90                if (_messagesToWrite.isEmpty())
     91                    _out.flush();
    8092            } catch (I2CPMessageException ime) {
    8193                _session.propogateError("Error writing out the message", ime);
  • core/java/src/net/i2p/client/I2PClientMessageHandlerMap.java

    r38c02b44 rcc97a19  
    1414import net.i2p.data.i2cp.DestReplyMessage;
    1515import net.i2p.data.i2cp.DisconnectMessage;
     16import net.i2p.data.i2cp.HostReplyMessage;
    1617import net.i2p.data.i2cp.MessagePayloadMessage;
    1718import net.i2p.data.i2cp.MessageStatusMessage;
     
    4142        highest = Math.max(highest, SetDateMessage.MESSAGE_TYPE);
    4243        highest = Math.max(highest, DestReplyMessage.MESSAGE_TYPE);
     44        highest = Math.max(highest, HostReplyMessage.MESSAGE_TYPE);
    4345        highest = Math.max(highest, BandwidthLimitsMessage.MESSAGE_TYPE);
    4446        highest = Math.max(highest, RequestVariableLeaseSetMessage.MESSAGE_TYPE);
     
    5456        _handlers[BandwidthLimitsMessage.MESSAGE_TYPE] = new BWLimitsMessageHandler(context);
    5557        _handlers[RequestVariableLeaseSetMessage.MESSAGE_TYPE] = new RequestVariableLeaseSetMessageHandler(context);
     58        _handlers[HostReplyMessage.MESSAGE_TYPE] = new HostReplyMessageHandler(context);
    5659    }
    5760
  • core/java/src/net/i2p/client/I2PSession.java

    r38c02b44 rcc97a19  
    215215
    216216    /**
     217     *  Lookup a Destination by Hash.
    217218     *  Blocking.
    218219     *  @param maxWait ms
     
    221222     */
    222223    public Destination lookupDest(Hash h, long maxWait) throws I2PSessionException;
     224
     225    /**
     226     *  Ask the router to lookup a Destination by host name.
     227     *  Blocking. Waits a max of 10 seconds by default.
     228     *
     229     *  This only makes sense for a b32 hostname, OR outside router context.
     230     *  Inside router context, just query the naming service.
     231     *  Outside router context, this does NOT query the context naming service.
     232     *  Do that first if you expect a local addressbook.
     233     *
     234     *  This will log a warning for non-b32 in router context.
     235     *
     236     *  Suggested implementation:
     237     *
     238     *<pre>
     239     *  if (name.length() == 60 && name.toLowerCase(Locale.US).endsWith(".b32.i2p")) {
     240     *      if (session != null)
     241     *          return session.lookup(Hash.create(Base32.decode(name.toLowerCase(Locale.US).substring(0, 52))));
     242     *      else
     243     *          return ctx.namingService().lookup(name); // simple session for xxx.b32.i2p handled by naming service (optional if you need lookup w/o an existing session)
     244     *  } else if (ctx.isRouterContext()) {
     245     *      return ctx.namingService().lookup(name); // hostname from router's naming service
     246     *  } else {
     247     *      Destination d = ctx.namingService().lookup(name); // local naming svc, optional
     248     *      if (d != null)
     249     *          return d;
     250     *      if (session != null)
     251     *          return session.lookup(name);
     252     *      // simple session (optional if you need lookup w/o an existing session)
     253     *      Destination rv = null;
     254     *      I2PClient client = new I2PSimpleClient();
     255     *      Properties opts = new Properties();
     256     *      opts.put(I2PClient.PROP_TCP_HOST, host);
     257     *      opts.put(I2PClient.PROP_TCP_PORT, port);
     258     *      I2PSession session = null;
     259     *      try {
     260     *          session = client.createSession(null, opts);
     261     *          session.connect();
     262     *          rv = session.lookupDest(name);
     263     *      } finally {
     264     *          if (session != null)
     265     *              session.destroySession();
     266     *      }
     267     *      return rv;
     268     *  }
     269     *</pre>
     270     *
     271     *  Requires router side to be 0.9.10 or higher. If the router is older,
     272     *  this will return null immediately.
     273     *
     274     *  @since 0.9.10
     275     */
     276    public Destination lookupDest(String name) throws I2PSessionException;
     277
     278    /**
     279     *  Ask the router to lookup a Destination by host name.
     280     *  Blocking. See above for details.
     281     *  @param maxWait ms
     282     *  @since 0.9.10
     283     *  @return null on failure
     284     */
     285    public Destination lookupDest(String name, long maxWait) throws I2PSessionException;
    223286
    224287    /**
  • core/java/src/net/i2p/client/I2PSessionImpl.java

    r38c02b44 rcc97a19  
    2020import java.util.ArrayList;
    2121import java.util.List;
     22import java.util.Locale;
    2223import java.util.Map;
    2324import java.util.Properties;
    2425import java.util.concurrent.ConcurrentHashMap;
    2526import java.util.concurrent.LinkedBlockingQueue;
     27import java.util.concurrent.atomic.AtomicInteger;
    2628
    2729import net.i2p.CoreVersion;
    2830import net.i2p.I2PAppContext;
     31import net.i2p.data.Base32;
    2932import net.i2p.data.DataFormatException;
    3033import net.i2p.data.Destination;
     
    3639import net.i2p.data.i2cp.GetBandwidthLimitsMessage;
    3740import net.i2p.data.i2cp.GetDateMessage;
     41import net.i2p.data.i2cp.HostLookupMessage;
    3842import net.i2p.data.i2cp.I2CPMessage;
    3943import net.i2p.data.i2cp.I2CPMessageReader;
     
    4751import net.i2p.util.LHMCache;
    4852import net.i2p.util.Log;
     53import net.i2p.util.OrderedProperties;
    4954import net.i2p.util.SimpleTimer;
    5055import net.i2p.util.VersionComparator;
     
    99104    /** hashes of lookups we are waiting for */
    100105    protected final LinkedBlockingQueue<LookupWaiter> _pendingLookups = new LinkedBlockingQueue<LookupWaiter>();
     106    private final AtomicInteger _lookupID = new AtomicInteger();
    101107    protected final Object _bwReceivedLock = new Object();
    102108    protected volatile int[] _bwLimits;
     
    104110    protected final I2PClientMessageHandlerMap _handlerMap;
    105111   
    106     /** used to seperate things out so we can get rid of singletons */
     112    /** used to separate things out so we can get rid of singletons */
    107113    protected final I2PAppContext _context;
    108114
     
    115121    protected enum State {
    116122        OPENING,
     123        /** @since 0.9.10 */
     124        GOTDATE,
    117125        OPEN,
    118126        CLOSING,
     
    122130    private State _state = State.CLOSED;
    123131    protected final Object _stateLock = new Object();
    124 
    125     /** have we received the current date from the router yet? */
    126     private volatile boolean _dateReceived;
    127     /** lock that we wait upon, that the SetDateMessageHandler notifies */
    128     private final Object _dateReceivedLock = new Object();
    129132
    130133    /**
     
    140143    private final boolean _fastReceive;
    141144    private volatile boolean _routerSupportsFastReceive;
    142 
    143     /**
     145    private volatile boolean _routerSupportsHostLookup;
     146
     147    /**
     148     *  Since 0.9.10, key is either a Hash or a String
    144149     *  @since 0.8.9
    145150     */
    146     private static final Map<Hash, Destination> _lookupCache = new LHMCache<Hash, Destination>(16);
     151    private static final Map<Object, Destination> _lookupCache = new LHMCache<Object, Destination>(16);
     152    private static final String MIN_HOST_LOOKUP_VERSION = "0.9.10";
     153    private static final boolean TEST_LOOKUP = false;
    147154
    148155    /** SSL interface (only) @since 0.8.3 */
    149156    protected static final String PROP_ENABLE_SSL = "i2cp.SSL";
     157    protected static final String PROP_USER = "i2cp.username";
     158    protected static final String PROP_PW = "i2cp.password";
    150159
    151160    private static final long VERIFY_USAGE_TIME = 60*1000;
     
    160169                                     (routerVersion != null && routerVersion.length() > 0 &&
    161170                                      VersionComparator.comp(routerVersion, MIN_FAST_VERSION) >= 0);
    162         _dateReceived = true;
    163         synchronized (_dateReceivedLock) {
    164             _dateReceivedLock.notifyAll();
     171        _routerSupportsHostLookup = _context.isRouterContext() ||
     172                                    TEST_LOOKUP ||
     173                                     (routerVersion != null && routerVersion.length() > 0 &&
     174                                      VersionComparator.comp(routerVersion, MIN_HOST_LOOKUP_VERSION) >= 0);
     175        synchronized (_stateLock) {
     176            if (_state == State.OPENING) {
     177                _state = State.GOTDATE;
     178                _stateLock.notifyAll();
     179            }
    165180        }
    166181    }
     
    206221            _signingPrivateKey = null;
    207222        }
     223        _routerSupportsFastReceive = _context.isRouterContext();
     224        _routerSupportsHostLookup = _context.isRouterContext();
    208225    }
    209226
     
    240257        if ((!_context.isRouterContext()) &&
    241258            _context.getBooleanProperty("i2cp.auth") &&
    242             ((!opts.containsKey("i2cp.username")) || (!opts.containsKey("i2cp.password")))) {
    243             String configUser = _context.getProperty("i2cp.username");
    244             String configPW = _context.getProperty("i2cp.password");
     259            ((!opts.containsKey(PROP_USER)) || (!opts.containsKey(PROP_PW)))) {
     260            String configUser = _context.getProperty(PROP_USER);
     261            String configPW = _context.getProperty(PROP_PW);
    245262            if (configUser != null && configPW != null) {
    246                 options.setProperty("i2cp.username", configUser);
    247                 options.setProperty("i2cp.password", configPW);
     263                options.setProperty(PROP_USER, configUser);
     264                options.setProperty(PROP_PW, configPW);
    248265            }
    249266        }
     
    400417                        break;
    401418                    case OPENING:
     419                    case GOTDATE:
    402420                        wasOpening = true;
    403421                        try {
     
    456474                    out.flush();
    457475                    _writer = new ClientWriterRunner(out, this);
     476                    _writer.startWriting();
    458477                    InputStream in = new BufferedInputStream(_socket.getInputStream(), BUF_SIZE);
    459478                    _reader = new I2CPMessageReader(in, this);
     
    463482            _reader.startReading();
    464483            if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Before getDate");
    465             sendMessage(new GetDateMessage(CoreVersion.VERSION));
    466             if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "After getDate / begin waiting for a response");
    467             int waitcount = 0;
    468             while (!_dateReceived) {
    469                 if (waitcount++ > 30) {
    470                     throw new IOException("No handshake received from the router");
    471                 }
    472                 synchronized (_dateReceivedLock) {
    473                     // InterruptedException caught below
    474                     _dateReceivedLock.wait(1000);
    475                 }
    476             }
    477             if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "After received a SetDate response");
     484            Properties auth = null;
     485            if ((!_context.isRouterContext()) && _options.containsKey(PROP_USER) && _options.containsKey(PROP_PW)) {
     486                // Only supported by routers 0.9.10 or higher, but we don't know the version yet.       
     487                // Auth will also be sent in the SessionConfig.
     488                auth = new OrderedProperties();
     489                auth.setProperty(PROP_USER, _options.getProperty(PROP_USER));
     490                auth.setProperty(PROP_PW, _options.getProperty(PROP_PW));
     491            }
     492            sendMessage(new GetDateMessage(CoreVersion.VERSION, auth));
     493            waitForDate();
    478494
    479495            if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Before producer.connect()");
     
    482498
    483499            // wait until we have created a lease set
    484             waitcount = 0;
     500            int waitcount = 0;
    485501            while (_leaseSet == null) {
    486502                if (waitcount++ > 5*60) {
     
    526542
    527543    /**
     544     *  @since 0.9.10 moved from connect()
     545     */
     546    protected void waitForDate() throws InterruptedException, IOException {
     547            if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "After getDate / begin waiting for a response");
     548            int waitcount = 0;
     549            while (true) {
     550                if (waitcount++ > 30) {
     551                    throw new IOException("No handshake received from the router");
     552                }
     553                synchronized(_stateLock) {
     554                    if (_state == State.GOTDATE)
     555                        break;
     556                    if (_state != State.OPENING)
     557                        throw new IOException("Socket closed");
     558                    // InterruptedException caught by caller
     559                    _stateLock.wait(1000);
     560                }
     561            }
     562            if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "After received a SetDate response");
     563    }
     564
     565    /**
    528566     * Pull the unencrypted data from the message that we've already prefetched and
    529567     * notified the user that its available.
     
    891929     */
    892930    protected void disconnect() {
     931        State oldState;
    893932        synchronized(_stateLock) {
    894933            if (_state == State.CLOSING || _state == State.CLOSED)
    895934                return;
     935            oldState = _state;
    896936            changeState(State.CLOSING);
    897937        }
    898938        if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Disconnect() called", new Exception("Disconnect"));
    899         if (shouldReconnect()) {
     939        // don't try to reconnect if it failed before GETTDATE
     940        if (oldState != State.OPENING && shouldReconnect()) {
    900941            if (reconnect()) {
    901942                if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "I2CP reconnection successful");
     
    9641005    }
    9651006
    966     /** called by the message handler */
     1007    /**
     1008     *  Called by the message handler
     1009     *  on reception of DestReplyMessage
     1010     */
    9671011    void destReceived(Destination d) {
    9681012        Hash h = d.calculateHash();
     
    9711015        }
    9721016        for (LookupWaiter w : _pendingLookups) {
    973             if (w.hash.equals(h)) {
     1017            if (h.equals(w.hash)) {
    9741018                w.destination = d;
    9751019                synchronized (w) {
     
    9801024    }
    9811025
    982     /** called by the message handler */
     1026    /**
     1027     *  Called by the message handler
     1028     *  on reception of DestReplyMessage
     1029     */
    9831030    void destLookupFailed(Hash h) {
    9841031        for (LookupWaiter w : _pendingLookups) {
    985             if (w.hash.equals(h)) {
     1032            if (h.equals(w.hash)) {
     1033                synchronized (w) {
     1034                    w.notifyAll();
     1035                }
     1036            }
     1037        }
     1038    }
     1039
     1040    /**
     1041     *  Called by the message handler
     1042     *  on reception of HostReplyMessage
     1043     *  @since 0.9.10
     1044     */
     1045    void destReceived(long nonce, Destination d) {
     1046        // notify by hash
     1047        destReceived(d);
     1048        // notify by nonce
     1049        for (LookupWaiter w : _pendingLookups) {
     1050            if (nonce == w.nonce) {
     1051                w.destination = d;
     1052                if (w.name != null) {
     1053                    synchronized (_lookupCache) {
     1054                        _lookupCache.put(w.name, d);
     1055                    }
     1056                }
     1057                synchronized (w) {
     1058                    w.notifyAll();
     1059                }
     1060            }
     1061        }
     1062    }
     1063
     1064    /**
     1065     *  Called by the message handler
     1066     *  on reception of HostReplyMessage
     1067     *  @since 0.9.10
     1068     */
     1069    void destLookupFailed(long nonce) {
     1070        for (LookupWaiter w : _pendingLookups) {
     1071            if (nonce == w.nonce) {
    9861072                synchronized (w) {
    9871073                    w.notifyAll();
     
    10041090     */
    10051091    private static class LookupWaiter {
    1006         /** the request */
     1092        /** the request (Hash mode) */
    10071093        public final Hash hash;
     1094        /** the request (String mode) */
     1095        public final String name;
     1096        /** the request (nonce mode) */
     1097        public final long nonce;
    10081098        /** the reply */
    10091099        public volatile Destination destination;
    10101100
    10111101        public LookupWaiter(Hash h) {
     1102            this(h, -1);
     1103        }
     1104
     1105        /** @since 0.9.10 */
     1106        public LookupWaiter(Hash h, long nonce) {
    10121107            this.hash = h;
     1108            this.name = null;
     1109            this.nonce = nonce;
     1110        }
     1111
     1112        /** @since 0.9.10 */
     1113        public LookupWaiter(String name, long nonce) {
     1114            this.hash = null;
     1115            this.name = name;
     1116            this.nonce = nonce;
    10131117        }
    10141118    }
     
    10381142                return rv;
    10391143        }
    1040         if (isClosed())
     1144        if (isClosed()) {
     1145            if (_log.shouldLog(Log.INFO))
     1146                _log.info("Session closed, cannot lookup " + h);
    10411147            return null;
    1042         LookupWaiter waiter = new LookupWaiter(h);
     1148        }
     1149        LookupWaiter waiter;
     1150        long nonce;
     1151        if (_routerSupportsHostLookup) {
     1152            nonce = _lookupID.incrementAndGet() & 0x7fffffff;
     1153            waiter = new LookupWaiter(h, nonce);
     1154        } else {
     1155            nonce = 0; // won't be used
     1156            waiter = new LookupWaiter(h);
     1157        }
    10431158        _pendingLookups.offer(waiter);
    10441159        try {
    1045             sendMessage(new DestLookupMessage(h));
     1160            if (_routerSupportsHostLookup) {
     1161                if (_log.shouldLog(Log.INFO))
     1162                    _log.info("Sending HostLookup for " + h);
     1163                sendMessage(new HostLookupMessage(h, nonce, maxWait));
     1164            } else {
     1165                if (_log.shouldLog(Log.INFO))
     1166                    _log.info("Sending DestLookup for " + h);
     1167                sendMessage(new DestLookupMessage(h));
     1168            }
     1169            try {
     1170                synchronized (waiter) {
     1171                    waiter.wait(maxWait);
     1172                }
     1173            } catch (InterruptedException ie) {
     1174                throw new I2PSessionException("Interrupted", ie);
     1175            }
     1176        } finally {
     1177            _pendingLookups.remove(waiter);
     1178        }
     1179        return waiter.destination;
     1180    }
     1181
     1182    /**
     1183     *  Ask the router to lookup a Destination by host name.
     1184     *  Blocking. Waits a max of 10 seconds by default.
     1185     *
     1186     *  This only makes sense for a b32 hostname, OR outside router context.
     1187     *  Inside router context, just query the naming service.
     1188     *  Outside router context, this does NOT query the context naming service.
     1189     *  Do that first if you expect a local addressbook.
     1190     *
     1191     *  This will log a warning for non-b32 in router context.
     1192     *
     1193     *  See interface for suggested implementation.
     1194     *
     1195     *  Requires router side to be 0.9.10 or higher. If the router is older,
     1196     *  this will return null immediately.
     1197     *
     1198     *  @since 0.9.10
     1199     */
     1200    public Destination lookupDest(String name) throws I2PSessionException {
     1201        return lookupDest(name, 10*1000);
     1202    }
     1203
     1204    /**
     1205     *  Ask the router to lookup a Destination by host name.
     1206     *  Blocking. See above for details.
     1207     *  @param maxWait ms
     1208     *  @since 0.9.10
     1209     *  @return null on failure
     1210     */
     1211    public Destination lookupDest(String name, long maxWait) throws I2PSessionException {
     1212        synchronized (_lookupCache) {
     1213            Destination rv = _lookupCache.get(name);
     1214            if (rv != null)
     1215                return rv;
     1216        }
     1217        if (isClosed()) {
     1218            if (_log.shouldLog(Log.INFO))
     1219                _log.info("Session closed, cannot lookup " + name);
     1220            return null;
     1221        }
     1222        if (!_routerSupportsHostLookup) {
     1223            // do them a favor and convert to Hash lookup
     1224            if (name.length() == 60 && name.toLowerCase(Locale.US).endsWith(".b32.i2p"))
     1225                return lookupDest(Hash.create(Base32.decode(name.toLowerCase(Locale.US).substring(0, 52))), maxWait);
     1226            // else unsupported
     1227            if (_log.shouldLog(Log.WARN))
     1228                _log.warn("Router does not support HostLookup for " + name);
     1229            return null;
     1230        }
     1231        int nonce = _lookupID.incrementAndGet() & 0x7fffffff;
     1232        LookupWaiter waiter = new LookupWaiter(name, nonce);
     1233        _pendingLookups.offer(waiter);
     1234        try {
     1235            if (_log.shouldLog(Log.INFO))
     1236                _log.info("Sending HostLookup for " + name);
     1237            sendMessage(new HostLookupMessage(name, nonce, maxWait));
    10461238            try {
    10471239                synchronized (waiter) {
  • core/java/src/net/i2p/client/I2PSimpleSession.java

    r38c02b44 rcc97a19  
    1515import java.util.Properties;
    1616
     17import net.i2p.CoreVersion;
    1718import net.i2p.I2PAppContext;
    1819import net.i2p.data.i2cp.BandwidthLimitsMessage;
    1920import net.i2p.data.i2cp.DestReplyMessage;
     21import net.i2p.data.i2cp.DisconnectMessage;
     22import net.i2p.data.i2cp.GetDateMessage;
     23import net.i2p.data.i2cp.HostReplyMessage;
    2024import net.i2p.data.i2cp.I2CPMessageReader;
     25import net.i2p.data.i2cp.SetDateMessage;
    2126import net.i2p.internal.InternalClientManager;
    2227import net.i2p.internal.QueuedI2CPMessageReader;
    2328import net.i2p.util.I2PSSLSocketFactory;
     29import net.i2p.util.Log;
     30import net.i2p.util.OrderedProperties;
    2431
    2532/**
     
    6976                    _queue = mgr.connect();
    7077                    _reader = new QueuedI2CPMessageReader(_queue, this);
     78                    _reader.startReading();
    7179                } else {
    7280                    if (Boolean.parseBoolean(getOptions().getProperty(PROP_ENABLE_SSL))) {
     
    8694                    out.flush();
    8795                    _writer = new ClientWriterRunner(out, this);
     96                    _writer.startWriting();
    8897                    InputStream in = new BufferedInputStream(_socket.getInputStream(), BUF_SIZE);
    8998                    _reader = new I2CPMessageReader(in, this);
     99                    _reader.startReading();
    90100                }
     101            }
     102            // must be out of synch block for writer to get unblocked
     103            if (!_context.isRouterContext()) {
     104                Properties opts = getOptions();
     105                // Send auth message if required
     106                // Auth was not enforced on a simple session until 0.9.10
     107                // We will get disconnected for router version < 0.9.10 since it doesn't
     108                // support the AuthMessage
     109                if ((!opts.containsKey(PROP_USER)) && (!opts.containsKey(PROP_PW))) {
     110                    // auto-add auth if not set in the options
     111                    String configUser = _context.getProperty(PROP_USER);
     112                    String configPW = _context.getProperty(PROP_PW);
     113                    if (configUser != null && configPW != null) {
     114                        opts.setProperty(PROP_USER, configUser);
     115                        opts.setProperty(PROP_PW, configPW);
     116                    }
     117                }
     118                if (opts.containsKey(PROP_USER) && opts.containsKey(PROP_PW)) {
     119                    Properties auth = new OrderedProperties();
     120                    auth.setProperty(PROP_USER, opts.getProperty(PROP_USER));
     121                    auth.setProperty(PROP_PW, opts.getProperty(PROP_PW));
     122                    sendMessage(new GetDateMessage(CoreVersion.VERSION, auth));
     123                } else {
     124                    // we must now send a GetDate even in SimpleSession, or we won't know
     125                    // what version we are talking with and cannot use HostLookup
     126                    sendMessage(new GetDateMessage(CoreVersion.VERSION));
     127                }
     128                waitForDate();
    91129            }
    92130            // we do not receive payload messages, so we do not need an AvailabilityNotifier
    93131            // ... or an Idle timer, or a VerifyUsage
    94             _reader.startReading();
    95132            success = true;
     133            if (_log.shouldLog(Log.INFO))
     134                _log.info(getPrefix() + " simple session connected");
     135        } catch (InterruptedException ie) {
     136            throw new I2PSessionException("Interrupted", ie);
    96137        } catch (UnknownHostException uhe) {
    97138            throw new I2PSessionException(getPrefix() + "Cannot connect to the router on " + _hostname + ':' + _portNum, uhe);
     
    116157        public SimpleMessageHandlerMap(I2PAppContext context) {
    117158            int highest = Math.max(DestReplyMessage.MESSAGE_TYPE, BandwidthLimitsMessage.MESSAGE_TYPE);
     159            highest = Math.max(highest, DisconnectMessage.MESSAGE_TYPE);
     160            highest = Math.max(highest, HostReplyMessage.MESSAGE_TYPE);
     161            highest = Math.max(highest, SetDateMessage.MESSAGE_TYPE);
    118162            _handlers = new I2CPMessageHandler[highest+1];
    119163            _handlers[DestReplyMessage.MESSAGE_TYPE] = new DestReplyMessageHandler(context);
    120164            _handlers[BandwidthLimitsMessage.MESSAGE_TYPE] = new BWLimitsMessageHandler(context);
     165            _handlers[DisconnectMessage.MESSAGE_TYPE] = new DisconnectMessageHandler(context);
     166            _handlers[HostReplyMessage.MESSAGE_TYPE] = new HostReplyMessageHandler(context);
     167            _handlers[SetDateMessage.MESSAGE_TYPE] = new SetDateMessageHandler(context);
    121168        }
    122169    }
  • core/java/src/net/i2p/client/naming/LookupDest.java

    r38c02b44 rcc97a19  
    3535
    3636    private static final long DEFAULT_TIMEOUT = 15*1000;
     37    private static final String PROP_ENABLE_SSL = "i2cp.SSL";
     38    private static final String PROP_USER = "i2cp.username";
     39    private static final String PROP_PW = "i2cp.password";
    3740
    3841    protected LookupDest(I2PAppContext context) {}
     
    6265        I2PClient client = new I2PSimpleClient();
    6366        Properties opts = new Properties();
    64         String s = ctx.getProperty(I2PClient.PROP_TCP_HOST);
    65         if (s != null)
    66             opts.put(I2PClient.PROP_TCP_HOST, s);
    67         s = ctx.getProperty(I2PClient.PROP_TCP_PORT);
    68         if (s != null)
    69             opts.put(I2PClient.PROP_TCP_PORT, s);
     67        if (!ctx.isRouterContext()) {
     68            String s = ctx.getProperty(I2PClient.PROP_TCP_HOST);
     69            if (s != null)
     70                opts.put(I2PClient.PROP_TCP_HOST, s);
     71            s = ctx.getProperty(I2PClient.PROP_TCP_PORT);
     72            if (s != null)
     73                opts.put(I2PClient.PROP_TCP_PORT, s);
     74            s = ctx.getProperty(PROP_ENABLE_SSL);
     75            if (s != null)
     76                opts.put(PROP_ENABLE_SSL, s);
     77            s = ctx.getProperty(PROP_USER);
     78            if (s != null)
     79                opts.put(PROP_USER, s);
     80            s = ctx.getProperty(PROP_PW);
     81            if (s != null)
     82                opts.put(PROP_PW, s);
     83        }
    7084        I2PSession session = null;
    7185        try {
  • core/java/src/net/i2p/data/i2cp/GetDateMessage.java

    r38c02b44 rcc97a19  
    1313import java.io.IOException;
    1414import java.io.InputStream;
     15import java.util.Map;
     16import java.util.Properties;
    1517
    1618import net.i2p.data.DataFormatException;
    1719import net.i2p.data.DataHelper;
     20import net.i2p.util.OrderedProperties;
    1821
    1922/**
    20  * Request the other side to send us what they think the current time is/
     23 * Request the other side to send us what they think the current time is.
    2124 * Only supported from client to router.
    2225 *
    2326 * Since 0.8.7, optionally include a version string.
     27 * Since 0.9.10, optionally include options.
    2428 */
    2529public class GetDateMessage extends I2CPMessageImpl {
    2630    public final static int MESSAGE_TYPE = 32;
    2731    private String _version;
     32    private Properties _options;
    2833
    2934    public GetDateMessage() {
     
    4146
    4247    /**
     48     *  @param version the client's version String to be sent to the router; may be null;
     49     *                 must be non-null if options is non-null and non-empty.
     50     *  @param options Client options to be sent to the router; primarily for authentication; may be null;
     51     *                 keys and values 255 bytes (not chars) max each
     52     *  @since 0.9.10
     53     */
     54    public GetDateMessage(String version, Properties options) {
     55        super();
     56        if (version == null && options != null && !options.isEmpty())
     57            throw new IllegalArgumentException();
     58        _version = version;
     59        _options = options;
     60    }
     61
     62    /**
    4363     *  @return may be null
    4464     *  @since 0.8.7
     
    4868    }
    4969
     70    /**
     71     *  Retrieve any configuration options for the connection.
     72     *  Primarily for authentication.
     73     *
     74     *  @return may be null
     75     *  @since 0.9.10
     76     */
     77    public Properties getOptions() {
     78        return _options;
     79    }
     80
    5081    @Override
    5182    protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
     
    5384            try {
    5485                _version = DataHelper.readString(in);
     86                if (size > 1 + _version.length())  // assume ascii
     87                    _options = DataHelper.readProperties(in);
    5588            } catch (DataFormatException dfe) {
    5689                throw new I2CPMessageException("Bad version string", dfe);
     
    6396        if (_version == null)
    6497            return new byte[0];
    65         ByteArrayOutputStream os = new ByteArrayOutputStream(16);
     98        ByteArrayOutputStream os = new ByteArrayOutputStream(_options != null ? 128 : 16);
    6699        try {
    67100            DataHelper.writeString(os, _version);
     101            if (_options != null && !_options.isEmpty())
     102                DataHelper.writeProperties(os, _options, true);  // UTF-8
    68103        } catch (DataFormatException dfe) {
    69104            throw new I2CPMessageException("Error writing out the message data", dfe);
     
    81116        buf.append("[GetDateMessage]");
    82117        buf.append("\n\tVersion: ").append(_version);
     118        if (_options != null && !_options.isEmpty()) {
     119            buf.append("\n\tOptions: #: ").append(_options.size());
     120            Properties sorted = new OrderedProperties();
     121            sorted.putAll(_options);
     122            for (Map.Entry<Object, Object> e : sorted.entrySet()) {
     123                String key = (String) e.getKey();
     124                String val = (String) e.getValue();
     125                buf.append("\n\t\t[").append(key).append("] = [").append(val).append("]");
     126            }
     127        }
    83128        return buf.toString();
    84129    }
  • core/java/src/net/i2p/data/i2cp/I2CPMessageHandler.java

    r38c02b44 rcc97a19  
    3232     */
    3333    public static I2CPMessage readMessage(InputStream in) throws IOException, I2CPMessageException {
    34         int length = -1;
     34        int length;
    3535        try {
    3636            length = (int) DataHelper.readLong(in, 4);
     
    4242            int type = (int) DataHelper.readLong(in, 1);
    4343            I2CPMessage msg = createMessage(type);
     44            // Note that the readMessage() calls don't, in general, read and discard
     45            // extra data, so we can't add new fields to the end of messages
     46            // in a compatible way. And the readers could read beyond the length too.
     47            // To fix this we'd have to read into a BAOS/BAIS or use a filter input stream
    4448            msg.readMessage(in, length, type);
    4549            return msg;
     
    5357     *
    5458     */
    55     private static I2CPMessage createMessage(int type) throws IOException,
     59    private static I2CPMessage createMessage(int type) throws
    5660                                                       I2CPMessageException {
    5761        switch (type) {
     
    98102        case BandwidthLimitsMessage.MESSAGE_TYPE:
    99103            return new BandwidthLimitsMessage();
     104        case HostLookupMessage.MESSAGE_TYPE:
     105            return new HostLookupMessage();
     106        case HostReplyMessage.MESSAGE_TYPE:
     107            return new HostReplyMessage();
    100108        default:
    101109            throw new I2CPMessageException("The type " + type + " is an unknown I2CP message");
  • core/java/src/net/i2p/data/i2cp/I2CPMessageReader.java

    r38c02b44 rcc97a19  
    111111         *
    112112         * @param reader I2CPMessageReader to notify
    113          * @param error Exception that was thrown
     113         * @param error Exception that was thrown, non-null
    114114         */
    115115        public void readError(I2CPMessageReader reader, Exception error);
  • core/java/src/net/i2p/data/i2cp/SessionConfig.java

    r38c02b44 rcc97a19  
    9090
    9191    /**
    92      * Configure the session with the given options
     92     * Configure the session with the given options;
     93     * keys and values 255 bytes (not chars) max each
    9394     *
    9495     * @param options Properties for this session
  • router/java/src/net/i2p/router/client/ClientConnectionRunner.java

    r38c02b44 rcc97a19  
    336336     *  See ClientMessageEventListener.handleCreateSession()
    337337     *  for why we don't send a SessionStatusMessage when we do this.
     338     *  @param reason will be truncated to 255 bytes
    338339     */
    339340    void disconnectClient(String reason) {
     
    342343
    343344    /**
     345     * @param reason will be truncated to 255 bytes
    344346     * @param logLevel e.g. Log.WARN
    345347     * @since 0.8.2
     
    352354                     + _config);
    353355        DisconnectMessage msg = new DisconnectMessage();
     356        if (reason.length() > 255)
     357            reason = reason.substring(0, 255);
    354358        msg.setReason(reason);
    355359        try {
  • router/java/src/net/i2p/router/client/ClientMessageEventListener.java

    r38c02b44 rcc97a19  
    2121import net.i2p.data.i2cp.GetBandwidthLimitsMessage;
    2222import net.i2p.data.i2cp.GetDateMessage;
     23import net.i2p.data.i2cp.HostLookupMessage;
    2324import net.i2p.data.i2cp.I2CPMessage;
    2425import net.i2p.data.i2cp.I2CPMessageException;
     
    5152    protected final ClientConnectionRunner _runner;
    5253    private final boolean  _enforceAuth;
     54    private volatile boolean _authorized;
    5355   
    5456    private static final String PROP_AUTH = "i2cp.auth";
     57    /** if true, user/pw must be in GetDateMessage */
     58    private static final String PROP_AUTH_STRICT = "i2cp.strictAuth";
    5559
    5660    /**
     
    6266        _runner = runner;
    6367        _enforceAuth = enforceAuth;
     68        if ((!_enforceAuth) || !_context.getBooleanProperty(PROP_AUTH))
     69            _authorized = true;
    6470        _context.statManager().createRateStat("client.distributeTime", "How long it took to inject the client message into the router", "ClientMessages", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
    6571    }
     
    7379        if (_log.shouldLog(Log.DEBUG))
    7480            _log.debug("Message received: \n" + message);
     81        int type = message.getType();
     82        if (!_authorized) {
     83            // TODO change to default true
     84            boolean strict = _context.getBooleanProperty(PROP_AUTH_STRICT);
     85            if ((strict && type != GetDateMessage.MESSAGE_TYPE) ||
     86                (type != CreateSessionMessage.MESSAGE_TYPE &&
     87                 type != GetDateMessage.MESSAGE_TYPE &&
     88                 type != DestLookupMessage.MESSAGE_TYPE &&
     89                 type != GetBandwidthLimitsMessage.MESSAGE_TYPE)) {
     90                _log.error("Received message type " + type + " without required authentication");
     91                _runner.disconnectClient("Authorization required");
     92                return;
     93            }
     94        }
    7595        switch (message.getType()) {
    7696            case GetDateMessage.MESSAGE_TYPE:
     
    103123            case DestLookupMessage.MESSAGE_TYPE:
    104124                handleDestLookup((DestLookupMessage)message);
     125                break;
     126            case HostLookupMessage.MESSAGE_TYPE:
     127                handleHostLookup((HostLookupMessage)message);
    105128                break;
    106129            case ReconfigureSessionMessage.MESSAGE_TYPE:
     
    125148            _log.error("Error occurred", error);
    126149        // Is this is a little drastic for an unknown message type?
     150        // Send the whole exception string over for diagnostics
     151        _runner.disconnectClient(error.toString());
    127152        _runner.stopRunning();
    128153    }
     
    138163        if (clientVersion != null)
    139164            _runner.setClientVersion(clientVersion);
     165        Properties props = message.getOptions();
     166        if (!checkAuth(props))
     167            return;
    140168        try {
    141169            // only send version if the client can handle it (0.8.7 or greater)
     
    175203
    176204        // Auth, since 0.8.2
    177         if (_enforceAuth && _context.getBooleanProperty(PROP_AUTH)) {
    178                 Properties props = in.getOptions();
    179                 String user = props.getProperty("i2cp.username");
    180                 String pw = props.getProperty("i2cp.password");
    181                 if (user == null || user.length() == 0 || pw == null || pw.length() == 0) {
    182                     _log.error("I2CP auth failed for client: " + props.getProperty("inbound.nickname"));
    183                     _runner.disconnectClient("Authorization required to create session, specify i2cp.username and i2cp.password in session options");
    184                     return;
    185                 }
    186                 PasswordManager mgr = new PasswordManager(_context);
    187                 if (!mgr.checkHash(PROP_AUTH, user, pw)) {
    188                     _log.error("I2CP auth failed for client: " + props.getProperty("inbound.nickname") + " user: " + user);
    189                     _runner.disconnectClient("Authorization failed for Create Session, user = " + user);
    190                     return;
    191                 }
    192                 if (_log.shouldLog(Log.INFO))
    193                     _log.info("I2CP auth success for client: " + props.getProperty("inbound.nickname") + " user: " + user);
    194         }
     205        Properties inProps = in.getOptions();
     206        if (!checkAuth(inProps))
     207            return;
    195208
    196209        SessionId sessionId = new SessionId();
     
    214227    }
    215228   
     229    /**
     230     *  Side effect - sets _authorized.
     231     *  Side effect - disconnects session if not authorized.
     232     *
     233     *  @param props contains i2cp.username and i2cp.password, may be null
     234     *  @return success
     235     *  @since 0.9.10
     236     */
     237    private boolean checkAuth(Properties props) {
     238        if (_authorized)
     239            return true;
     240        if (_enforceAuth && _context.getBooleanProperty(PROP_AUTH)) {
     241            String user = null;
     242            String pw = null;
     243            if (props != null) {
     244                user = props.getProperty("i2cp.username");
     245                pw = props.getProperty("i2cp.password");
     246            }
     247            if (user == null || user.length() == 0 || pw == null || pw.length() == 0) {
     248                _log.error("I2CP auth failed");
     249                _runner.disconnectClient("Authorization required, specify i2cp.username and i2cp.password in options");
     250                _authorized = false;
     251                return false;
     252            }
     253            PasswordManager mgr = new PasswordManager(_context);
     254            if (!mgr.checkHash(PROP_AUTH, user, pw)) {
     255                _log.error("I2CP auth failed user: " + user);
     256                _runner.disconnectClient("Authorization failed, user = " + user);
     257                _authorized = false;
     258                return false;
     259            }
     260            if (_log.shouldLog(Log.INFO))
     261                _log.info("I2CP auth success user: " + user);
     262        }
     263        _authorized = true;
     264        return true;
     265    }
     266
    216267    /**
    217268     *  Override for testing
     
    317368
    318369    /**
     370     * override for testing
     371     * @since 0.9.10
     372     */
     373    protected void handleHostLookup(HostLookupMessage message) {
     374        _context.jobQueue().addJob(new LookupDestJob(_context, _runner, message.getReqID(),
     375                                                     message.getTimeout(), message.getHash(), message.getHostname()));
     376    }
     377
     378    /**
    319379     * Message's Session ID ignored. This doesn't support removing previously set options.
    320380     * Nor do we bother with message.getSessionConfig().verifySignature() ... should we?
  • router/java/src/net/i2p/router/client/LookupDestJob.java

    r38c02b44 rcc97a19  
    55package net.i2p.router.client;
    66
     7import java.util.Locale;
     8
     9import net.i2p.data.Base32;
    710import net.i2p.data.Destination;
    811import net.i2p.data.Hash;
    912import net.i2p.data.LeaseSet;
    1013import net.i2p.data.i2cp.DestReplyMessage;
     14import net.i2p.data.i2cp.HostReplyMessage;
     15import net.i2p.data.i2cp.I2CPMessage;
    1116import net.i2p.data.i2cp.I2CPMessageException;
    1217import net.i2p.router.JobImpl;
     
    1419
    1520/**
    16  * Look up the lease of a hash, to convert it to a Destination for the client
     21 * Look up the lease of a hash, to convert it to a Destination for the client.
     22 * Or, since 0.9.10, lookup a host name in the naming service.
    1723 */
    1824class LookupDestJob extends JobImpl {
    1925    private final ClientConnectionRunner _runner;
     26    private final long _reqID;
     27    private final long _timeout;
    2028    private final Hash _hash;
     29    private final String _name;
     30
     31    private static final long DEFAULT_TIMEOUT = 15*1000;
    2132
    2233    public LookupDestJob(RouterContext context, ClientConnectionRunner runner, Hash h) {
     34        this(context, runner, -1, DEFAULT_TIMEOUT, h, null);
     35    }
     36
     37    /**
     38     *  One of h or name non-null
     39     *  @param reqID must be >= 0 if name != null
     40     *  @since 0.9.10
     41     */
     42    public LookupDestJob(RouterContext context, ClientConnectionRunner runner,
     43                         long reqID, long timeout, Hash h, String name) {
    2344        super(context);
     45        if ((h == null && name == null) ||
     46            (h != null && name != null) ||
     47            (reqID < 0 && name != null))
     48            throw new IllegalArgumentException();
    2449        _runner = runner;
     50        _reqID = reqID;
     51        _timeout = timeout;
     52        if (name != null && name.length() == 60) {
     53            // convert a b32 lookup to a hash lookup
     54            String nlc = name.toLowerCase(Locale.US);
     55            if (nlc.endsWith(".b32.i2p")) {
     56                byte[] b = Base32.decode(nlc.substring(0, 52));
     57                if (b != null && b.length == Hash.HASH_LENGTH) {
     58                    h = Hash.create(b);
     59                    name = null;
     60                }
     61            }
     62        }
    2563        _hash = h;
     64        _name = name;
    2665    }
    2766   
    28     public String getName() { return "LeaseSet Lookup for Client"; }
     67    public String getName() { return _name != null ?
     68                                     "HostName Lookup for Client" :
     69                                     "LeaseSet Lookup for Client";
     70    }
     71
    2972    public void runJob() {
    30         DoneJob done = new DoneJob(getContext());
    31         // TODO add support for specifying the timeout in the lookup message
    32         getContext().netDb().lookupLeaseSet(_hash, done, done, 15*1000);
     73        if (_name != null) {
     74            // inline, ignore timeout
     75            Destination d = getContext().namingService().lookup(_name);
     76            if (d != null)
     77                returnDest(d);
     78            else
     79                returnFail();
     80        } else {
     81            DoneJob done = new DoneJob(getContext());
     82            getContext().netDb().lookupLeaseSet(_hash, done, done, _timeout);
     83        }
    3384    }
    3485
     
    4394                returnDest(ls.getDestination());
    4495            else
    45                 returnHash(_hash);
     96                returnFail();
    4697        }
    4798    }
    4899
    49100    private void returnDest(Destination d) {
    50         DestReplyMessage msg = new DestReplyMessage(d);
     101        I2CPMessage msg;
     102        if (_reqID >= 0)
     103            msg = new HostReplyMessage(d, _reqID);
     104        else
     105            msg = new DestReplyMessage(d);
    51106        try {
    52107            _runner.doSend(msg);
     
    58113     *  @since 0.8.3
    59114     */
    60     private void returnHash(Hash h) {
    61         DestReplyMessage msg = new DestReplyMessage(h);
     115    private void returnFail() {
     116        I2CPMessage msg;
     117        if (_reqID >= 0)
     118            msg = new HostReplyMessage(HostReplyMessage.RESULT_FAILURE, _reqID);
     119        else
     120            msg = new DestReplyMessage(_hash);
    62121        try {
    63122            _runner.doSend(msg);
Note: See TracChangeset for help on using the changeset viewer.