diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java index 34dc025d56f802e0904c0b0605efb485ba019404..8b322d45a04db3e78e564790c56c927c683ca1d2 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java @@ -1328,25 +1328,30 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { * @param l logger to receive events and output */ private void runConfig(String args[], Logging l) { - if (args.length >= 2) { + if (args.length >= 1) { int i = 0; - if (args[0].equals("-s")) { + boolean ssl = args[0].equals("-s"); + if (ssl) { _clientOptions.setProperty("i2cp.SSL", "true"); i++; } else { _clientOptions.remove("i2cp.SSL"); } - host = args[i++]; - listenHost = host; - port = args[i]; + if (i < args.length) { + host = args[i++]; + listenHost = host; + } + if (i < args.length) + port = args[i]; + l.log("New setting: " + host + ' ' + port + (ssl ? " SSL" : " non-SSL")); notifyEvent("configResult", "ok"); } else { boolean ssl = Boolean.parseBoolean(_clientOptions.getProperty("i2cp.SSL")); l.log("Usage:\n" + - " config [-s] <i2phost> <i2pport>\n" + - " sets the connection to the i2p router.\n" + - "Current setting:\n" + - " " + host + ' ' + port + (ssl ? " SSL" : "")); + " config [-s] [<i2phost>] [<i2pport>]\n" + + " Sets the address and port of the I2P router.\n" + + " Use -s for SSL.\n" + + "Current setting: " + host + ' ' + port + (ssl ? " SSL" : " non-SSL")); notifyEvent("configResult", "error"); } } @@ -1750,8 +1755,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { */ public void log(String s) { System.out.println(s); - if (_log.shouldLog(Log.INFO)) - _log.info(getPrefix() + "Display: " + s); + //if (_log.shouldLog(Log.INFO)) + // _log.info(getPrefix() + "Display: " + s); } /** @@ -1769,8 +1774,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { l.log("Generating new keys..."); I2PClient client = I2PClientFactory.createClient(); Destination d = client.createDestination(writeTo); - l.log("Secret key saved.\n" + - "Public key: " + d.toBase64()); + l.log("New destination: " + d.toBase32()); writeTo.flush(); writeTo.close(); writePubKey(d, pubDest, l); @@ -1793,7 +1797,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { try { Destination d = new Destination(); d.readBytes(readFrom); - l.log("Public key: " + d.toBase64()); + l.log("Destination: " + d.toBase32()); readFrom.close(); writePubKey(d, pubDest, l); } catch (I2PException ex) { @@ -1808,7 +1812,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { * Deprecated - only used by CLI * * @param d Destination to write - * @param o stream to write the destination to + * @param o stream to write the destination to, or null for noop * @param l logger to send messages to */ private static void writePubKey(Destination d, OutputStream o, Logging l) throws I2PException, IOException { diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java index e3fdee8a37089b031a4b10768d62a942f38f6e86..e35f4342a12018cce181cedbc4299275632c2d50 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java @@ -102,6 +102,7 @@ public class TunnelControllerGroup implements ClientApp { * @param mgr may be null * @param args one arg, the config file, if not absolute will be relative to the context's config dir, * if empty or null, the default is i2ptunnel.config + * @throws IllegalArgumentException if too many args * @since 0.9.4 */ public TunnelControllerGroup(I2PAppContext context, ClientAppManager mgr, String[] args) { @@ -149,7 +150,19 @@ public class TunnelControllerGroup implements ClientApp { * @since 0.9.4 */ public void startup() { - loadControllers(_configFile); + try { + loadControllers(_configFile); + } catch (IllegalArgumentException iae) { + if (DEFAULT_CONFIG_FILE.equals(_configFile) && !_context.isRouterContext()) { + // for i2ptunnel command line + synchronized (_controllersLoadedLock) { + _controllersLoaded = true; + } + _log.logAlways(Log.WARN, "Not in router context and no preconfigured tunnels"); + } else { + throw iae; + } + } startControllers(); if (_mgr != null) _mgr.register(this); @@ -274,8 +287,12 @@ public class TunnelControllerGroup implements ClientApp { synchronized (_controllersLoadedLock) { _controllersLoaded = true; } - if (_log.shouldLog(Log.INFO)) - _log.info(i + " controllers loaded from " + configFile); + if (i > 0) { + if (_log.shouldLog(Log.INFO)) + _log.info(i + " controllers loaded from " + configFile); + } else { + _log.logAlways(Log.WARN, "No i2ptunnel configurations found in " + configFile); + } } /** @@ -294,6 +311,10 @@ public class TunnelControllerGroup implements ClientApp { synchronized(TunnelControllerGroup.this) { _controllersLock.readLock().lock(); try { + if (_controllers.size() <= 0) { + _log.logAlways(Log.WARN, "No configured tunnels to start"); + return; + } for (int i = 0; i < _controllers.size(); i++) { TunnelController controller = _controllers.get(i); if (controller.getStartOnLoad()) diff --git a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java index 226973fe4f6440c0d723cd8a62f3b309e4de1803..10a480c78e56349266b784d1c96f89d3034b48df 100644 --- a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java +++ b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java @@ -165,7 +165,9 @@ class ClientConnectionRunner { */ public synchronized void stopRunning() { if (_dead) return; - if (_context.router().isAlive() && _log.shouldLog(Log.WARN)) + // router may be null in unit tests + if ((_context.router() == null || _context.router().isAlive()) && + _log.shouldWarn()) _log.warn("Stop the I2CP connection! current leaseSet: " + _currentLeaseSet, new Exception("Stop client connection")); _dead = true; diff --git a/router/java/src/net/i2p/router/client/ClientListenerRunner.java b/router/java/src/net/i2p/router/client/ClientListenerRunner.java index 44d7811b6b06243f3ef69083fed58091c262cd6e..8510fa0719f5de7bb6344243c4db5987c31e87eb 100644 --- a/router/java/src/net/i2p/router/client/ClientListenerRunner.java +++ b/router/java/src/net/i2p/router/client/ClientListenerRunner.java @@ -15,6 +15,7 @@ import java.net.ServerSocket; import java.net.Socket; import net.i2p.client.I2PClient; +import net.i2p.router.Router; import net.i2p.router.RouterContext; import net.i2p.util.Log; @@ -101,17 +102,17 @@ class ClientListenerRunner implements Runnable { } catch (IOException ioe) {} } } catch (IOException ioe) { - if (_context.router().isAlive()) + if (isAlive()) _log.error("Server error accepting", ioe); } catch (Throwable t) { - if (_context.router().isAlive()) + if (isAlive()) _log.error("Fatal error running client listener - killing the thread!", t); _listening = false; return; } } } catch (IOException ioe) { - if (_context.router().isAlive()) + if (isAlive()) _log.error("Error listening on port " + _port, ioe); } @@ -121,7 +122,7 @@ class ClientListenerRunner implements Runnable { _socket = null; } - if (!_context.router().isAlive()) break; + if (!isAlive()) break; if (curDelay < 60*1000) _log.error("Error listening, waiting " + (curDelay/1000) + "s before we try again"); @@ -131,11 +132,20 @@ class ClientListenerRunner implements Runnable { curDelay = Math.min(curDelay*3, 60*1000); } - if (_context.router().isAlive()) + if (isAlive()) _log.error("CANCELING I2CP LISTEN", new Exception("I2CP Listen cancelled!!!")); _running = false; } + /** + * Just so unit tests don't NPE, where router could be null. + * @since 0.9.20 + */ + private boolean isAlive() { + Router r = _context.router(); + return r == null || r.isAlive(); + } + /** give the i2cp client 5 seconds to show that they're really i2cp clients */ protected final static int CONNECT_TIMEOUT = 5*1000; private final static int LOOP_DELAY = 250; diff --git a/router/java/test/junit/net/i2p/router/client/LocalClientConnectionRunner.java b/router/java/test/junit/net/i2p/router/client/LocalClientConnectionRunner.java index 85dcbcab08e4d320858cd9a2693afa5664ecb54b..9ded6ce909defda33a46d9dc0f59046dcfae9dc2 100644 --- a/router/java/test/junit/net/i2p/router/client/LocalClientConnectionRunner.java +++ b/router/java/test/junit/net/i2p/router/client/LocalClientConnectionRunner.java @@ -8,6 +8,8 @@ import net.i2p.data.Lease; import net.i2p.data.LeaseSet; import net.i2p.data.i2cp.I2CPMessageException; import net.i2p.data.i2cp.I2CPMessageReader; +import net.i2p.data.i2cp.MessageId; +import net.i2p.data.i2cp.MessageStatusMessage; import net.i2p.data.i2cp.RequestVariableLeaseSetMessage; import net.i2p.router.Job; import net.i2p.router.RouterContext; @@ -54,6 +56,27 @@ class LocalClientConnectionRunner extends ClientConnectionRunner { } } + /** + * No job queue, so super NPEs + */ + @Override + void updateMessageDeliveryStatus(MessageId id, long messageNonce, int status) { + if (messageNonce <= 0) + return; + MessageStatusMessage msg = new MessageStatusMessage(); + msg.setMessageId(id.getMessageId()); + msg.setSessionId(getSessionId().getSessionId()); + // has to be >= 0, it is initialized to -1 + msg.setNonce(messageNonce); + msg.setSize(0); + msg.setStatus(status); + try { + doSend(msg); + } catch (I2CPMessageException ime) { + _log.warn("Error updating the status for " + id, ime); + } + } + /** * So LocalClientMessageEventListener can lookup other local dests */ diff --git a/router/java/test/junit/net/i2p/router/client/LocalClientManager.java b/router/java/test/junit/net/i2p/router/client/LocalClientManager.java index f8df0298997caf6c52a059a0e319794e5e9fd890..12666969da0b315e6d170a03810b4a4d0d13e639 100644 --- a/router/java/test/junit/net/i2p/router/client/LocalClientManager.java +++ b/router/java/test/junit/net/i2p/router/client/LocalClientManager.java @@ -73,7 +73,7 @@ class LocalClientManager extends ClientManager { ClientManager mgr = new LocalClientManager(ctx, port); mgr.start(); System.out.println("Listening on port " + port); - try { Thread.sleep(5*60*1000); } catch (InterruptedException ie) {} + try { Thread.sleep(60*60*1000); } catch (InterruptedException ie) {} System.out.println("Done listening on port " + port); } } diff --git a/router/java/test/junit/net/i2p/router/client/LocalClientMessageEventListener.java b/router/java/test/junit/net/i2p/router/client/LocalClientMessageEventListener.java index 611e0c24fb6d2717c56487e202fdd70db8ae40ef..17cd20e7165986fd68ad264c25f95f92bc78ae45 100644 --- a/router/java/test/junit/net/i2p/router/client/LocalClientMessageEventListener.java +++ b/router/java/test/junit/net/i2p/router/client/LocalClientMessageEventListener.java @@ -9,7 +9,9 @@ package net.i2p.router.client; */ import java.util.Date; +import java.util.Locale; +import net.i2p.data.Base32; import net.i2p.data.Destination; import net.i2p.data.Hash; import net.i2p.data.Lease; @@ -20,7 +22,10 @@ import net.i2p.data.i2cp.CreateLeaseSetMessage; import net.i2p.data.i2cp.DestLookupMessage; import net.i2p.data.i2cp.DestReplyMessage; import net.i2p.data.i2cp.GetBandwidthLimitsMessage; +import net.i2p.data.i2cp.HostLookupMessage; +import net.i2p.data.i2cp.HostReplyMessage; import net.i2p.data.i2cp.I2CPMessageException; +import net.i2p.data.i2cp.SessionId; import net.i2p.router.RouterContext; /** @@ -78,6 +83,40 @@ class LocalClientMessageEventListener extends ClientMessageEventListener { } } + /** + * Look only in current local dests + */ + @Override + protected void handleHostLookup(HostLookupMessage message) { + Hash h = message.getHash(); + String name = message.getHostname(); + long reqID = message.getReqID(); + SessionId sessID = message.getSessionId(); + if (h == null && name != null && name.length() == 60) { + // convert a b32 lookup to a hash lookup + String nlc = name.toLowerCase(Locale.US); + if (nlc.endsWith(".b32.i2p")) { + byte[] b = Base32.decode(nlc.substring(0, 52)); + if (b != null && b.length == Hash.HASH_LENGTH) { + h = Hash.create(b); + } + } + } + Destination d = null; + if (h != null) + d = ((LocalClientConnectionRunner)_runner).localLookup(h); + HostReplyMessage msg; + if (d != null) + msg = new HostReplyMessage(sessID, d, reqID); + else + msg = new HostReplyMessage(sessID, HostReplyMessage.RESULT_FAILURE, reqID); + try { + _runner.doSend(msg); + } catch (I2CPMessageException ime) { + ime.printStackTrace(); + } + } + /** * Send dummy limits */