summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerManager.java')
-rw-r--r--src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerManager.java308
1 files changed, 308 insertions, 0 deletions
diff --git a/src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerManager.java b/src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerManager.java
new file mode 100644
index 0000000..729193a
--- /dev/null
+++ b/src/com/isode/stroke/filetransfer/SOCKS5BytestreamServerManager.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2012-2015 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * 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.filetransfer;
+
+import com.isode.stroke.signals.Slot1;
+import com.isode.stroke.signals.Signal1;
+import com.isode.stroke.signals.SignalConnection;
+import com.isode.stroke.network.ConnectionServer;
+import com.isode.stroke.network.ConnectionServerFactory;
+import com.isode.stroke.network.NATTraverser;
+import com.isode.stroke.network.NetworkInterface;
+import com.isode.stroke.network.NATPortMapping;
+import com.isode.stroke.network.NetworkEnvironment;
+import com.isode.stroke.network.NATTraversalGetPublicIPRequest;
+import com.isode.stroke.network.NATTraversalForwardPortRequest;
+import com.isode.stroke.network.NATTraversalRemovePortForwardingRequest;
+import com.isode.stroke.network.HostAddress;
+import com.isode.stroke.network.HostAddressPort;
+import java.util.Vector;
+import java.util.logging.Logger;
+
+public class SOCKS5BytestreamServerManager {
+
+ public static final int LISTEN_PORTS_BEGIN = 10000;
+ public static final int LISTEN_PORTS_END = 11000;
+
+ private SOCKS5BytestreamRegistry bytestreamRegistry;
+ private ConnectionServerFactory connectionServerFactory;
+ private NetworkEnvironment networkEnvironment;
+ private NATTraverser natTraverser;
+ private enum State { Start, Initializing, Initialized };
+ private State state;
+ private SOCKS5BytestreamServer server;
+ private ConnectionServer connectionServer;
+ private int connectionServerPort;
+
+ private NATTraversalGetPublicIPRequest getPublicIPRequest;
+ private NATTraversalForwardPortRequest forwardPortRequest;
+ private NATTraversalRemovePortForwardingRequest unforwardPortRequest;
+ private HostAddress publicAddress;
+ private NATPortMapping portMapping;
+ private boolean attemptedPortMapping_;
+
+ private SOCKS5BytestreamServerResourceUser s5bServerResourceUser_;
+ private SOCKS5BytestreamServerPortForwardingUser s5bServerPortForwardingUser_;
+ private Logger logger_ = Logger.getLogger(this.getClass().getName());
+
+ public SOCKS5BytestreamServerManager(SOCKS5BytestreamRegistry bytestreamRegistry, ConnectionServerFactory connectionServerFactory, NetworkEnvironment networkEnvironment, NATTraverser natTraverser) {
+ this.bytestreamRegistry = bytestreamRegistry;
+ this.connectionServerFactory = connectionServerFactory;
+ this.networkEnvironment = networkEnvironment;
+ this.natTraverser = natTraverser;
+ this.state = State.Start;
+ this.server = null;
+ this.attemptedPortMapping_ = false;
+ }
+
+ /**
+ * User should call delete to free the resources.
+ */
+ public void delete() {
+ //SWIFT_LOG_ASSERT(!connectionServer, warning) << std::endl;
+ //SWIFT_LOG_ASSERT(!getPublicIPRequest, warning) << std::endl;
+ //SWIFT_LOG_ASSERT(!forwardPortRequest, warning) << std::endl;
+ //SWIFT_LOG_ASSERT(state == Start, warning) << std::endl;
+ if (portMapping != null && unforwardPortRequest == null) {
+ //SWIFT_LOG(warning) << "Port forwarding still alive. Trying to remove it now." << std::endl;
+ unforwardPortRequest = natTraverser.createRemovePortForwardingRequest(portMapping.getLocalPort(), portMapping.getPublicPort());
+ unforwardPortRequest.start();
+ }
+ }
+
+ protected void finalize() throws Throwable {
+ try {
+ delete();
+ }
+ finally {
+ super.finalize();
+ }
+ }
+
+ public SOCKS5BytestreamServerResourceUser aquireResourceUser() {
+ SOCKS5BytestreamServerResourceUser resourceUser = null;
+ if (s5bServerResourceUser_ == null) {
+ resourceUser = new SOCKS5BytestreamServerResourceUser(this);
+ s5bServerResourceUser_ = resourceUser;
+ }
+ else {
+ resourceUser = s5bServerResourceUser_;
+ }
+ return resourceUser;
+ }
+
+ public SOCKS5BytestreamServerPortForwardingUser aquirePortForwardingUser() {
+ SOCKS5BytestreamServerPortForwardingUser portForwardingUser = null;
+ if (s5bServerPortForwardingUser_ == null) {
+ portForwardingUser = new SOCKS5BytestreamServerPortForwardingUser(this);
+ s5bServerPortForwardingUser_ = portForwardingUser;
+ }
+ else {
+ portForwardingUser = s5bServerPortForwardingUser_;
+ }
+ return portForwardingUser;
+ }
+
+ public void stop() {
+ if (getPublicIPRequest != null) {
+ getPublicIPRequest.stop();
+ getPublicIPRequest = null;
+ }
+ if (forwardPortRequest != null) {
+ forwardPortRequest.stop();
+ forwardPortRequest = null;
+ }
+ if (server != null) {
+ server.stop();
+ server = null;
+ }
+ if (connectionServer != null) {
+ connectionServer.stop();
+ connectionServer = null;
+ }
+
+ state = State.Start;
+ }
+
+ public Vector<HostAddressPort> getHostAddressPorts() {
+ Vector<HostAddressPort> result = new Vector<HostAddressPort>();
+ if (connectionServer != null) {
+ Vector<NetworkInterface> networkInterfaces = networkEnvironment.getNetworkInterfaces();
+ for (final NetworkInterface networkInterface : networkInterfaces) {
+ for (final HostAddress address : networkInterface.getAddresses()) {
+ result.add(new HostAddressPort(address, connectionServerPort));
+ }
+ }
+ }
+ return result;
+ }
+
+ public Vector<HostAddressPort> getAssistedHostAddressPorts() {
+ Vector<HostAddressPort> result = new Vector<HostAddressPort>();
+ if (publicAddress != null && portMapping != null) {
+ result.add(new HostAddressPort(publicAddress, portMapping.getPublicPort()));
+ }
+ return result;
+ }
+
+ public SOCKS5BytestreamServer getServer() {
+ return server;
+ }
+
+ boolean isInitialized() {
+ return State.Initialized.equals(state);
+ }
+
+ void initialize() {
+ if (State.Start.equals(state)) {
+ state = State.Initializing;
+
+ // Find a port to listen on
+ assert(connectionServer == null);
+ int port;
+ for (port = LISTEN_PORTS_BEGIN; port < LISTEN_PORTS_END; ++port) {
+ logger_.fine("Trying to start server on port " + port + "\n");
+ connectionServer = connectionServerFactory.createConnectionServer(new HostAddress("0.0.0.0"), port);
+ ConnectionServer.Error error = connectionServer.tryStart();
+ if (error == null) {
+ break;
+ }
+ else if (!ConnectionServer.Error.Conflict.equals(error)) {
+ logger_.fine("Error starting server\n");
+ onInitialized.emit(false);
+ return;
+ }
+ connectionServer = null;
+ }
+ if (connectionServer == null) {
+ logger_.fine("Unable to find an open port\n");
+ onInitialized.emit(false);
+ return;
+ }
+ logger_.fine("Server started succesfully\n");
+ connectionServerPort = port;
+
+ // Start bytestream server. Should actually happen before the connectionserver is started
+ // but that doesn't really matter here.
+ assert(server == null);
+ server = new SOCKS5BytestreamServer(connectionServer, bytestreamRegistry);
+ server.start();
+ checkInitializeFinished();
+ }
+ }
+
+ boolean isPortForwardingReady() {
+ return attemptedPortMapping_ && getPublicIPRequest == null && forwardPortRequest == null;
+ }
+
+ void setupPortForwarding() {
+ assert(server != null);
+ attemptedPortMapping_ = true;
+
+ // Retrieve public addresses
+ assert(getPublicIPRequest == null);
+ publicAddress = null;
+ if ((natTraverser.createGetPublicIPRequest() != null)) {
+ getPublicIPRequest = natTraverser.createGetPublicIPRequest();
+ getPublicIPRequest.onResult.connect(new Slot1<HostAddress>() {
+ @Override
+ public void call(HostAddress a) {
+ handleGetPublicIPResult(a);
+ }
+ });
+ getPublicIPRequest.start();
+ }
+
+ // Forward ports
+ int port = server.getAddressPort().getPort();
+ assert(forwardPortRequest == null);
+ portMapping = null;
+ if ((natTraverser.createForwardPortRequest(port, port) != null)) {
+ forwardPortRequest = natTraverser.createForwardPortRequest(port, port);
+ forwardPortRequest.onResult.connect(new Slot1<NATPortMapping>() {
+ @Override
+ public void call(NATPortMapping n) {
+ handleForwardPortResult(n);
+ }
+ });
+ forwardPortRequest.start();
+ }
+ }
+
+ void removePortForwarding() {
+ // remove port forwards
+ if (portMapping != null) {
+ unforwardPortRequest = natTraverser.createRemovePortForwardingRequest(portMapping.getLocalPort(), portMapping.getPublicPort());
+ unforwardPortRequest.onResult.connect(new Slot1<Boolean>() {
+ @Override
+ public void call(Boolean b) {
+ handleUnforwardPortResult(b);
+ }
+ });
+ unforwardPortRequest.start();
+ }
+ }
+
+ void checkInitializeFinished() {
+ assert(State.Initializing.equals(state));
+ state = State.Initialized;
+ onInitialized.emit(true);
+ }
+
+ void handleGetPublicIPResult(HostAddress address) {
+ if (address != null) {
+ logger_.fine("Public IP discovered as " + address.toString() + ".\n");
+ }
+ else {
+ logger_.fine("No public IP discoverable.\n");
+ }
+
+ publicAddress = address;
+
+ getPublicIPRequest.stop();
+ getPublicIPRequest = null;
+ }
+
+ void handleForwardPortResult(NATPortMapping mapping) {
+ if (mapping != null) {
+ logger_.fine("Mapping port was successful.\n");
+ }
+ else {
+ logger_.fine("Mapping port has failed.\n");
+ }
+
+ portMapping = mapping;
+ onPortForwardingSetup.emit(mapping != null);
+
+ forwardPortRequest.stop();
+ forwardPortRequest = null;
+ }
+
+ void handleUnforwardPortResult(Boolean result) {
+ if (result != null && result) {
+ portMapping = null;
+ }
+ else {
+ logger_.warning("Failed to remove port forwarding.\n");
+ }
+ attemptedPortMapping_ = false;
+ unforwardPortRequest = null;
+ }
+
+ Signal1<Boolean/* success */> onInitialized = new Signal1<Boolean>();
+ Signal1<Boolean/* success */> onPortForwardingSetup = new Signal1<Boolean>();
+} \ No newline at end of file