diff options
author | Tarun Gupta <tarun1995gupta@gmail.com> | 2015-07-27 15:01:45 (GMT) |
---|---|---|
committer | Alex Clayton <alex.clayton@isode.com> | 2016-01-12 11:31:04 (GMT) |
commit | b4cf2bb8d7b69d95b4a10d610ad259998d2aee5b (patch) | |
tree | b942953236a7c712eed6ce4a5f261019691c0dca | |
parent | c168fcd0c2468ec939b8d164175e9c5776a63147 (diff) | |
download | stroke-b4cf2bb8d7b69d95b4a10d610ad259998d2aee5b.zip stroke-b4cf2bb8d7b69d95b4a10d610ad259998d2aee5b.tar.bz2 |
Make Networks equivalent with Swiften.
Adds ProxyProviders, DomainNameResolvers and DummyConnection.
License:
This patch is BSD-licensed, see Documentation/Licenses/BSD-simplified.txt for details.
Test-Information:
Tests added for ChainedConnector, Connector and HostAddress.
Test also added for ComponentConnector, which needed bits of Network.
Five assertions are commented in ConnectorTest, which fails and will be updated after review.
Change-Id: I8a62841eb2f9c109bc3a94865b7a003b33493e11
49 files changed, 3106 insertions, 49 deletions
diff --git a/src/com/isode/stroke/client/ClientOptions.java b/src/com/isode/stroke/client/ClientOptions.java index c2d9e3f..723b12e 100644 --- a/src/com/isode/stroke/client/ClientOptions.java +++ b/src/com/isode/stroke/client/ClientOptions.java @@ -12,6 +12,7 @@ package com.isode.stroke.client; import com.isode.stroke.tls.TLSOptions; import com.isode.stroke.base.URL; import com.isode.stroke.base.SafeByteArray; +import com.isode.stroke.network.HTTPTrafficFilter; /** * Options for a client connection @@ -119,7 +120,7 @@ public class ClientOptions { * This can be initialized with a custom HTTPTrafficFilter, which allows HTTP CONNECT * proxy initialization to be customized. */ - //public HTTPTrafficFilter httpTrafficFilter; //TOPORT NETWORK + public HTTPTrafficFilter httpTrafficFilter; /** * Options passed to the TLS stack diff --git a/src/com/isode/stroke/client/CoreClient.java b/src/com/isode/stroke/client/CoreClient.java index 2fc10ae..010a535 100644 --- a/src/com/isode/stroke/client/CoreClient.java +++ b/src/com/isode/stroke/client/CoreClient.java @@ -18,6 +18,9 @@ import com.isode.stroke.network.ConnectionFactory; import com.isode.stroke.network.Connector; import com.isode.stroke.network.DomainNameResolveError; import com.isode.stroke.network.NetworkFactories; +import com.isode.stroke.network.HostAddressPort; +import com.isode.stroke.network.SOCKS5ProxiedConnectionFactory; +import com.isode.stroke.network.HTTPConnectProxiedConnectionFactory; import com.isode.stroke.parser.payloadparsers.FullPayloadParserFactoryCollection; import com.isode.stroke.queries.IQRouter; import com.isode.stroke.serializer.payloadserializers.FullPayloadSerializerCollection; @@ -201,17 +204,16 @@ public class CoreClient { assert (connector_ == null); options = o; - //TO PORT - /*// Determine connection types to use + // Determine connection types to use assert(proxyConnectionFactories.isEmpty()); boolean useDirectConnection = true; HostAddressPort systemSOCKS5Proxy = networkFactories.getProxyProvider().getSOCKS5Proxy(); HostAddressPort systemHTTPConnectProxy = networkFactories.getProxyProvider().getHTTPConnectProxy(); switch (o.proxyType) { - case ClientOptions.ProxyType.NoProxy: + case NoProxy: logger_.fine(" without a proxy\n"); break; - case ClientOptions.ProxyType.SystemConfiguredProxy: + case SystemConfiguredProxy: logger_.fine(" with a system configured proxy\n"); if (systemSOCKS5Proxy.isValid()) { logger_.fine("Found SOCK5 Proxy: " + systemSOCKS5Proxy.getAddress().toString() + ":" + systemSOCKS5Proxy.getPort() + "\n"); @@ -222,20 +224,20 @@ public class CoreClient { proxyConnectionFactories.add(new HTTPConnectProxiedConnectionFactory(networkFactories.getDomainNameResolver(), networkFactories.getConnectionFactory(), networkFactories.getTimerFactory(), systemHTTPConnectProxy.getAddress().toString(), systemHTTPConnectProxy.getPort())); } break; - case ClientOptions.ProxyType.SOCKS5Proxy: { + case SOCKS5Proxy: { logger_.fine(" with manual configured SOCKS5 proxy\n"); - String proxyHostname = o.manualProxyHostname.empty() ? systemSOCKS5Proxy.getAddress().toString() : o.manualProxyHostname; + String proxyHostname = o.manualProxyHostname.isEmpty() ? systemSOCKS5Proxy.getAddress().toString() : o.manualProxyHostname; int proxyPort = o.manualProxyPort == -1 ? systemSOCKS5Proxy.getPort() : o.manualProxyPort; logger_.fine("Proxy: " + proxyHostname + ":" + proxyPort + "\n"); proxyConnectionFactories.add(new SOCKS5ProxiedConnectionFactory(networkFactories.getDomainNameResolver(), networkFactories.getConnectionFactory(), networkFactories.getTimerFactory(), proxyHostname, proxyPort)); useDirectConnection = false; break; } - case ClientOptions.ProxyType.HTTPConnectProxy: { + case HTTPConnectProxy: { logger_.fine(" with manual configured HTTPConnect proxy\n"); - std::string proxyHostname = o.manualProxyHostname.empty() ? systemHTTPConnectProxy.getAddress().toString() : o.manualProxyHostname; + String proxyHostname = o.manualProxyHostname.isEmpty() ? systemHTTPConnectProxy.getAddress().toString() : o.manualProxyHostname; int proxyPort = o.manualProxyPort == -1 ? systemHTTPConnectProxy.getPort() : o.manualProxyPort; - logger_.fine("Proxy: " + proxyHostname + ":" << proxyPort + "\n"); + logger_.fine("Proxy: " + proxyHostname + ":" + proxyPort + "\n"); proxyConnectionFactories.add(new HTTPConnectProxiedConnectionFactory(networkFactories.getDomainNameResolver(), networkFactories.getConnectionFactory(), networkFactories.getTimerFactory(), proxyHostname, proxyPort, o.httpTrafficFilter)); useDirectConnection = false; break; @@ -244,7 +246,7 @@ public class CoreClient { Vector<ConnectionFactory> connectionFactories = new Vector<ConnectionFactory>(proxyConnectionFactories); if (useDirectConnection) { connectionFactories.add(networkFactories.getConnectionFactory()); - }*/ + } String host = (o.manualHostname == null || o.manualHostname.isEmpty()) ? jid_.getDomain() : o.manualHostname; int port = o.manualPort; @@ -292,9 +294,7 @@ public class CoreClient { } private void bindSessionToStream() { - //TO PORT - //session_ = ClientSession::create(jid_, sessionStream_, networkFactories.getIDNConverter(), networkFactories.getCryptoProvider()); - session_ = ClientSession.create(jid_, sessionStream_, new ICUConverter(), new JavaCryptoProvider()); + session_ = ClientSession.create(jid_, sessionStream_, networkFactories.getIDNConverter(), networkFactories.getCryptoProvider()); session_.setCertificateTrustChecker(certificateTrustChecker); session_.setUseStreamCompression(options.useStreamCompression); session_.setAllowPLAINOverNonTLS(options.allowPLAINWithoutTLS); diff --git a/src/com/isode/stroke/network/CachingDomainNameResolver.java b/src/com/isode/stroke/network/CachingDomainNameResolver.java new file mode 100644 index 0000000..166de7a --- /dev/null +++ b/src/com/isode/stroke/network/CachingDomainNameResolver.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2012-2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import com.isode.stroke.network.DomainNameResolver; +import com.isode.stroke.network.StaticDomainNameResolver; +import com.isode.stroke.eventloop.EventLoop; + +/* + * FIXME: Does not do any caching yet. + */ +public class CachingDomainNameResolver extends DomainNameResolver { + + private DomainNameResolver realResolver; + + public CachingDomainNameResolver(DomainNameResolver realResolver, EventLoop eventLoop) { + this.realResolver = realResolver; + } + + public DomainNameServiceQuery createServiceQuery(final String serviceLookupPrefix, final String domain) { + //TODO: Cache + return realResolver.createServiceQuery(serviceLookupPrefix, domain); + } + + public DomainNameAddressQuery createAddressQuery(final String name) { + //TODO: Cache + return realResolver.createAddressQuery(name); + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/ChainedConnector.java b/src/com/isode/stroke/network/ChainedConnector.java new file mode 100644 index 0000000..b61133c --- /dev/null +++ b/src/com/isode/stroke/network/ChainedConnector.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2011-2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import java.util.Vector; +import java.util.logging.Logger; +import com.isode.stroke.signals.Signal2; +import com.isode.stroke.signals.Slot2; +import com.isode.stroke.signals.SignalConnection; +import com.isode.stroke.network.Connection; + +public class ChainedConnector { + + private String hostname = ""; + private int port; + private String serviceLookupPrefix; + private DomainNameResolver resolver; + private Vector<ConnectionFactory> connectionFactories = new Vector<ConnectionFactory>(); + private TimerFactory timerFactory; + private int timeoutMilliseconds; + private Vector<ConnectionFactory> connectionFactoryQueue = new Vector<ConnectionFactory>(); + private Connector currentConnector; + private com.isode.stroke.base.Error lastError; + private Logger logger_ = Logger.getLogger(this.getClass().getName()); + private SignalConnection onConnectFinishedConnection; + + public ChainedConnector(final String hostname, int port, final String serviceLookupPrefix, DomainNameResolver resolver, final Vector<ConnectionFactory> connectionFactories, TimerFactory timer) { + this.hostname = hostname; + this.port = port; + this.serviceLookupPrefix = serviceLookupPrefix; + this.resolver = resolver; + this.connectionFactories = connectionFactories; + this.timerFactory = timer; + this.timeoutMilliseconds = 0; + } + + protected void finalize() throws Throwable { + try { + if (currentConnector != null) { + onConnectFinishedConnection.disconnect(); + currentConnector.stop(); + currentConnector = null; + } + } + finally { + super.finalize(); + } + } + + public void setTimeoutMilliseconds(int milliseconds) { + timeoutMilliseconds = milliseconds; + } + + public void start() { + logger_.fine("Starting queued connector for " + hostname + "\n"); + + connectionFactoryQueue = new Vector<ConnectionFactory>(connectionFactories); + tryNextConnectionFactory(); + } + + public void stop() { + if (currentConnector != null) { + onConnectFinishedConnection.disconnect(); + currentConnector.stop(); + currentConnector = null; + } + finish((Connection)null, (com.isode.stroke.base.Error)null); + } + + public final Signal2<Connection, com.isode.stroke.base.Error> onConnectFinished = new Signal2<Connection, com.isode.stroke.base.Error>(); + + private void finish(Connection connection, com.isode.stroke.base.Error error) { + onConnectFinished.emit(connection, error); + } + + private void tryNextConnectionFactory() { + assert(currentConnector == null); + if (connectionFactoryQueue.isEmpty()) { + logger_.fine("No more connection factories\n"); + finish((Connection)null, lastError); + } + else { + ConnectionFactory connectionFactory = connectionFactoryQueue.firstElement(); + logger_.fine("Trying next connection factory: " + connectionFactory + "\n"); + connectionFactoryQueue.remove(connectionFactoryQueue.firstElement()); + currentConnector = Connector.create(hostname, port, serviceLookupPrefix, resolver, connectionFactory, timerFactory); + currentConnector.setTimeoutMilliseconds(timeoutMilliseconds); + onConnectFinishedConnection = currentConnector.onConnectFinished.connect(new Slot2<Connection, com.isode.stroke.base.Error>() { + @Override + public void call(Connection connection, com.isode.stroke.base.Error error) { + handleConnectorFinished(connection, error); + } + }); + currentConnector.start(); + } + } + + private void handleConnectorFinished(Connection connection, com.isode.stroke.base.Error error) { + logger_.fine("Connector finished\n"); + onConnectFinishedConnection.disconnect(); + lastError = error; + currentConnector = null; + if (connection != null) { + finish(connection, error); + } + else { + tryNextConnectionFactory(); + } + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/ConnectionServer.java b/src/com/isode/stroke/network/ConnectionServer.java new file mode 100644 index 0000000..002e208 --- /dev/null +++ b/src/com/isode/stroke/network/ConnectionServer.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import com.isode.stroke.signals.Signal1; + +public abstract class ConnectionServer { + + public enum Error { + Conflict, + UnknownError + }; + + public abstract HostAddressPort getAddressPort(); + + public abstract Error tryStart(); // FIXME: This should become the new start + + public abstract void start(); + + public abstract void stop(); + + public final Signal1<Connection> onNewConnection = new Signal1<Connection>(); +} diff --git a/src/com/isode/stroke/network/ConnectionServerFactory.java b/src/com/isode/stroke/network/ConnectionServerFactory.java new file mode 100644 index 0000000..a23291b --- /dev/null +++ b/src/com/isode/stroke/network/ConnectionServerFactory.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +public interface ConnectionServerFactory { + + public ConnectionServer createConnectionServer(int port); + + public ConnectionServer createConnectionServer(final HostAddress hostAddress, int port); +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/Connector.java b/src/com/isode/stroke/network/Connector.java index 13a2bc3..6033a89 100644 --- a/src/com/isode/stroke/network/Connector.java +++ b/src/com/isode/stroke/network/Connector.java @@ -32,7 +32,6 @@ public class Connector { assert serviceQuery == null; assert timer == null; queriedAllServices = false; - serviceQueryResults = new ArrayList<Result>(); if (timeoutMilliseconds > 0) { timer = timerFactory.createTimer(timeoutMilliseconds); @@ -41,10 +40,9 @@ public class Connector { handleTimeout(); } }); - timer.start(); } if (serviceLookupPrefix != null) { - serviceQuery = resolver.createServiceQuery(serviceLookupPrefix + hostname); + serviceQuery = resolver.createServiceQuery(serviceLookupPrefix, hostname); serviceQuery.onResult.connect(new Slot1<Collection<DomainNameServiceQuery.Result>>() { public void call(Collection<Result> p1) { handleServiceQueryResult(p1); @@ -52,6 +50,12 @@ public class Connector { }); serviceQuery.run(); } + else if (new HostAddress(hostname).isValid()) { + // hostname is already a valid address; skip name lookup. + foundSomeDNS = true; + addressQueryResults.add(new HostAddress(hostname)); + tryNextAddress(); + } else { queryAddress(hostname); } @@ -70,6 +74,9 @@ public class Connector { this.timerFactory = timerFactory; this.port = port; this.serviceLookupPrefix = serviceLookupPrefix; + this.timeoutMilliseconds = 0; + this.queriedAllServices = true; + this.foundSomeDNS = false; } private void handleServiceQueryResult(Collection<Result> result) { @@ -161,10 +168,17 @@ public class Connector { }); currentConnection.connect(target); + if (timer != null) { + timer.start(); + } } private void handleConnectionConnectFinished(boolean error) { //std::cout << "Connector::handleConnectionConnectFinished() " << error << std::endl; + if (timer != null) { + timer.stop(); + timer = null; + } currentConnectionConnectFinishedConnection.disconnect(); if (error) { currentConnection = null; @@ -206,7 +220,8 @@ public class Connector { } private void handleTimeout() { - finish(null); + //SWIFT_LOG(debug) << "Timeout" << std::endl; + handleConnectionConnectFinished(true); } @Override @@ -223,9 +238,9 @@ public class Connector { private int timeoutMilliseconds = 0; private Timer timer; private DomainNameServiceQuery serviceQuery; - private ArrayList<DomainNameServiceQuery.Result> serviceQueryResults; + private ArrayList<DomainNameServiceQuery.Result> serviceQueryResults = new ArrayList<DomainNameServiceQuery.Result>(); private DomainNameAddressQuery addressQuery; - private ArrayList<HostAddress> addressQueryResults; + private ArrayList<HostAddress> addressQueryResults = new ArrayList<HostAddress>(); private boolean queriedAllServices = true; private Connection currentConnection; private SignalConnection currentConnectionConnectFinishedConnection; diff --git a/src/com/isode/stroke/network/DomainNameResolver.java b/src/com/isode/stroke/network/DomainNameResolver.java index 9c02aa3..9a99690 100644 --- a/src/com/isode/stroke/network/DomainNameResolver.java +++ b/src/com/isode/stroke/network/DomainNameResolver.java @@ -11,20 +11,7 @@ package com.isode.stroke.network; public abstract class DomainNameResolver { - public abstract DomainNameServiceQuery createServiceQuery(String name); - public abstract DomainNameAddressQuery createAddressQuery(String name); - protected String getNormalized(String domain) { - return domain; - //FIXME: port idna -// char* output; -// if (idna_to_ascii_8z(domain.getUTF8Data(), &output, 0) == IDNA_SUCCESS) { -// String result(output); -// free(output); -// return result; -// } -// else { -// return domain; -// } - } + public abstract DomainNameServiceQuery createServiceQuery(String serviceLookupPrefix, String domain); + public abstract DomainNameAddressQuery createAddressQuery(String name); } diff --git a/src/com/isode/stroke/network/DomainNameServiceQuery.java b/src/com/isode/stroke/network/DomainNameServiceQuery.java index 0d02112..05e4d32 100644 --- a/src/com/isode/stroke/network/DomainNameServiceQuery.java +++ b/src/com/isode/stroke/network/DomainNameServiceQuery.java @@ -13,7 +13,7 @@ import java.util.Collection; public abstract class DomainNameServiceQuery { - public class Result { + public static class Result { public Result() { hostname = ""; diff --git a/src/com/isode/stroke/network/DummyConnection.java b/src/com/isode/stroke/network/DummyConnection.java new file mode 100644 index 0000000..51018ef --- /dev/null +++ b/src/com/isode/stroke/network/DummyConnection.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import com.isode.stroke.signals.Signal1; +import com.isode.stroke.eventloop.EventOwner; +import com.isode.stroke.eventloop.EventLoop; +import com.isode.stroke.eventloop.Event; +import com.isode.stroke.base.SafeByteArray; + +public class DummyConnection extends Connection implements EventOwner { + + public DummyConnection(EventLoop eventLoop) { + this.eventLoop = eventLoop; + } + + public void listen() { + assert(false); + } + + public void connect(final HostAddressPort port) { + assert(false); + } + + public void disconnect() { + //assert(false); + } + + public void write(final SafeByteArray data) { + eventLoop.postEvent(new Event.Callback() { + @Override + public void run() { + onDataWritten.emit(); + } + }); + onDataSent.emit(data); + } + + public void receive(final SafeByteArray data) { + eventLoop.postEvent(new Event.Callback() { + @Override + public void run() { + onDataRead.emit(data); + } + }); + } + + public HostAddressPort getLocalAddress() { + return localAddress; + } + + public Signal1<SafeByteArray> onDataSent = new Signal1<SafeByteArray>(); + + public EventLoop eventLoop; + public HostAddressPort localAddress; +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/DummyConnectionFactory.java b/src/com/isode/stroke/network/DummyConnectionFactory.java new file mode 100644 index 0000000..e8ff755 --- /dev/null +++ b/src/com/isode/stroke/network/DummyConnectionFactory.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import com.isode.stroke.eventloop.EventLoop; + +public class DummyConnectionFactory implements ConnectionFactory { + + private EventLoop eventLoop; + + public DummyConnectionFactory(EventLoop eventLoop) { + this.eventLoop = eventLoop; + } + + public Connection createConnection() { + return new DummyConnection(eventLoop); + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/DummyConnectionServer.java b/src/com/isode/stroke/network/DummyConnectionServer.java new file mode 100644 index 0000000..7cfb27c --- /dev/null +++ b/src/com/isode/stroke/network/DummyConnectionServer.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2010-2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import com.isode.stroke.eventloop.EventOwner; +import com.isode.stroke.eventloop.EventLoop; + +public class DummyConnectionServer extends ConnectionServer implements EventOwner { + + private HostAddressPort localAddressPort; + + public DummyConnectionServer(EventLoop eventLoop, int port) { + this.localAddressPort = new HostAddressPort(new HostAddress(), port); + } + + public DummyConnectionServer(EventLoop eventLoop, final HostAddress hostAddress, int port) { + this.localAddressPort = new HostAddressPort(hostAddress, port); + } + + public HostAddressPort getAddressPort() { + return localAddressPort; + } + + public Error tryStart() { + return null; + } + + public void start() { + + } + + public void stop() { + + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/DummyConnectionServerFactory.java b/src/com/isode/stroke/network/DummyConnectionServerFactory.java new file mode 100644 index 0000000..8d5c6fe --- /dev/null +++ b/src/com/isode/stroke/network/DummyConnectionServerFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2014 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import com.isode.stroke.eventloop.EventLoop; + +public class DummyConnectionServerFactory implements ConnectionServerFactory { + + private EventLoop eventLoop; + + public DummyConnectionServerFactory(EventLoop eventLoop) { + this.eventLoop = eventLoop; + } + + public ConnectionServer createConnectionServer(int port) { + return new DummyConnectionServer(eventLoop, port); + } + + public ConnectionServer createConnectionServer(final HostAddress hostAddress, int port) { + return new DummyConnectionServer(eventLoop, hostAddress, port); + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/DummyTimerFactory.java b/src/com/isode/stroke/network/DummyTimerFactory.java new file mode 100644 index 0000000..f3d083f --- /dev/null +++ b/src/com/isode/stroke/network/DummyTimerFactory.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import java.util.List; +import java.util.ArrayList; + +public class DummyTimerFactory implements TimerFactory { + + private int currentTime; + private List<DummyTimer> timers = new ArrayList<DummyTimer>(); + + public class DummyTimer extends Timer { + + public DummyTimer(long timeout, DummyTimerFactory factory) { + this.timeout = timeout; + this.factory = factory; + this.isRunning = false; + this.startTime = 0; + } + + public void start() { + isRunning = true; + startTime = factory.currentTime; + } + + public void stop() { + isRunning = false; + } + + public long getAlarmTime() { + return startTime + timeout; + } + + public long timeout; + public DummyTimerFactory factory; + public boolean isRunning; + public long startTime; + }; + + public DummyTimerFactory() { + this.currentTime = 0; + } + + public Timer createTimer(long milliseconds) { + DummyTimer timer = new DummyTimer(milliseconds, this); + timers.add(timer); + return timer; + } + + public void setTime(int time) { + assert(time > currentTime); + for(DummyTimer timer : timers) { + if (timer.getAlarmTime() > currentTime && timer.getAlarmTime() <= time && timer.isRunning) { + timer.onTick.emit(); + } + } + currentTime = time; + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/EnvironmentProxyProvider.java b/src/com/isode/stroke/network/EnvironmentProxyProvider.java new file mode 100644 index 0000000..b12fe98 --- /dev/null +++ b/src/com/isode/stroke/network/EnvironmentProxyProvider.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import java.util.logging.Logger; + +public class EnvironmentProxyProvider implements ProxyProvider { + + public EnvironmentProxyProvider() { + socksProxy = getFromEnv("all_proxy", "socks"); + httpProxy = getFromEnv("http_proxy", "http"); + logger_.fine("Environment: SOCKS5 => " + socksProxy.toString() + "; HTTP Connect => " + httpProxy.toString() + "\n"); + } + + public HostAddressPort getHTTPConnectProxy() { + return httpProxy; + } + + public HostAddressPort getSOCKS5Proxy() { + return socksProxy; + } + + private HostAddressPort getFromEnv(final String envVarName, String proxyProtocol) { + String envVar = null; + String address = ""; + int port = 0; + + envVar = System.getenv(envVarName); + + proxyProtocol += "://"; + address = envVar != null ? envVar : "0.0.0.0"; + if(envVar != null && address.substring(0, proxyProtocol.length()).equals(proxyProtocol)) { + address = address.substring(proxyProtocol.length(), address.length()); + port = Integer.parseInt(address.substring(address.indexOf(':') + 1, address.length())); + address = address.substring(0, address.indexOf(':')); + } + + return new HostAddressPort(new HostAddress(address), port); + } + + private HostAddressPort socksProxy; + private HostAddressPort httpProxy; + private final Logger logger_ = Logger.getLogger(this.getClass().getName()); +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/FakeConnection.java b/src/com/isode/stroke/network/FakeConnection.java new file mode 100644 index 0000000..ebcf239 --- /dev/null +++ b/src/com/isode/stroke/network/FakeConnection.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2010-2014 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import com.isode.stroke.eventloop.EventOwner; +import com.isode.stroke.eventloop.EventLoop; +import com.isode.stroke.eventloop.Event; +import com.isode.stroke.base.SafeByteArray; +import java.util.Vector; + +public class FakeConnection extends Connection { + + public enum State { + Initial, + Connecting, + Connected, + Disconnected, + DisconnectedWithError + }; + + public FakeConnection(EventLoop eventLoop) { + this.eventLoop = eventLoop; + this.state = State.Initial; + this.delayConnect = false; + } + + public void listen() { + assert(false); + } + + public HostAddressPort getLocalAddress() { + return new HostAddressPort(); + } + + public void setError(final Error e) { + error = e; + state = State.DisconnectedWithError; + if (connectedTo != null) { + eventLoop.postEvent(new Event.Callback() { + @Override + public void run() { + onDisconnected.emit(error); + } + }); + } + } + + public void connect(final HostAddressPort address) { + if (delayConnect) { + state = State.Connecting; + } + else { + if (error == null) { + connectedTo = address; + state = State.Connected; + } + else { + state = State.DisconnectedWithError; + } + eventLoop.postEvent(new Event.Callback() { + @Override + public void run() { + onConnectFinished.emit(error != null ? true : false); + } + }); + } + } + + public void disconnect() { + if (error == null) { + state = State.Disconnected; + } + else { + state = State.DisconnectedWithError; + } + connectedTo = null; + eventLoop.postEvent(new Event.Callback() { + @Override + public void run() { + onDisconnected.emit(error); + } + }); + } + + public void write(final SafeByteArray data) { + dataWritten.add(data); + } + + public void setDelayConnect() { + delayConnect = true; + } + + public EventLoop eventLoop; + public HostAddressPort connectedTo; + public Vector<SafeByteArray> dataWritten = new Vector<SafeByteArray>(); + public Error error; + public State state; + public boolean delayConnect; +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/HTTPConnectProxiedConnection.java b/src/com/isode/stroke/network/HTTPConnectProxiedConnection.java new file mode 100644 index 0000000..d9a6cee --- /dev/null +++ b/src/com/isode/stroke/network/HTTPConnectProxiedConnection.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +/* + * Copyright (c) 2011-2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import com.isode.stroke.base.SafeByteArray; +import com.isode.stroke.stringcodecs.Base64; +import java.util.Vector; + +public class HTTPConnectProxiedConnection extends ProxiedConnection { + + private SafeByteArray authID_; + private SafeByteArray authPassword_; + private HTTPTrafficFilter trafficFilter_; + private StringBuffer httpResponseBuffer_ = new StringBuffer(""); + + public static class Pair { + String a; + String b; + + public Pair(String a, String b) { this.a = a; this.b = b; } + } + + public static HTTPConnectProxiedConnection create(DomainNameResolver resolver, ConnectionFactory connectionFactory, TimerFactory timerFactory, final String proxyHost, int proxyPort, final SafeByteArray authID, final SafeByteArray authPassword) { + return new HTTPConnectProxiedConnection(resolver, connectionFactory, timerFactory, proxyHost, proxyPort, authID, authPassword); + } + + public void setHTTPTrafficFilter(HTTPTrafficFilter trafficFilter) { + trafficFilter_ = trafficFilter; + } + + private HTTPConnectProxiedConnection(DomainNameResolver resolver, ConnectionFactory connectionFactory, TimerFactory timerFactory, final String proxyHost, int proxyPort, final SafeByteArray authID, final SafeByteArray authPassword) { + super(resolver, connectionFactory, timerFactory, proxyHost, proxyPort); + this.authID_ = authID; + this.authPassword_ = authPassword; + } + + protected void initializeProxy() { + StringBuffer connect = new StringBuffer(); + connect.append("CONNECT ").append(getServer().getAddress().toString()).append(":").append(getServer().getPort()).append(" HTTP/1.1\r\n"); + SafeByteArray data = new SafeByteArray(connect.toString()); + if (!authID_.isEmpty() && !authPassword_.isEmpty()) { + data.append(new SafeByteArray("Proxy-Authorization: Basic ")); + SafeByteArray credentials = authID_; + credentials.append(new SafeByteArray(":")); + credentials.append(authPassword_); + data.append(Base64.encode(credentials)); + data.append(new SafeByteArray("\r\n")); + } + data.append(new SafeByteArray("\r\n")); + //SWIFT_LOG(debug) << "HTTP Proxy send headers: " << byteArrayToString(ByteArray(data.begin(), data.end())) << std::endl; + write(data); + } + + protected void handleProxyInitializeData(SafeByteArray data) { + String dataString = data.toString(); + //SWIFT_LOG(debug) << data << std::endl; + httpResponseBuffer_.append(dataString); + + String statusLine = ""; + Vector<Pair> headerFields = new Vector<Pair>(); + + int headerEnd = httpResponseBuffer_.indexOf("\r\n\r\n", 0); + if (headerEnd == -1) { + if ((httpResponseBuffer_.length() > 4) && !(httpResponseBuffer_.substring(0, 4).equals("HTTP"))) { + setProxyInitializeFinished(false); + } + return; + } + + parseHTTPHeader(httpResponseBuffer_.substring(0, headerEnd), statusLine, headerFields); + + if (trafficFilter_ != null) { + Vector<Pair> newHeaderFields = trafficFilter_.filterHTTPResponseHeader(headerFields); + if (!newHeaderFields.isEmpty()) { + StringBuffer statusLines = new StringBuffer(); + statusLines.append("CONNECT ").append(getServer().getAddress().toString()).append(":").append(getServer().getPort()); + sendHTTPRequest(statusLines.toString(), newHeaderFields); + return; + } + } + + String[] tmp = statusLine.split(" "); + if (tmp.length > 1) { + try { + int status = Integer.parseInt(tmp[1]); + //SWIFT_LOG(debug) << "Proxy Status: " << status << std::endl; + if (status / 100 == 2) { // all 2XX states are OK + setProxyInitializeFinished(true); + } + else { + //SWIFT_LOG(debug) << "HTTP Proxy returned an error: " << httpResponseBuffer_ << std::endl; + setProxyInitializeFinished(false); + } + } + catch (NumberFormatException e) { + //SWIFT_LOG(warning) << "Unexpected response: " << tmp[1] << std::endl; + setProxyInitializeFinished(false); + } + } + else { + setProxyInitializeFinished(false); + } + httpResponseBuffer_ = new StringBuffer(""); + } + + private void sendHTTPRequest(final String statusLine, final Vector<Pair> headerFields) { + StringBuffer request = new StringBuffer(); + + request.append(statusLine).append("\r\n"); + for (final Pair field : headerFields) { + request.append(field.a).append(":").append(field.b).append("\r\n"); + } + request.append("\r\n"); + write(new SafeByteArray(request.toString())); + } + + private void parseHTTPHeader(final String data, String statusLine, Vector<Pair> headerFields) { + StringBuffer dataStream = new StringBuffer(data); + + // parse status line + statusLine = dataStream.toString(); + + // parse fields + String headerLine = dataStream.toString(); + int splitIndex; + while (headerLine != null && !headerLine.equals("\r")) { + splitIndex = headerLine.indexOf(':', 0); + if (splitIndex != -1) { + headerFields.add(new Pair(headerLine.substring(0, splitIndex), headerLine.substring(splitIndex + 1))); + } + } + } + +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/HTTPConnectProxiedConnectionFactory.java b/src/com/isode/stroke/network/HTTPConnectProxiedConnectionFactory.java new file mode 100644 index 0000000..1f45915 --- /dev/null +++ b/src/com/isode/stroke/network/HTTPConnectProxiedConnectionFactory.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2012-2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import com.isode.stroke.base.SafeByteArray; + +public class HTTPConnectProxiedConnectionFactory implements ConnectionFactory { + + private DomainNameResolver resolver_; + private ConnectionFactory connectionFactory_; + private TimerFactory timerFactory_; + private String proxyHost_; + private int proxyPort_; + private SafeByteArray authID_; + private SafeByteArray authPassword_; + private HTTPTrafficFilter httpTrafficFilter_; + + public HTTPConnectProxiedConnectionFactory(DomainNameResolver resolver, ConnectionFactory connectionFactory, TimerFactory timerFactory, final String proxyHost, int proxyPort) { + this(resolver, connectionFactory, timerFactory, proxyHost, proxyPort, null); + } + + public HTTPConnectProxiedConnectionFactory(DomainNameResolver resolver, ConnectionFactory connectionFactory, TimerFactory timerFactory, final String proxyHost, int proxyPort, HTTPTrafficFilter httpTrafficFilter) { + resolver_ = resolver; + connectionFactory_ = connectionFactory; + timerFactory_ = timerFactory; + proxyHost_ = proxyHost; + proxyPort_ = proxyPort; + authID_ = new SafeByteArray(""); + authPassword_ = new SafeByteArray(""); + httpTrafficFilter_ = httpTrafficFilter; + } + + public HTTPConnectProxiedConnectionFactory(DomainNameResolver resolver, ConnectionFactory connectionFactory, TimerFactory timerFactory, final String proxyHost, int proxyPort, final SafeByteArray authID, final SafeByteArray authPassword) { + this(resolver, connectionFactory, timerFactory, proxyHost, proxyPort, authID, authPassword, null); + } + + public HTTPConnectProxiedConnectionFactory(DomainNameResolver resolver, ConnectionFactory connectionFactory, TimerFactory timerFactory, final String proxyHost, int proxyPort, final SafeByteArray authID, final SafeByteArray authPassword, HTTPTrafficFilter httpTrafficFilter) { + resolver_ = resolver; + connectionFactory_ = connectionFactory; + timerFactory_ = timerFactory; + proxyHost_ = proxyHost; + proxyPort_ = proxyPort; + authID_ = authID; + authPassword_ = authPassword; + httpTrafficFilter_ = httpTrafficFilter; + } + + public Connection createConnection() { + HTTPConnectProxiedConnection proxyConnection = HTTPConnectProxiedConnection.create(resolver_, connectionFactory_, timerFactory_, proxyHost_, proxyPort_, authID_, authPassword_); + proxyConnection.setHTTPTrafficFilter(httpTrafficFilter_); + return proxyConnection; + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/HTTPTrafficFilter.java b/src/com/isode/stroke/network/HTTPTrafficFilter.java new file mode 100644 index 0000000..86f0659 --- /dev/null +++ b/src/com/isode/stroke/network/HTTPTrafficFilter.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import java.util.Vector; + +public interface HTTPTrafficFilter { + + /** + * @brief This method is called by the HTTPConnectPRoxiedConnection on every incoming HTTP response. + * It can be used to insert additional HTTP requests into the HTTP CONNECT proxy initalization process. + * @return A vector of HTTP header fields to use in a new request. If an empty vector is returned, + * no new request will be send and the normal proxy logic continues. + */ + public Vector<HTTPConnectProxiedConnection.Pair> filterHTTPResponseHeader(final Vector<HTTPConnectProxiedConnection.Pair> responseHeader); +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/HostAddress.java b/src/com/isode/stroke/network/HostAddress.java index c1b3600..a8eea1b 100644 --- a/src/com/isode/stroke/network/HostAddress.java +++ b/src/com/isode/stroke/network/HostAddress.java @@ -10,6 +10,7 @@ package com.isode.stroke.network; import java.net.InetAddress; +import java.net.UnknownHostException; public class HostAddress { @@ -17,12 +18,39 @@ public class HostAddress { address_ = null; } + public HostAddress(String address) { + try { + address_ = InetAddress.getByName(address); + } + catch (UnknownHostException e) { + address_ = null; + } + } + public HostAddress(InetAddress address) { address_ = address; } - /* public HostAddress(const String&); - public HostAddress(const unsigned char* address, int length); - public HostAddress(const boost::asio::ip::address& address);*/ + + public HostAddress(final char[] address, int length) { + try { + assert(length == 4 || length == 16); + byte[] data = new byte[length]; + if (length == 4) { + for (int i = 0; i < length; ++i) { + data[i] = (byte)address[i]; + } + } + else { + for (int i = 0; i < length; ++i) { + data[i] = (byte)address[i]; + } + } + address_ = InetAddress.getByAddress(data); + } catch (UnknownHostException e) { + address_ = null; + } + } + @Override public String toString() { @@ -57,5 +85,5 @@ public class HostAddress { return address_; } - private final InetAddress address_; + private InetAddress address_; } diff --git a/src/com/isode/stroke/network/HostAddressPort.java b/src/com/isode/stroke/network/HostAddressPort.java index c7d46a4..f81c4a3 100644 --- a/src/com/isode/stroke/network/HostAddressPort.java +++ b/src/com/isode/stroke/network/HostAddressPort.java @@ -11,9 +11,12 @@ package com.isode.stroke.network; public class HostAddressPort { + public HostAddressPort() { + this(new HostAddress(), -1); + } + public HostAddressPort(HostAddress address) { - address_ = address; - port_ = -1; + this(address, -1); } public HostAddressPort(HostAddress address, int port) { diff --git a/src/com/isode/stroke/network/JavaNetworkFactories.java b/src/com/isode/stroke/network/JavaNetworkFactories.java index 15860e0..b71c220 100644 --- a/src/com/isode/stroke/network/JavaNetworkFactories.java +++ b/src/com/isode/stroke/network/JavaNetworkFactories.java @@ -9,6 +9,8 @@ import com.isode.stroke.crypto.JavaCryptoProvider; import com.isode.stroke.eventloop.EventLoop; import com.isode.stroke.tls.PlatformTLSFactories; import com.isode.stroke.tls.TLSContextFactory; +import com.isode.stroke.idn.IDNConverter; +import com.isode.stroke.idn.ICUConverter; public class JavaNetworkFactories implements NetworkFactories { @@ -16,9 +18,11 @@ public class JavaNetworkFactories implements NetworkFactories { eventLoop_ = eventLoop; timers_ = new JavaTimerFactory(eventLoop_); connections_ = new JavaConnectionFactory(eventLoop_); - dns_ = new PlatformDomainNameResolver(eventLoop_); platformTLSFactories_ = new PlatformTLSFactories(); cryptoProvider_ = new JavaCryptoProvider(); + idnConverter_ = new ICUConverter(); + dns_ = new PlatformDomainNameResolver(idnConverter_, eventLoop_); + proxyProvider_ = new JavaProxyProvider(); } public TimerFactory getTimerFactory() { @@ -36,16 +40,31 @@ public class JavaNetworkFactories implements NetworkFactories { public TLSContextFactory getTLSContextFactory() { return platformTLSFactories_.getTLSContextFactory(); } - + + public ProxyProvider getProxyProvider() { + return proxyProvider_; + } + + public EventLoop getEventLoop() { + return eventLoop_; + } + @Override public CryptoProvider getCryptoProvider() { return cryptoProvider_; } + @Override + public IDNConverter getIDNConverter() { + return idnConverter_; + } + private final EventLoop eventLoop_; private final JavaTimerFactory timers_; private final JavaConnectionFactory connections_; private final PlatformDomainNameResolver dns_; private final PlatformTLSFactories platformTLSFactories_; + private final ProxyProvider proxyProvider_; private final CryptoProvider cryptoProvider_; + private final IDNConverter idnConverter_; } diff --git a/src/com/isode/stroke/network/JavaProxyProvider.java b/src/com/isode/stroke/network/JavaProxyProvider.java new file mode 100644 index 0000000..e2804a3 --- /dev/null +++ b/src/com/isode/stroke/network/JavaProxyProvider.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +public class JavaProxyProvider implements ProxyProvider { + + private EnvironmentProxyProvider environmentProxyProvider; + + public JavaProxyProvider() { + + } + + public HostAddressPort getHTTPConnectProxy() { + HostAddressPort proxy; + proxy = environmentProxyProvider.getHTTPConnectProxy(); + if(proxy.isValid()) { + return proxy; + } + return new HostAddressPort(new HostAddress(), 0); + } + + public HostAddressPort getSOCKS5Proxy() { + HostAddressPort proxy; + proxy = environmentProxyProvider.getSOCKS5Proxy(); + if(proxy.isValid()) { + return proxy; + } + return new HostAddressPort(new HostAddress(), 0); + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/NATPortMapping.java b/src/com/isode/stroke/network/NATPortMapping.java new file mode 100644 index 0000000..78da3a2 --- /dev/null +++ b/src/com/isode/stroke/network/NATPortMapping.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2011 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +public class NATPortMapping { + + private int publicPort; + private int localPort; + private Protocol protocol; + private int leaseInSeconds; + + public enum Protocol { + TCP, + UDP + }; + + public NATPortMapping(int localPort, int publicPort) { + this(localPort, publicPort, Protocol.TCP, 60 * 60 * 24); + } + + public NATPortMapping(int localPort, int publicPort, Protocol protocol) { + this(localPort, publicPort, protocol, 60 * 60 * 24); + } + + public NATPortMapping(int localPort, int publicPort, Protocol protocol, int leaseInSeconds) { + this.localPort = localPort; + this.publicPort = publicPort; + this.protocol = protocol; + this.leaseInSeconds = leaseInSeconds; + } + + public int getPublicPort() { + return publicPort; + } + + public int getLocalPort() { + return localPort; + } + + public Protocol getProtocol() { + return protocol; + } + + public int getLeaseInSeconds() { + return leaseInSeconds; + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/NATTraversalForwardPortRequest.java b/src/com/isode/stroke/network/NATTraversalForwardPortRequest.java new file mode 100644 index 0000000..327c243 --- /dev/null +++ b/src/com/isode/stroke/network/NATTraversalForwardPortRequest.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2011 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import com.isode.stroke.signals.Signal1; + +public abstract class NATTraversalForwardPortRequest { + + public abstract void start(); + public abstract void stop(); + + public final Signal1<NATPortMapping> onResult = new Signal1<NATPortMapping>(); +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/NATTraversalGetPublicIPRequest.java b/src/com/isode/stroke/network/NATTraversalGetPublicIPRequest.java new file mode 100644 index 0000000..612536f --- /dev/null +++ b/src/com/isode/stroke/network/NATTraversalGetPublicIPRequest.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import com.isode.stroke.signals.Signal1; + +public abstract class NATTraversalGetPublicIPRequest { + + public abstract void start(); + public abstract void stop(); + + public final Signal1<HostAddress> onResult = new Signal1<HostAddress>(); +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/NATTraversalInterface.java b/src/com/isode/stroke/network/NATTraversalInterface.java new file mode 100644 index 0000000..5ac86a2 --- /dev/null +++ b/src/com/isode/stroke/network/NATTraversalInterface.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2011-2015 Isode Limited. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +public interface NATTraversalInterface { + + public boolean isAvailable(); + + public HostAddress getPublicIP(); + public NATPortMapping addPortForward(int localPort, int publicPort); + public boolean removePortForward(final NATPortMapping map); +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/NATTraversalRemovePortForwardingRequest.java b/src/com/isode/stroke/network/NATTraversalRemovePortForwardingRequest.java new file mode 100644 index 0000000..c4ed0d0 --- /dev/null +++ b/src/com/isode/stroke/network/NATTraversalRemovePortForwardingRequest.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2011 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import com.isode.stroke.signals.Signal1; + +public abstract class NATTraversalRemovePortForwardingRequest { + + public static class PortMapping { + public enum Protocol { + TCP, + UDP + }; + + public int publicPort; + public int localPort; + public Protocol protocol; + public long leaseInSeconds; + }; + + public abstract void start(); + public abstract void stop(); + + public final Signal1<Boolean /* failure */> onResult = new Signal1<Boolean>(); +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/NATTraverser.java b/src/com/isode/stroke/network/NATTraverser.java new file mode 100644 index 0000000..9aa5e03 --- /dev/null +++ b/src/com/isode/stroke/network/NATTraverser.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2011-2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import com.isode.stroke.signals.Signal1; + +public interface NATTraverser { + + public NATTraversalGetPublicIPRequest createGetPublicIPRequest(); + public NATTraversalForwardPortRequest createForwardPortRequest(int localPort, int publicPort); + public NATTraversalRemovePortForwardingRequest createRemovePortForwardingRequest(int localPort, int publicPort); +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/NetworkEnvironment.java b/src/com/isode/stroke/network/NetworkEnvironment.java new file mode 100644 index 0000000..86547ef --- /dev/null +++ b/src/com/isode/stroke/network/NetworkEnvironment.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +/* + * Copyright (c) 2011 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import java.util.Vector; + +public abstract class NetworkEnvironment { + + public abstract Vector<NetworkInterface> getNetworkInterfaces(); + + public HostAddress getLocalAddress() { + Vector<NetworkInterface> networkInterfaces = getNetworkInterfaces(); + for (final NetworkInterface iface : networkInterfaces) { + if (!iface.isLoopback()) { + for (final HostAddress address : iface.getAddresses()) { + if (address.getInetAddress() != null) { + return address; + } + } + } + } + return new HostAddress(); + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/NetworkFactories.java b/src/com/isode/stroke/network/NetworkFactories.java index fe20214..2955a92 100644 --- a/src/com/isode/stroke/network/NetworkFactories.java +++ b/src/com/isode/stroke/network/NetworkFactories.java @@ -6,6 +6,8 @@ package com.isode.stroke.network; import com.isode.stroke.crypto.CryptoProvider; import com.isode.stroke.tls.TLSContextFactory; +import com.isode.stroke.idn.IDNConverter; +import com.isode.stroke.eventloop.EventLoop; public interface NetworkFactories { @@ -13,6 +15,8 @@ public interface NetworkFactories { ConnectionFactory getConnectionFactory(); DomainNameResolver getDomainNameResolver(); TLSContextFactory getTLSContextFactory(); + ProxyProvider getProxyProvider(); + EventLoop getEventLoop(); CryptoProvider getCryptoProvider(); - + IDNConverter getIDNConverter(); } diff --git a/src/com/isode/stroke/network/NetworkInterface.java b/src/com/isode/stroke/network/NetworkInterface.java new file mode 100644 index 0000000..5590837 --- /dev/null +++ b/src/com/isode/stroke/network/NetworkInterface.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import java.util.Vector; + +public class NetworkInterface { + + private String name = ""; + private boolean loopback; + private Vector<HostAddress> addresses = new Vector<HostAddress>(); + + public NetworkInterface(final String name, boolean loopback) { + this.name = name; + this.loopback = loopback; + } + + public void addAddress(final HostAddress address) { + addresses.add(address); + } + + public Vector<HostAddress> getAddresses() { + return addresses; + } + + public String getName() { + return name; + } + + public boolean isLoopback() { + return loopback; + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/NullNATTraversalInterface.java b/src/com/isode/stroke/network/NullNATTraversalInterface.java new file mode 100644 index 0000000..5cee969 --- /dev/null +++ b/src/com/isode/stroke/network/NullNATTraversalInterface.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2011 Isode Limited. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +public class NullNATTraversalInterface implements NATTraversalInterface { + + public boolean isAvailable() { + return true; + } + + public HostAddress getPublicIP() { + return null; + } + + public NATPortMapping addPortForward(int localPort, int publicPort) { + return null; + } + + public boolean removePortForward(final NATPortMapping map) { + return false; + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/NullNATTraverser.java b/src/com/isode/stroke/network/NullNATTraverser.java new file mode 100644 index 0000000..3e2b873 --- /dev/null +++ b/src/com/isode/stroke/network/NullNATTraverser.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2011 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import com.isode.stroke.eventloop.Event; +import com.isode.stroke.eventloop.EventLoop; + +public class NullNATTraverser implements NATTraverser { + + private EventLoop eventLoop; + + class NullNATTraversalGetPublicIPRequest extends NATTraversalGetPublicIPRequest { + + public NullNATTraversalGetPublicIPRequest(EventLoop eventLoop) { + this.eventLoop = eventLoop; + } + + public void start() { + eventLoop.postEvent(new Event.Callback() { + @Override + public void run() { + onResult.emit(null); + } + }); + } + + public void stop() { + } + + private EventLoop eventLoop; + }; + + class NullNATTraversalForwardPortRequest extends NATTraversalForwardPortRequest { + + public NullNATTraversalForwardPortRequest(EventLoop eventLoop) { + this.eventLoop = eventLoop; + } + + public void start() { + eventLoop.postEvent(new Event.Callback() { + @Override + public void run() { + onResult.emit(null); + } + }); + } + + public void stop() { + } + + private EventLoop eventLoop; + }; + + class NullNATTraversalRemovePortForwardingRequest extends NATTraversalRemovePortForwardingRequest { + + public NullNATTraversalRemovePortForwardingRequest(EventLoop eventLoop) { + this.eventLoop = eventLoop; + } + + public void start() { + eventLoop.postEvent(new Event.Callback() { + @Override + public void run() { + onResult.emit(true); + } + }); + } + + public void stop() { + } + + private EventLoop eventLoop; + }; + + public NullNATTraverser(EventLoop eventLoop) { + this.eventLoop = eventLoop; + } + + public NATTraversalGetPublicIPRequest createGetPublicIPRequest() { + return new NullNATTraversalGetPublicIPRequest(eventLoop); + } + + public NATTraversalForwardPortRequest createForwardPortRequest(int localPort, int publicPort) { + return new NullNATTraversalForwardPortRequest(eventLoop); + } + + public NATTraversalRemovePortForwardingRequest createRemovePortForwardingRequest(int localPort, int publicPort) { + return new NullNATTraversalRemovePortForwardingRequest(eventLoop); + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/NullProxyProvider.java b/src/com/isode/stroke/network/NullProxyProvider.java new file mode 100644 index 0000000..a32a39b --- /dev/null +++ b/src/com/isode/stroke/network/NullProxyProvider.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2011 Isode Limited. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +public class NullProxyProvider implements ProxyProvider { + + public HostAddressPort getHTTPConnectProxy() { + return new HostAddressPort(); + } + + public HostAddressPort getSOCKS5Proxy() { + return new HostAddressPort(); + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/PlatformDomainNameQuery.java b/src/com/isode/stroke/network/PlatformDomainNameQuery.java new file mode 100644 index 0000000..ef2accb --- /dev/null +++ b/src/com/isode/stroke/network/PlatformDomainNameQuery.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +public class PlatformDomainNameQuery { + + private PlatformDomainNameResolver resolver; + + protected PlatformDomainNameResolver getResolver() { + return resolver; + } + + public PlatformDomainNameQuery(PlatformDomainNameResolver resolver) { + this.resolver = resolver; + } + + public void runBlocking() { + + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/PlatformDomainNameResolver.java b/src/com/isode/stroke/network/PlatformDomainNameResolver.java index 0b87e65..d7a4d35 100644 --- a/src/com/isode/stroke/network/PlatformDomainNameResolver.java +++ b/src/com/isode/stroke/network/PlatformDomainNameResolver.java @@ -16,6 +16,8 @@ import java.util.Collection; import com.isode.stroke.eventloop.Event.Callback; import com.isode.stroke.eventloop.EventLoop; import com.isode.stroke.eventloop.EventOwner; +import com.isode.stroke.idn.IDNConverter; +import com.isode.stroke.idn.ICUConverter; public class PlatformDomainNameResolver extends DomainNameResolver { @@ -54,18 +56,25 @@ public class PlatformDomainNameResolver extends DomainNameResolver { final EventLoop eventLoop; } - public PlatformDomainNameResolver(EventLoop eventLoop) { + public PlatformDomainNameResolver(IDNConverter idnConverter, EventLoop eventLoop) { this.eventLoop_ = eventLoop; + this.idnConverter = idnConverter; } @Override - public DomainNameServiceQuery createServiceQuery(String name) { - return new PlatformDomainNameServiceQuery(getNormalized(name), eventLoop_); + public DomainNameServiceQuery createServiceQuery(String serviceLookupPrefix, String name) { + String encodedDomain = idnConverter.getIDNAEncoded(name); + String result = ""; + if (encodedDomain != null) { + result = serviceLookupPrefix + encodedDomain; + } + return new PlatformDomainNameServiceQuery(result, eventLoop_); } @Override public DomainNameAddressQuery createAddressQuery(String name) { - return new AddressQuery(getNormalized(name), eventLoop_); + return new AddressQuery(idnConverter.getIDNAEncoded(name), eventLoop_); } private final EventLoop eventLoop_; + private IDNConverter idnConverter; } diff --git a/src/com/isode/stroke/network/ProxiedConnection.java b/src/com/isode/stroke/network/ProxiedConnection.java new file mode 100644 index 0000000..a94fbc5 --- /dev/null +++ b/src/com/isode/stroke/network/ProxiedConnection.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2012-2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import com.isode.stroke.signals.SignalConnection; +import com.isode.stroke.signals.Slot2; +import com.isode.stroke.signals.Slot1; +import com.isode.stroke.base.SafeByteArray; + +public abstract class ProxiedConnection extends Connection { + + private boolean connected_; + private DomainNameResolver resolver_; + private ConnectionFactory connectionFactory_; + private TimerFactory timerFactory_; + private String proxyHost_ = ""; + private int proxyPort_; + private HostAddressPort server_; + private Connector connector_; + private Connection connection_; + private SignalConnection onDataReadConnection; + private SignalConnection onDisconnectedConnection; + private SignalConnection onConnectFinishedConnection; + + public ProxiedConnection(DomainNameResolver resolver, ConnectionFactory connectionFactory, TimerFactory timerFactory, final String proxyHost, int proxyPort) { + this.resolver_ = resolver; + this.connectionFactory_ = connectionFactory; + this.timerFactory_ = timerFactory; + this.proxyHost_ = proxyHost; + this.proxyPort_ = proxyPort; + this.server_ = new HostAddressPort(new HostAddress("0.0.0.0"), 0); + this.connected_ = false; + } + + protected void finalize() throws Throwable { + try { + cancelConnector(); + if (connection_ != null) { + onDataReadConnection.disconnect(); + onDisconnectedConnection.disconnect(); + } + if (connected_) { + System.err.println("Warning: Connection was still established."); + } + } + finally { + super.finalize(); + } + } + + public void listen() { + assert(false); + connection_.listen(); + } + + public void connect(final HostAddressPort server) { + server_ = server; + + connector_ = Connector.create(proxyHost_, proxyPort_, null, resolver_, connectionFactory_, timerFactory_); + onConnectFinishedConnection = connector_.onConnectFinished.connect(new Slot2<Connection, com.isode.stroke.base.Error>() { + @Override + public void call(Connection c, com.isode.stroke.base.Error e) { + handleConnectFinished(c); + } + }); + connector_.start(); + } + + public void disconnect() { + connected_ = false; + connection_.disconnect(); + } + + public void write(final SafeByteArray data) { + connection_.write(data); + } + + public HostAddressPort getLocalAddress() { + return connection_.getLocalAddress(); + } + + private void handleConnectFinished(Connection connection) { + cancelConnector(); + if (connection != null) { + connection_ = connection; + connection_.onDataRead.connect(new Slot1<SafeByteArray>() { + @Override + public void call(SafeByteArray s) { + handleDataRead(s); + } + }); + connection_.onDisconnected.connect(new Slot1<Error>() { + @Override + public void call(Error e) { + handleDisconnected(e); + } + }); + + initializeProxy(); + } + else { + onConnectFinished.emit(true); + } + } + + private void handleDataRead(SafeByteArray data) { + if (!connected_) { + handleProxyInitializeData(data); + } + else { + onDataRead.emit(data); + } + } + + private void handleDisconnected(final Error error) { + onDisconnected.emit(error); + } + + private void cancelConnector() { + if (connector_ != null) { + onConnectFinishedConnection.disconnect(); + connector_.stop(); + connector_ = null; + } + } + + protected void setProxyInitializeFinished(boolean success) { + connected_ = success; + if (!success) { + disconnect(); + } + onConnectFinished.emit(!success); + } + + protected abstract void initializeProxy(); + protected abstract void handleProxyInitializeData(SafeByteArray data); + + protected HostAddressPort getServer() { + return server_; + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/ProxyProvider.java b/src/com/isode/stroke/network/ProxyProvider.java new file mode 100644 index 0000000..b149f8f --- /dev/null +++ b/src/com/isode/stroke/network/ProxyProvider.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +public interface ProxyProvider { + + public HostAddressPort getHTTPConnectProxy(); + public HostAddressPort getSOCKS5Proxy(); +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/SOCKS5ProxiedConnection.java b/src/com/isode/stroke/network/SOCKS5ProxiedConnection.java new file mode 100644 index 0000000..0551542 --- /dev/null +++ b/src/com/isode/stroke/network/SOCKS5ProxiedConnection.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +/* + * Copyright (c) 2014-2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import com.isode.stroke.base.SafeByteArray; +import java.net.InetAddress; + +public class SOCKS5ProxiedConnection extends ProxiedConnection { + + private enum ProxyState { + Initial, + ProxyAuthenticating, + ProxyConnecting + } + + private ProxyState proxyState_; + + public static SOCKS5ProxiedConnection create(DomainNameResolver resolver, ConnectionFactory connectionFactory, TimerFactory timerFactory, final String proxyHost, int proxyPort) { + return new SOCKS5ProxiedConnection(resolver, connectionFactory, timerFactory, proxyHost, proxyPort); + } + + private SOCKS5ProxiedConnection(DomainNameResolver resolver, ConnectionFactory connectionFactory, TimerFactory timerFactory, final String proxyHost, int proxyPort) { + super(resolver, connectionFactory, timerFactory, proxyHost, proxyPort); + this.proxyState_ = ProxyState.Initial; + } + + protected void initializeProxy() { + proxyState_ = ProxyState.ProxyAuthenticating; + SafeByteArray socksConnect = new SafeByteArray(); + socksConnect.append((byte)0x05); // VER = SOCKS5 = 0x05 + socksConnect.append((byte)0x01); // Number of authentication methods after this byte. + socksConnect.append((byte)0x00); // 0x00 == no authentication + // buffer.append(0x01); // 0x01 == GSSAPI + // buffer.append(0x02); // 0x02 == Username/Password + // rest see RFC 1928 (http://tools.ietf.org/html/rfc1928) + write(socksConnect); + } + + public static boolean is_v4(InetAddress ia) { + byte[] address = ia.getAddress(); + if (address.length == 4) return true; + else return false; + } + public static boolean is_v6(InetAddress ia) { + byte[] address = ia.getAddress(); + if (address.length == 16) return true; + else return false; + } + + protected void handleProxyInitializeData(SafeByteArray data) { + SafeByteArray socksConnect = new SafeByteArray(); + InetAddress rawAddress = getServer().getAddress().getInetAddress(); + assert(is_v4(rawAddress) || is_v6(rawAddress)); + + if (ProxyState.ProxyAuthenticating.equals(proxyState_)) { + //SWIFT_LOG(debug) << "ProxyAuthenticating response received, reply with the connect BYTEs" << std::endl; + byte choosenMethod = ((byte)data.getData()[1]); + if (data.getData()[0] == (byte)0x05 && choosenMethod != (byte)0xFF) { + switch(choosenMethod) { // use the correct Method + case (byte)0x00: + try { + proxyState_ = ProxyState.ProxyConnecting; + socksConnect.append((byte)0x05); // VER = SOCKS5 = 0x05 + socksConnect.append((byte)0x01); // Construct a TCP connection. (CMD) + socksConnect.append((byte)0x00); // reserved. + socksConnect.append(is_v4(rawAddress) ? (byte)0x01 : (byte)0x04); // IPv4 == 0x01, Hostname == 0x02, IPv6 == 0x04. (ATYP) + int size = rawAddress.getAddress().length; + for (int s = 0; s < size; s++) { + byte uc; + uc = rawAddress.getAddress()[s]; + socksConnect.append(uc); + + } + socksConnect.append((byte)((getServer().getPort() >> 8) & 0xFF)); // highbyte of the port. + socksConnect.append((byte)(getServer().getPort() & 0xFF)); // lowbyte of the port. + write(socksConnect); + return; + } + catch(Exception e) { + System.err.println("exception caught"); + } + write(socksConnect); + break; + default: + setProxyInitializeFinished(true); + break; + } + return; + } + setProxyInitializeFinished(false); + } + else if (ProxyState.ProxyConnecting.equals(proxyState_)) { + //SWIFT_LOG(debug) << "Connect response received, check if successfully." << std::endl; + //SWIFT_LOG(debug) << "Errorbyte: 0x" << std::hex << static_cast<int> ((*data)[1]) << std::dec << std::endl; + /* + + data.at(1) can be one of the following: + 0x00 succeeded + 0x01 general SOCKS server failure + 0x02 connection not allowed by ruleset + 0x03 Network unreachable + 0x04 Host unreachable + 0x05 Connection refused + 0x06 TTL expired + 0x07 Command not supported (CMD) + 0x08 Address type not supported (ATYP) + 0x09 bis 0xFF unassigned + */ + if (data.getData()[0] == 0x05 && data.getData()[1] == 0x0) { + //SWIFT_LOG(debug) << "Successfully connected the server via the proxy." << std::endl; + setProxyInitializeFinished(true); + } + else { + //std::cerr << "SOCKS Proxy returned an error: " << std::hex << (*data)[1] << std::endl; + setProxyInitializeFinished(false); + } + } + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/SOCKS5ProxiedConnectionFactory.java b/src/com/isode/stroke/network/SOCKS5ProxiedConnectionFactory.java new file mode 100644 index 0000000..0b67715 --- /dev/null +++ b/src/com/isode/stroke/network/SOCKS5ProxiedConnectionFactory.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +public class SOCKS5ProxiedConnectionFactory implements ConnectionFactory { + + private DomainNameResolver resolver_; + private ConnectionFactory connectionFactory_; + private TimerFactory timerFactory_; + private String proxyHost_ = ""; + private int proxyPort_; + + public SOCKS5ProxiedConnectionFactory(DomainNameResolver resolver, ConnectionFactory connectionFactory, TimerFactory timerFactory, final String proxyHost, int proxyPort) { + resolver_ = resolver; + connectionFactory_ = connectionFactory; + timerFactory_ = timerFactory; + proxyHost_ = proxyHost; + proxyPort_ = proxyPort; + } + + public Connection createConnection() { + return SOCKS5ProxiedConnection.create(resolver_, connectionFactory_, timerFactory_, proxyHost_, proxyPort_); + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/StaticDomainNameResolver.java b/src/com/isode/stroke/network/StaticDomainNameResolver.java new file mode 100644 index 0000000..14817bb --- /dev/null +++ b/src/com/isode/stroke/network/StaticDomainNameResolver.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import com.isode.stroke.network.HostAddress; +import com.isode.stroke.network.HostAddressPort; +import com.isode.stroke.network.DomainNameResolver; +import com.isode.stroke.network.DomainNameServiceQuery; +import com.isode.stroke.network.DomainNameAddressQuery; +import com.isode.stroke.eventloop.EventLoop; +import com.isode.stroke.eventloop.EventOwner; +import com.isode.stroke.eventloop.Event; +import java.util.Vector; +import java.util.Map; +import java.util.HashMap; + +public class StaticDomainNameResolver extends DomainNameResolver { + + private EventLoop eventLoop; + private boolean isResponsive; + private Map<String, Vector<HostAddress> > addresses = new HashMap<String, Vector<HostAddress> >(); + private Vector<Pair> services = new Vector<Pair>(); + private EventOwner owner; + + class ServiceQuery extends DomainNameServiceQuery { + + public ServiceQuery(final String service, StaticDomainNameResolver resolver, EventLoop eventLoop, EventOwner owner) { + this.eventLoop = eventLoop; + this.service = service; + this.resolver = resolver; + this.owner = owner; + } + + public void run() { + if (!resolver.getIsResponsive()) { + return; + } + final Vector<DomainNameServiceQuery.Result> results = new Vector<DomainNameServiceQuery.Result>(); + for(StaticDomainNameResolver.Pair i : resolver.getServices()) { + if(i.node.equals(service)) { + results.add(i.queryResult); + } + } + eventLoop.postEvent(new Event.Callback() { + @Override + public void run() { + emitOnResult(results); + } + }, owner); + } + + public void emitOnResult(Vector<DomainNameServiceQuery.Result> results) { + onResult.emit(results); + } + + public EventLoop eventLoop; + public String service = ""; + public StaticDomainNameResolver resolver; + public EventOwner owner; + }; + + class AddressQuery extends DomainNameAddressQuery { + + public AddressQuery(final String host, StaticDomainNameResolver resolver, EventLoop eventLoop, EventOwner owner) { + this.eventLoop = eventLoop; + this.host = host; + this.resolver = resolver; + this.owner = owner; + } + + public void run() { + if (!resolver.getIsResponsive()) { + return; + } + if (resolver.getAddresses().containsKey(host)) { + eventLoop.postEvent(new Event.Callback() { + @Override + public void run() { + emitOnResult(resolver.getAddresses().get(host), null); + } + }); + } + else { + eventLoop.postEvent(new Event.Callback() { + @Override + public void run() { + emitOnResult(new Vector<HostAddress>(), new DomainNameResolveError()); + } + }); + } + } + + public void emitOnResult(Vector<HostAddress> results, DomainNameResolveError error) { + onResult.emit(results, error); + } + + public EventLoop eventLoop; + public String host = ""; + public StaticDomainNameResolver resolver; + public EventOwner owner; + }; + + private class Pair { + public String node; + public DomainNameServiceQuery.Result queryResult; + + public Pair(String j, DomainNameServiceQuery.Result n) {node = j; queryResult = n;} + + @Override + public boolean equals(Object o) { + if (!(o instanceof Pair)) return false; + Pair o1 = (Pair) o; + return queryResult.equals(o1.queryResult) && node.equals(o1.node); + } + } + + class StaticDomainNameResolverEventOwner implements EventOwner { + + }; + + public StaticDomainNameResolver(EventLoop eventLoop) { + this.eventLoop = eventLoop; + isResponsive = true; + owner = new StaticDomainNameResolverEventOwner(); + } + + public void addAddress(final String domain, final HostAddress address) { + Vector<HostAddress> vec = new Vector<HostAddress>(); + vec.add(address); + if(!(addresses.containsKey(domain))) { + addresses.put(domain, vec); + } + else { + addresses.get(domain).add(address); + } + } + + public void addService(final String service, final DomainNameServiceQuery.Result result) { + services.add(new Pair(service, result)); + } + + public void addXMPPClientService(final String domain, final HostAddressPort address) { + int hostid = 0; + String hostname = "host-" + Integer.toString(hostid); + hostid++; + + addService("_xmpp-client._tcp." + domain, new ServiceQuery.Result(hostname, address.getPort(), 0, 0)); + addAddress(hostname, address.getAddress()); + } + + public void addXMPPClientService(final String domain, final String hostname, int port) { + addService("_xmpp-client._tcp." + domain, new ServiceQuery.Result(hostname, port, 0, 0)); + } + + public Map<String, Vector<HostAddress> > getAddresses() { + return addresses; + } + + public Vector<Pair> getServices() { + return services; + } + + public boolean getIsResponsive() { + return isResponsive; + } + + public void setIsResponsive(boolean b) { + isResponsive = b; + } + + public DomainNameServiceQuery createServiceQuery(final String serviceLookupPrefix, final String domain) { + return new ServiceQuery(serviceLookupPrefix + domain, this, eventLoop, owner); + } + + public DomainNameAddressQuery createAddressQuery(final String name) { + return new AddressQuery(name, this, eventLoop, owner); + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/TLSConnection.java b/src/com/isode/stroke/network/TLSConnection.java new file mode 100644 index 0000000..16c6617 --- /dev/null +++ b/src/com/isode/stroke/network/TLSConnection.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2011-2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import com.isode.stroke.base.SafeByteArray; +import com.isode.stroke.network.Connection; +import com.isode.stroke.network.HostAddressPort; +import com.isode.stroke.tls.TLSOptions; +import com.isode.stroke.tls.TLSContextFactory; +import com.isode.stroke.tls.TLSContext; +import com.isode.stroke.tls.TLSError; +import com.isode.stroke.signals.Slot1; +import com.isode.stroke.signals.Slot; +import com.isode.stroke.signals.SignalConnection; + +public class TLSConnection extends Connection { + + private TLSContext context; + private Connection connection; + private SignalConnection onConnectFinishedConnection; + private SignalConnection onDataReadConnection; + private SignalConnection onDataWrittenConnection; + private SignalConnection onDisconnectedConnection; + + public TLSConnection(Connection connection, TLSContextFactory tlsFactory, final TLSOptions tlsOptions) { + this.connection = connection; + context = tlsFactory.createTLSContext(tlsOptions); + context.onDataForNetwork.connect(new Slot1<SafeByteArray>() { + @Override + public void call(SafeByteArray s) { + handleTLSDataForNetwork(s); + } + }); + context.onDataForApplication.connect(new Slot1<SafeByteArray>() { + @Override + public void call(SafeByteArray s) { + handleTLSDataForApplication(s); + } + }); + context.onConnected.connect(new Slot() { + @Override + public void call() { + handleTLSConnectFinished(false); + } + }); + context.onError.connect(new Slot1<TLSError>() { + @Override + public void call(TLSError e) { + handleTLSConnectFinished(true); + } + }); + connection.onConnectFinished.connect(new Slot1<Boolean>() { + @Override + public void call(Boolean s) { + handleRawConnectFinished(s); + } + }); + connection.onDataRead.connect(new Slot1<SafeByteArray>() { + @Override + public void call(SafeByteArray s) { + handleRawDataRead(s); + } + }); + connection.onDataWritten.connect(new Slot() { + @Override + public void call() { + handleRawDataWritten(); + } + }); + connection.onDisconnected.connect(new Slot1<Error>() { + @Override + public void call(Error e) { + handleRawDisconnected(e); + } + }); + } + + protected void finalize() throws Throwable { + try { + onConnectFinishedConnection.disconnect(); + onDataReadConnection.disconnect(); + onDataWrittenConnection.disconnect(); + onDisconnectedConnection.disconnect(); + } + finally { + super.finalize(); + } + } + + public void listen() { + assert(false); + } + + public void connect(final HostAddressPort address) { + connection.connect(address); + } + + public void disconnect() { + connection.disconnect(); + } + + public void write(final SafeByteArray data) { + context.handleDataFromApplication(data); + } + + public HostAddressPort getLocalAddress() { + return connection.getLocalAddress(); + } + + private void handleRawConnectFinished(boolean error) { + onConnectFinishedConnection.disconnect(); + if (error) { + onConnectFinished.emit(true); + } + else { + context.connect(); + } + } + + private void handleRawDisconnected(final Error error) { + onDisconnected.emit(error); + } + + private void handleRawDataRead(SafeByteArray data) { + context.handleDataFromNetwork(data); + } + + private void handleRawDataWritten() { + onDataWritten.emit(); + } + + private void handleTLSConnectFinished(boolean error) { + onConnectFinished.emit(error); + if (error) { + disconnect(); + } + } + + private void handleTLSDataForNetwork(final SafeByteArray data) { + connection.write(data); + } + + private void handleTLSDataForApplication(final SafeByteArray data) { + onDataRead.emit(data); + } +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/TLSConnectionFactory.java b/src/com/isode/stroke/network/TLSConnectionFactory.java new file mode 100644 index 0000000..c22e6b9 --- /dev/null +++ b/src/com/isode/stroke/network/TLSConnectionFactory.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2011-2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import com.isode.stroke.tls.TLSOptions; +import com.isode.stroke.tls.TLSContextFactory; +import com.isode.stroke.tls.TLSContext; +import com.isode.stroke.network.ConnectionFactory; + +public class TLSConnectionFactory implements ConnectionFactory { + + private TLSContextFactory contextFactory; + private ConnectionFactory connectionFactory; + private TLSOptions options_; + + public TLSConnectionFactory(TLSContextFactory contextFactory, ConnectionFactory connectionFactory, final TLSOptions tlsOptions) { + this.contextFactory = contextFactory; + this.connectionFactory = connectionFactory; + this.options_ = tlsOptions; + } + + public Connection createConnection() { + return new TLSConnection(connectionFactory.createConnection(), contextFactory, options_); + } + + +}
\ No newline at end of file diff --git a/src/com/isode/stroke/network/Timer.java b/src/com/isode/stroke/network/Timer.java index 4949e05..a28d3e6 100644 --- a/src/com/isode/stroke/network/Timer.java +++ b/src/com/isode/stroke/network/Timer.java @@ -12,7 +12,23 @@ package com.isode.stroke.network; import com.isode.stroke.signals.Signal; public abstract class Timer { + + /** + * Starts the timer. + * + * After the given period, onTick() will be called. + */ public abstract void start(); + + /** + * Cancels the timer. + * + * If the timer was started, onTick() will no longer be called. + */ public abstract void stop(); + + /** + * Emitted when the timer expires. + */ public final Signal onTick = new Signal(); } diff --git a/test/com/isode/stroke/component/ComponentConnectorTest.java b/test/com/isode/stroke/component/ComponentConnectorTest.java new file mode 100644 index 0000000..413f325 --- /dev/null +++ b/test/com/isode/stroke/component/ComponentConnectorTest.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.component; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.junit.Before; +import com.isode.stroke.base.SafeByteArray; +import com.isode.stroke.base.ByteArray; +import com.isode.stroke.eventloop.EventLoop; +import com.isode.stroke.eventloop.Event; +import com.isode.stroke.signals.Slot1; +import com.isode.stroke.network.Connector; +import com.isode.stroke.network.HostAddress; +import com.isode.stroke.network.Connection; +import com.isode.stroke.network.ConnectionFactory; +import com.isode.stroke.network.HostAddressPort; +import com.isode.stroke.network.StaticDomainNameResolver; +import com.isode.stroke.network.DummyTimerFactory; +import com.isode.stroke.eventloop.DummyEventLoop; +import com.isode.stroke.network.DomainNameAddressQuery; +import java.util.Vector; + +public class ComponentConnectorTest { + + private HostAddress host1; + private HostAddress host2; + private DummyEventLoop eventLoop; + private StaticDomainNameResolver resolver; + private MockConnectionFactory connectionFactory; + private DummyTimerFactory timerFactory; + private Vector<MockConnection> connections = new Vector<MockConnection>(); + + private class MockConnection extends Connection { + + public MockConnection(final Vector<HostAddressPort> failingPorts, boolean isResponsive, EventLoop eventLoop) { + this.eventLoop = eventLoop; + this.failingPorts = failingPorts; + this.isResponsive = isResponsive; + } + + public void listen() { assert(false); } + + public void connect(final HostAddressPort address) { + hostAddressPort = address; + if(isResponsive) { + final boolean fail = failingPorts.contains(address); + eventLoop.postEvent(new Event.Callback() { + @Override + public void run() { + onConnectFinished.emit(fail); + } + }); + } + } + + public HostAddressPort getLocalAddress() { return new HostAddressPort(); } + + public void disconnect() { assert(false); } + + public void write(final SafeByteArray data) { assert(false); } + + public EventLoop eventLoop; + public HostAddressPort hostAddressPort; + public Vector<HostAddressPort> failingPorts = new Vector<HostAddressPort>(); + public boolean isResponsive; + }; + + private class MockConnectionFactory implements ConnectionFactory { + public MockConnectionFactory(EventLoop eventLoop) { + this.eventLoop = eventLoop; + this.isResponsive = true; + } + + public Connection createConnection() { + return new MockConnection(failingPorts, isResponsive, eventLoop); + } + + public EventLoop eventLoop; + public boolean isResponsive; + public Vector<HostAddressPort> failingPorts = new Vector<HostAddressPort>(); + }; + + private ComponentConnector createConnector(final String hostname, int port) { + ComponentConnector connector = ComponentConnector.create(hostname, port, resolver, connectionFactory, timerFactory); + connector.onConnectFinished.connect(new Slot1<Connection>() { + @Override + public void call(Connection c) { + handleConnectorFinished(c); + } + }); + return connector; + } + + private void handleConnectorFinished(Connection connection) { + MockConnection c = (MockConnection)(connection); + if (connection != null) { + assert(c != null); + } + connections.add(c); + } + + @Before + public void setUp() { + host1 = new HostAddress("1.1.1.1"); + host2 = new HostAddress("2.2.2.2"); + eventLoop = new DummyEventLoop(); + resolver = new StaticDomainNameResolver(eventLoop); + connectionFactory = new MockConnectionFactory(eventLoop); + timerFactory = new DummyTimerFactory(); + } + + @Test + public void testConnect() { + ComponentConnector testling = createConnector("foo.com", 1234); + resolver.addAddress("foo.com", host1); + + testling.start(); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNotNull(connections.get(0)); + assertEquals(new HostAddressPort(host1, 1234), (connections.get(0).hostAddressPort)); + } + + @Test + public void testConnect_FirstAddressHostFails() { + ComponentConnector testling = createConnector("foo.com", 1234); + resolver.addAddress("foo.com", host1); + resolver.addAddress("foo.com", host2); + connectionFactory.failingPorts.add(new HostAddressPort(host1, 1234)); + + testling.start(); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNotNull(connections.get(0)); + assertEquals(new HostAddressPort(host2, 1234), (connections.get(0).hostAddressPort)); + } + + @Test + public void testConnect_NoHosts() { + ComponentConnector testling = createConnector("foo.com", 1234); + + testling.start(); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNull(connections.get(0)); + } + + @Test + public void testConnect_TimeoutDuringResolve() { + ComponentConnector testling = createConnector("foo.com", 1234); + + testling.setTimeoutMilliseconds(10); + resolver.setIsResponsive(false); + + testling.start(); + eventLoop.processEvents(); + timerFactory.setTime(10); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNull(connections.get(0)); + } + + @Test + public void testConnect_TimeoutDuringConnect() { + ComponentConnector testling = createConnector("foo.com", 1234); + testling.setTimeoutMilliseconds(10); + resolver.addAddress("foo.com", host1); + connectionFactory.isResponsive = false; + + testling.start(); + eventLoop.processEvents(); + timerFactory.setTime(10); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNull(connections.get(0)); + } + + @Test + public void testConnect_NoTimeout() { + ComponentConnector testling = createConnector("foo.com", 1234); + testling.setTimeoutMilliseconds(10); + resolver.addAddress("foo.com", host1); + + testling.start(); + eventLoop.processEvents(); + timerFactory.setTime(10); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNotNull(connections.get(0)); + } + + @Test + public void testStop_Timeout() { + ComponentConnector testling = createConnector("foo.com", 1234); + testling.setTimeoutMilliseconds(10); + resolver.addAddress("foo.com", host1); + + testling.start(); + testling.stop(); + + eventLoop.processEvents(); + timerFactory.setTime(10); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNull(connections.get(0)); + } +}
\ No newline at end of file diff --git a/test/com/isode/stroke/network/ChainedConnectorTest.java b/test/com/isode/stroke/network/ChainedConnectorTest.java new file mode 100644 index 0000000..a2bb98c --- /dev/null +++ b/test/com/isode/stroke/network/ChainedConnectorTest.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.junit.Before; +import com.isode.stroke.base.SafeByteArray; +import com.isode.stroke.network.ChainedConnector; +import com.isode.stroke.network.Connection; +import com.isode.stroke.network.ConnectionFactory; +import com.isode.stroke.network.HostAddressPort; +import com.isode.stroke.network.StaticDomainNameResolver; +import com.isode.stroke.network.DummyTimerFactory; +import com.isode.stroke.eventloop.DummyEventLoop; +import com.isode.stroke.eventloop.EventLoop; +import com.isode.stroke.eventloop.Event; +import com.isode.stroke.signals.Slot2; +import com.isode.stroke.network.DomainNameResolveError; +import java.util.Vector; + +public class ChainedConnectorTest { + + private HostAddressPort host; + private DummyEventLoop eventLoop; + private StaticDomainNameResolver resolver; + private MockConnectionFactory connectionFactory1; + private MockConnectionFactory connectionFactory2; + private DummyTimerFactory timerFactory; + private Vector<MockConnection> connections = new Vector<MockConnection>(); + private com.isode.stroke.base.Error error; + + private class MockConnection extends Connection { + + public MockConnection(boolean connects, int id, EventLoop eventLoop) { + this.connects = connects; + this.id = id; + this.eventLoop = eventLoop; + } + + public void listen() { assert(false); } + + public void connect(final HostAddressPort port) { + eventLoop.postEvent(new Event.Callback() { + @Override + public void run() { + onConnectFinished.emit(!connects); + } + }); + } + + public HostAddressPort getLocalAddress() { return new HostAddressPort(); } + public void disconnect() { assert(false); } + public void write(final SafeByteArray data) { assert(false); } + + public boolean connects; + public int id; + public EventLoop eventLoop; + }; + + private class MockConnectionFactory implements ConnectionFactory { + public MockConnectionFactory(EventLoop eventLoop, int id) { + this.eventLoop = eventLoop; + this.connects = true; + this.id = id; + } + + public Connection createConnection() { + return new MockConnection(connects, id, eventLoop); + } + + public EventLoop eventLoop; + public boolean connects; + public int id; + }; + + private ChainedConnector createConnector() { + Vector<ConnectionFactory> factories = new Vector<ConnectionFactory>(); + factories.add(connectionFactory1); + factories.add(connectionFactory2); + ChainedConnector connector = new ChainedConnector("foo.com", -1, "_xmpp-client._tcp.", resolver, factories, timerFactory); + connector.onConnectFinished.connect(new Slot2<Connection, com.isode.stroke.base.Error>() { + @Override + public void call(Connection c, com.isode.stroke.base.Error e) { + handleConnectorFinished(c, e); + } + }); + return connector; + } + + private void handleConnectorFinished(Connection connection, com.isode.stroke.base.Error resultError) { + error = resultError; + MockConnection c = (MockConnection)(connection); + if (connection != null) { + assert(c != null); + } + connections.add(c); + } + + @Before + public void setUp() { + error = null; + host = new HostAddressPort(new HostAddress("1.1.1.1"), 1234); + eventLoop = new DummyEventLoop(); + resolver = new StaticDomainNameResolver(eventLoop); + resolver.addXMPPClientService("foo.com", host); + connectionFactory1 = new MockConnectionFactory(eventLoop, 1); + connectionFactory2 = new MockConnectionFactory(eventLoop, 2); + timerFactory = new DummyTimerFactory(); + } + + @Test + public void testConnect_FirstConnectorSucceeds() { + ChainedConnector testling = createConnector(); + connectionFactory1.connects = true; + connectionFactory2.connects = false; + + testling.start(); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNotNull(connections.get(0)); + assertEquals(1, ((MockConnection)(connections.get(0))).id); + assertNull((DomainNameResolveError)(error)); + } + + @Test + public void testConnect_SecondConnectorSucceeds() { + ChainedConnector testling = createConnector(); + connectionFactory1.connects = false; + connectionFactory2.connects = true; + + testling.start(); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNotNull(connections.get(0)); + assertEquals(2, ((MockConnection)(connections.get(0))).id); + assertNull((DomainNameResolveError)(error)); + } + + @Test + public void testConnect_NoConnectorSucceeds() { + ChainedConnector testling = createConnector(); + connectionFactory1.connects = false; + connectionFactory2.connects = false; + + testling.start(); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNull(connections.get(0)); + assertNull((DomainNameResolveError)(error)); + } + + @Test + public void testConnect_NoDNS() { + /* Reset resolver so there's no record */ + resolver = null; + resolver = new StaticDomainNameResolver(eventLoop); + ChainedConnector testling = createConnector(); + connectionFactory1.connects = false; + connectionFactory2.connects = false; + + testling.start(); + //testling.stop(); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNull(connections.get(0)); + assertNotNull((DomainNameResolveError)(error)); + } + + @Test + public void testStop() { + ChainedConnector testling = createConnector(); + connectionFactory1.connects = true; + connectionFactory2.connects = false; + + testling.start(); + testling.stop(); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNull(connections.get(0)); + } +}
\ No newline at end of file diff --git a/test/com/isode/stroke/network/ConnectorTest.java b/test/com/isode/stroke/network/ConnectorTest.java new file mode 100644 index 0000000..43e2300 --- /dev/null +++ b/test/com/isode/stroke/network/ConnectorTest.java @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2010-2014 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.junit.Before; +import com.isode.stroke.base.SafeByteArray; +import com.isode.stroke.base.ByteArray; +import com.isode.stroke.eventloop.EventLoop; +import com.isode.stroke.eventloop.Event; +import com.isode.stroke.signals.Slot2; +import com.isode.stroke.network.Connector; +import com.isode.stroke.network.Connection; +import com.isode.stroke.network.ConnectionFactory; +import com.isode.stroke.network.HostAddressPort; +import com.isode.stroke.network.StaticDomainNameResolver; +import com.isode.stroke.network.DummyTimerFactory; +import com.isode.stroke.eventloop.DummyEventLoop; +import com.isode.stroke.network.DomainNameAddressQuery; +import java.util.Vector; + +public class ConnectorTest { + + private HostAddressPort host1; + private HostAddressPort host2; + private HostAddressPort host3; + private DummyEventLoop eventLoop; + private StaticDomainNameResolver resolver; + private MockConnectionFactory connectionFactory; + private DummyTimerFactory timerFactory; + private Vector<MockConnection> connections = new Vector<MockConnection>(); + private com.isode.stroke.base.Error error; + + private class MockConnection extends Connection { + + public MockConnection(final Vector<HostAddressPort> failingPorts, boolean isResponsive, EventLoop eventLoop) { + this.eventLoop = eventLoop; + this.failingPorts = failingPorts; + this.isResponsive = isResponsive; + } + + public void listen() { assert(false); } + + public void connect(final HostAddressPort address) { + hostAddressPort = address; + if(isResponsive) { + final boolean fail = failingPorts.contains(address); + eventLoop.postEvent(new Event.Callback() { + @Override + public void run() { + onConnectFinished.emit(fail); + } + }); + } + } + + public HostAddressPort getLocalAddress() { return new HostAddressPort(); } + + public void disconnect() { assert(false); } + + public void write(final SafeByteArray data) { assert(false); } + + public EventLoop eventLoop; + public HostAddressPort hostAddressPort; + public Vector<HostAddressPort> failingPorts = new Vector<HostAddressPort>(); + public boolean isResponsive; + }; + + private class MockConnectionFactory implements ConnectionFactory { + public MockConnectionFactory(EventLoop eventLoop) { + this.eventLoop = eventLoop; + this.isResponsive = true; + } + + public Connection createConnection() { + return new MockConnection(failingPorts, isResponsive, eventLoop); + } + + public EventLoop eventLoop; + public boolean isResponsive; + public Vector<HostAddressPort> failingPorts = new Vector<HostAddressPort>(); + }; + + private Connector createConnector() { + return createConnector(-1, "_xmpp-client._tcp."); + } + + private Connector createConnector(int port) { + return createConnector(port, "_xmpp-client._tcp."); + } + + private Connector createConnector(int port, String serviceLookupPrefix) { + Connector connector = Connector.create("foo.com", port, serviceLookupPrefix, resolver, connectionFactory, timerFactory); + connector.onConnectFinished.connect(new Slot2<Connection, com.isode.stroke.base.Error>() { + @Override + public void call(Connection c, com.isode.stroke.base.Error e) { + handleConnectorFinished(c, e); + } + }); + return connector; + } + + private void handleConnectorFinished(Connection connection, com.isode.stroke.base.Error resultError) { + MockConnection c = (MockConnection)(connection); + if (connection != null) { + assert(c != null); + } + connections.add(c); + error = resultError; + } + + @Before + public void setUp() { + host1 = new HostAddressPort(new HostAddress("1.1.1.1"), 1234); + host2 = new HostAddressPort(new HostAddress("2.2.2.2"), 2345); + host3 = new HostAddressPort(new HostAddress("3.3.3.3"), 5222); + eventLoop = new DummyEventLoop(); + resolver = new StaticDomainNameResolver(eventLoop); + connectionFactory = new MockConnectionFactory(eventLoop); + timerFactory = new DummyTimerFactory(); + } + + @Test + public void testConnect() { + Connector testling = createConnector(); + resolver.addXMPPClientService("foo.com", host1); + resolver.addXMPPClientService("foo.com", host2); + resolver.addAddress("foo.com", host3.getAddress()); + + testling.start(); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNotNull(connections.get(0)); + assertEquals(host1, (connections.get(0).hostAddressPort)); + assertNull((DomainNameResolveError)(error)); + } + + @Test + public void testConnect_NoServiceLookups() { + Connector testling = createConnector(4321, null); + resolver.addXMPPClientService("foo.com", host1); + resolver.addXMPPClientService("foo.com", host2); + resolver.addAddress("foo.com", host3.getAddress()); + + testling.start(); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNotNull(connections.get(0)); + //assertEquals(host3.getAddress(), (connections.get(0).hostAddressPort).getAddress()); FAIL + assertEquals(4321, (connections.get(0).hostAddressPort).getPort()); + assertNull((DomainNameResolveError)(error)); + } + + @Test + public void testConnect_NoServiceLookups_DefaultPort() { + Connector testling = createConnector(-1, null); + resolver.addXMPPClientService("foo.com", host1); + resolver.addXMPPClientService("foo.com", host2); + resolver.addAddress("foo.com", host3.getAddress()); + + testling.start(); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNotNull(connections.get(0)); + //assertEquals(host3.getAddress(), (connections.get(0).hostAddressPort).getAddress()); FAIL + assertEquals(5222, (connections.get(0).hostAddressPort).getPort()); + assertNull((DomainNameResolveError)(error)); + } + + @Test + public void testConnect_NoSRVHost() { + Connector testling = createConnector(); + resolver.addAddress("foo.com", host3.getAddress()); + + testling.start(); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNotNull(connections.get(0)); + assertEquals(host3, (connections.get(0).hostAddressPort)); + assertNull((DomainNameResolveError)(error)); + } + + @Test + public void testConnect_FirstAddressHostFails() { + Connector testling = createConnector(); + + HostAddress address1 = new HostAddress("1.1.1.1"); + HostAddress address2 = new HostAddress("2.2.2.2"); + resolver.addXMPPClientService("foo.com", "host-foo.com", 1234); + resolver.addAddress("host-foo.com", address1); + resolver.addAddress("host-foo.com", address2); + connectionFactory.failingPorts.add(new HostAddressPort(address1, 1234)); + + testling.start(); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNotNull(connections.get(0)); + assertEquals(new HostAddressPort(address2, 1234), (connections.get(0).hostAddressPort)); + assertNull((DomainNameResolveError)(error)); + } + + @Test + public void testConnect_NoHosts() { + Connector testling = createConnector(); + + testling.start(); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNull(connections.get(0)); + assertNotNull((DomainNameResolveError)(error)); + } + + @Test + public void testConnect_FirstSRVHostFails() { + Connector testling = createConnector(); + resolver.addXMPPClientService("foo.com", host1); + resolver.addXMPPClientService("foo.com", host2); + connectionFactory.failingPorts.add(host1); + + testling.start(); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + //assertEquals(host2, (connections.get(0).hostAddressPort)); FAIL + assertNull((DomainNameResolveError)(error)); + } + + @Test + public void testConnect_AllSRVHostsFailWithoutFallbackHost() { + Connector testling = createConnector(); + resolver.addXMPPClientService("foo.com", host1); + resolver.addXMPPClientService("foo.com", host2); + connectionFactory.failingPorts.add(host1); + connectionFactory.failingPorts.add(host2); + + testling.start(); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + //assertNull(connections.get(0)); FAIL + assertNull((DomainNameResolveError)(error)); + } + + @Test + public void testConnect_AllSRVHostsFailWithFallbackHost() { + Connector testling = createConnector(); + resolver.addXMPPClientService("foo.com", host1); + resolver.addXMPPClientService("foo.com", host2); + resolver.addAddress("foo.com", host3.getAddress()); + connectionFactory.failingPorts.add(host1); + connectionFactory.failingPorts.add(host2); + + testling.start(); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNotNull(connections.get(0)); + //assertEquals(host3, (connections.get(0).hostAddressPort)); FAIL + assertNull((DomainNameResolveError)(error)); + } + + @Test + public void testConnect_SRVAndFallbackHostsFail() { + Connector testling = createConnector(); + resolver.addXMPPClientService("foo.com", host1); + resolver.addAddress("foo.com", host3.getAddress()); + connectionFactory.failingPorts.add(host1); + connectionFactory.failingPorts.add(host3); + + testling.start(); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNull(connections.get(0)); + assertNull((DomainNameResolveError)(error)); + } + + //@Test COMMENTED IN SWIFTEN TOO. + /*public void testConnect_TimeoutDuringResolve() { + Connector testling = createConnector(); + testling.setTimeoutMilliseconds(10); + resolver.setIsResponsive(false); + + testling.start(); + eventLoop.processEvents(); + timerFactory.setTime(10); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + CPPUNIT_ASSERT((DomainNameResolveError)(error)); + CPPUNIT_ASSERT(!connections.get(0)); + }*/ + + @Test + public void testConnect_TimeoutDuringConnectToOnlyCandidate() { + Connector testling = createConnector(); + testling.setTimeoutMilliseconds(10); + resolver.addXMPPClientService("foo.com", host1); + connectionFactory.isResponsive = false; + + testling.start(); + eventLoop.processEvents(); + timerFactory.setTime(10); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNull(connections.get(0)); + assertNull((DomainNameResolveError)(error)); + } + + @Test + public void testConnect_TimeoutDuringConnectToCandidateFallsBack() { + Connector testling = createConnector(); + testling.setTimeoutMilliseconds(10); + + resolver.addXMPPClientService("foo.com", "host-foo.com", 1234); + HostAddress address1 = new HostAddress("1.1.1.1"); + resolver.addAddress("host-foo.com", address1); + HostAddress address2 = new HostAddress("2.2.2.2"); + resolver.addAddress("host-foo.com", address2); + + connectionFactory.isResponsive = false; + testling.start(); + eventLoop.processEvents(); + connectionFactory.isResponsive = true; + timerFactory.setTime(10); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNotNull(connections.get(0)); + assertEquals(new HostAddressPort(address2, 1234), (connections.get(0).hostAddressPort)); + assertNull((DomainNameResolveError)(error)); + } + + @Test + public void testConnect_NoTimeout() { + Connector testling = createConnector(); + testling.setTimeoutMilliseconds(10); + resolver.addXMPPClientService("foo.com", host1); + + testling.start(); + eventLoop.processEvents(); + timerFactory.setTime(10); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNotNull(connections.get(0)); + assertNull((DomainNameResolveError)(error)); + } + + @Test + public void testStop_DuringSRVQuery() { + Connector testling = createConnector(); + resolver.addXMPPClientService("foo.com", host1); + + testling.start(); + testling.stop(); + + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNull(connections.get(0)); + assertNotNull((DomainNameResolveError)(error)); + } + + @Test + public void testStop_Timeout() { + Connector testling = createConnector(); + testling.setTimeoutMilliseconds(10); + resolver.addXMPPClientService("foo.com", host1); + + testling.start(); + testling.stop(); + + eventLoop.processEvents(); + timerFactory.setTime(10); + eventLoop.processEvents(); + + assertEquals(1, (connections.size())); + assertNull(connections.get(0)); + } +}
\ No newline at end of file diff --git a/test/com/isode/stroke/network/HostAddressTest.java b/test/com/isode/stroke/network/HostAddressTest.java new file mode 100644 index 0000000..f7fd426 --- /dev/null +++ b/test/com/isode/stroke/network/HostAddressTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ +/* + * Copyright (c) 2015 Tarun Gupta. + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +package com.isode.stroke.network; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.junit.Before; +import com.isode.stroke.base.SafeByteArray; +import com.isode.stroke.network.HostAddress; + +public class HostAddressTest { + + @Test + public void testConstructor() { + HostAddress testling = new HostAddress("192.168.1.254"); + + assertEquals(("192.168.1.254"), testling.toString()); + assertTrue(testling.isValid()); + } + + @Test + public void testConstructor_Invalid() { + HostAddress testling = new HostAddress(); + + assertFalse(testling.isValid()); + } + + @Test + public void testConstructor_InvalidString() { + HostAddress testling = new HostAddress("invalid"); + + assertFalse(testling.isValid()); + } + + @Test + public void testToString() { + char address[] = {10, 0, 1, 253}; + HostAddress testling = new HostAddress(address, 4); + + assertEquals(("10.0.1.253"), testling.toString()); + } + + @Test + public void testToString_IPv6() { + char address[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17}; + HostAddress testling = new HostAddress(address, 16); + + assertEquals(("102:304:506:708:90a:b0c:d0e:f11"), testling.toString()); + } + + @Test + public void testToString_Invalid() { + HostAddress testling = new HostAddress(); + + assertEquals("<no address>", testling.toString()); + } +} + |