From 72f57255f0590fd70bba09a1a1b13b75548d898a Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Fri, 26 Sep 2014 14:16:08 +0000 Subject: [PATCH] Transport: Hooks for pluggable transports (ticket #1170) --- .../src/net/i2p/router/CommSystemFacade.java | 20 ++++++ .../transport/CommSystemFacadeImpl.java | 42 ++++++++++++ .../router/transport/TransportManager.java | 68 +++++++++++++++++-- 3 files changed, 125 insertions(+), 5 deletions(-) diff --git a/router/java/src/net/i2p/router/CommSystemFacade.java b/router/java/src/net/i2p/router/CommSystemFacade.java index 59a22eb9ca..b81f5bfffb 100644 --- a/router/java/src/net/i2p/router/CommSystemFacade.java +++ b/router/java/src/net/i2p/router/CommSystemFacade.java @@ -14,6 +14,8 @@ import java.util.Collections; import java.util.List; import net.i2p.data.Hash; import net.i2p.data.router.RouterAddress; +import net.i2p.router.transport.Transport; +import net.i2p.router.transport.crypto.DHSessionKeyBuilder; /** * Manages the communication subsystem between peers, including connections, @@ -85,6 +87,24 @@ public abstract class CommSystemFacade implements Service { */ public void notifyReplaceAddress(RouterAddress UDPAddr) {} + /** + * Pluggable transport + * @since 0.9.16 + */ + public void registerTransport(Transport t) {} + + /** + * Pluggable transport + * @since 0.9.16 + */ + public void unregisterTransport(Transport t) {} + + /** + * Hook for pluggable transport creation. + * @since 0.9.16 + */ + public DHSessionKeyBuilder.Factory getDHFactory() { return null; } + /** * These must be increasing in "badness" (see TransportManager.java), * but UNKNOWN must be last. diff --git a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java index b9e38f9da3..0c7cd430f1 100644 --- a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java +++ b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java @@ -22,6 +22,7 @@ import net.i2p.data.router.RouterInfo; import net.i2p.router.CommSystemFacade; import net.i2p.router.OutNetMessage; import net.i2p.router.RouterContext; +import net.i2p.router.transport.crypto.DHSessionKeyBuilder; import net.i2p.router.transport.udp.UDPTransport; import net.i2p.router.util.EventLog; import net.i2p.util.Addresses; @@ -222,6 +223,47 @@ public class CommSystemFacadeImpl extends CommSystemFacade { } _manager.externalAddressReceived(Transport.AddressSource.SOURCE_SSU, ip, port); } + + /** + * Pluggable transports. Not for NTCP or SSU. + * + * Do not call from transport constructor. Transport must be ready to be started. + * + * Following transport methods will be called: + * setListener() + * externalAddressReceived() (zero or more times, one for each known address) + * startListening(); + * + * @since 0.9.16 + */ + @Override + public void registerTransport(Transport t) { + _manager.registerAndStart(t); + } + + /** + * Pluggable transports. Not for NTCP or SSU. + * + * Following transport methods will be called: + * setListener(null) + * stoptListening(); + * + * @since 0.9.16 + */ + @Override + public void unregisterTransport(Transport t) { + _manager.stopAndUnregister(t); + } + + /** + * Hook for pluggable transport creation. + * + * @since 0.9.16 + */ + @Override + public DHSessionKeyBuilder.Factory getDHFactory() { + return _manager.getDHFactory(); + } /* * GeoIP stuff diff --git a/router/java/src/net/i2p/router/transport/TransportManager.java b/router/java/src/net/i2p/router/transport/TransportManager.java index 75e843707c..5062cc14bb 100644 --- a/router/java/src/net/i2p/router/transport/TransportManager.java +++ b/router/java/src/net/i2p/router/transport/TransportManager.java @@ -13,6 +13,7 @@ import java.io.Writer; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -44,6 +45,8 @@ public class TransportManager implements TransportEventListener { * If we want more than one transport with the same style we will have to change this. */ private final Map<String, Transport> _transports; + /** locking: this */ + private final Map<String, Transport> _pluggableTransports; private final RouterContext _context; private final UPnPManager _upnpManager; private final DHSessionKeyBuilder.PrecalcRunner _dhThread; @@ -66,22 +69,74 @@ public class TransportManager implements TransportEventListener { _context.statManager().createRateStat("transport.bidFailNoTransports", "Could not attempt to bid on message, as none of the transports could attempt it", "Transport", new long[] { 60*1000, 10*60*1000, 60*60*1000 }); _context.statManager().createRateStat("transport.bidFailAllTransports", "Could not attempt to bid on message, as all of the transports had failed", "Transport", new long[] { 60*1000, 10*60*1000, 60*60*1000 }); _transports = new ConcurrentHashMap<String, Transport>(2); + _pluggableTransports = new HashMap<String, Transport>(2); if (_context.getBooleanPropertyDefaultTrue(PROP_ENABLE_UPNP)) _upnpManager = new UPnPManager(context, this); else _upnpManager = null; _dhThread = new DHSessionKeyBuilder.PrecalcRunner(context); } + + /** + * Pluggable transports. Not for NTCP or SSU. + * + * @since 0.9.16 + */ + synchronized void registerAndStart(Transport t) { + String style = t.getStyle(); + if (style.equals(NTCPTransport.STYLE) || style.equals(UDPTransport.STYLE)) + throw new IllegalArgumentException("Builtin transport"); + if (_transports.containsKey(style) || _pluggableTransports.containsKey(style)) + throw new IllegalStateException("Dup transport"); + boolean shouldStart = !_transports.isEmpty(); + _pluggableTransports.put(style, t); + addTransport(t); + t.setListener(this); + if (shouldStart) { + initializeAddress(t); + t.startListening(); + _context.router().rebuildRouterInfo(); + } // else will be started by configTransports() (unlikely) + } + + /** + * Pluggable transports. Not for NTCP or SSU. + * + * @since 0.9.16 + */ + synchronized void stopAndUnregister(Transport t) { + String style = t.getStyle(); + if (style.equals(NTCPTransport.STYLE) || style.equals(UDPTransport.STYLE)) + throw new IllegalArgumentException("Builtin transport"); + t.setListener(null); + _pluggableTransports.remove(style); + removeTransport(t); + t.stopListening(); + _context.router().rebuildRouterInfo(); + } + + /** + * Hook for pluggable transport creation. + * + * @since 0.9.16 + */ + DHSessionKeyBuilder.Factory getDHFactory() { + return _dhThread; + } - public void addTransport(Transport transport) { + private void addTransport(Transport transport) { if (transport == null) return; - _transports.put(transport.getStyle(), transport); + Transport old = _transports.put(transport.getStyle(), transport); + if (old != null && old != transport && _log.shouldLog(Log.WARN)) + _log.warn("Replacing transport " + transport.getStyle()); transport.setListener(this); } - public void removeTransport(Transport transport) { + private void removeTransport(Transport transport) { if (transport == null) return; - _transports.remove(transport.getStyle()); + Transport old = _transports.remove(transport.getStyle()); + if (old != null && _log.shouldLog(Log.WARN)) + _log.warn("Removing transport " + transport.getStyle()); transport.setListener(null); } @@ -174,7 +229,10 @@ public class TransportManager implements TransportEventListener { tp = getTransport(UDPTransport.STYLE); if (tp != null) tps.add(tp); - //for (Transport t : _transports.values()) { + // now add any others (pluggable) + for (Transport t : _pluggableTransports.values()) { + tps.add(t); + } for (Transport t : tps) { t.startListening(); if (_log.shouldLog(Log.DEBUG)) -- GitLab