From e1d9e05b8dbee2fd2902762db697ab78ecd537b7 Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Wed, 22 Apr 2015 11:59:40 +0000 Subject: [PATCH] i2ptunnel: Fixes and cleanups for command line testing; catch IAE from getInstance() if i2ptunnel.config isn't found in app context; log tweaks; config command tweaks Unit tests: Fix several NPEs in LocalClientManager, implement HostLookup --- .../java/src/net/i2p/i2ptunnel/I2PTunnel.java | 34 +++++++++------- .../i2p/i2ptunnel/TunnelControllerGroup.java | 27 +++++++++++-- .../router/client/ClientConnectionRunner.java | 4 +- .../router/client/ClientListenerRunner.java | 20 +++++++--- .../client/LocalClientConnectionRunner.java | 23 +++++++++++ .../i2p/router/client/LocalClientManager.java | 2 +- .../LocalClientMessageEventListener.java | 39 +++++++++++++++++++ 7 files changed, 124 insertions(+), 25 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java index 34dc025d56..8b322d45a0 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 e3fdee8a37..e35f4342a1 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 226973fe4f..10a480c78e 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 44d7811b6b..8510fa0719 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 85dcbcab08..9ded6ce909 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 f8df029899..12666969da 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 611e0c24fb..17cd20e716 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 */ -- GitLab