diff options
Diffstat (limited to 'app/src/main/java/se/leap/bitmaskclient/pluggableTransports')
-rw-r--r-- | app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Obfs4Options.java | 5 | ||||
-rw-r--r-- | app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsVpnClient.java | 127 |
2 files changed, 131 insertions, 1 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Obfs4Options.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Obfs4Options.java index 2f9cb732..b96f88ca 100644 --- a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Obfs4Options.java +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/Obfs4Options.java @@ -7,12 +7,15 @@ public class Obfs4Options implements Serializable { public String iatMode; public String remoteIP; public String remotePort; + // openvpn is still using tcp, obfs4 is wrapped in kcp, if udp == true + public boolean udp; - public Obfs4Options(String remoteIP, String remotePort, String cert, String iatMode) { + public Obfs4Options(String remoteIP, String remotePort, String cert, String iatMode, boolean udp) { this.cert = cert; this.iatMode = iatMode; this.remoteIP = remoteIP; this.remotePort = remotePort; + this.udp = udp; } } diff --git a/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsVpnClient.java b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsVpnClient.java new file mode 100644 index 00000000..f6c8837e --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/pluggableTransports/ObfsVpnClient.java @@ -0,0 +1,127 @@ +package se.leap.bitmaskclient.pluggableTransports; + +import android.util.Log; + +import java.util.Observable; +import java.util.Observer; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import client.Client_; +import de.blinkt.openvpn.core.ConnectionStatus; +import de.blinkt.openvpn.core.VpnStatus; +import se.leap.bitmaskclient.eip.EipStatus; + +public class ObfsVpnClient implements Observer, client.EventLogger { + + public static final AtomicInteger SOCKS_PORT = new AtomicInteger(4430); + public static final String SOCKS_IP = "127.0.0.1"; + private static final String ERR_BIND = "bind: address already in use"; + + private static final String TAG = ObfsVpnClient.class.getSimpleName(); + private volatile boolean noNetwork; + private final AtomicBoolean pendingNetworkErrorHandling = new AtomicBoolean(false); + private final AtomicInteger reconnectRetry = new AtomicInteger(0); + private static final int MAX_RETRY = 5; + + private final client.Client_ obfsVpnClient; + private final Object LOCK = new Object(); + + public ObfsVpnClient(Obfs4Options options) { + obfsVpnClient = new Client_(options.udp, SOCKS_IP+":"+SOCKS_PORT.get(), options.cert); + obfsVpnClient.setEventLogger(this); + } + + /** + * starts the client + * @return the port ObfsVpn is running on + */ + public int start() { + synchronized (LOCK) { + Log.d(TAG, "aquired LOCK"); + new Thread(this::startSync).start(); + waitUntilStarted(); + Log.d(TAG, "returning LOCK after " + (reconnectRetry.get() + 1) * 200 +" ms"); + } + return SOCKS_PORT.get(); + } + + private void waitUntilStarted() { + int count = -1; + try { + while (count < reconnectRetry.get() && reconnectRetry.get() < MAX_RETRY) { + Thread.sleep(200); + count++; + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private void startSync() { + try { + obfsVpnClient.start(); + } catch (Exception e) { + Log.e(TAG, "[obfsvpn] exception: " + e.getLocalizedMessage()); + VpnStatus.logError("[obfsvpn] " + e.getLocalizedMessage()); + if (e.getLocalizedMessage() != null && e.getLocalizedMessage().contains(ERR_BIND) && reconnectRetry.get() < MAX_RETRY) { + reconnectRetry.addAndGet(1); + SOCKS_PORT.addAndGet(1); + obfsVpnClient.setSocksAddr(SOCKS_IP+":"+SOCKS_PORT.get()); + Log.d(TAG, "[obfsvpn] reconnecting on different port... " + SOCKS_PORT.get()); + VpnStatus.logDebug("[obfsvpn] reconnecting on different port... " + SOCKS_PORT.get()); + startSync(); + } else if (noNetwork) { + pendingNetworkErrorHandling.set(true); + } + } + } + + public void stop() { + synchronized (LOCK) { + Log.d(TAG, "stopping obfsVpnClient..."); + try { + obfsVpnClient.stop(); + reconnectRetry.set(0); + SOCKS_PORT.set(4430); + Thread.sleep(100); + } catch (Exception e) { + e.printStackTrace(); + VpnStatus.logError("[obfsvpn] " + e.getLocalizedMessage()); + } + pendingNetworkErrorHandling.set(false); + Log.d(TAG, "stopping obfsVpnClient releasing LOCK ..."); + } + } + + public boolean isStarted() { + return obfsVpnClient.isStarted(); + } + + @Override + public void update(Observable observable, Object arg) { + if (observable instanceof EipStatus) { + EipStatus status = (EipStatus) observable; + if (status.getLevel() == ConnectionStatus.LEVEL_NONETWORK) { + noNetwork = true; + } else { + noNetwork = false; + if (pendingNetworkErrorHandling.getAndSet(false)) { + stop(); + start(); + } + } + } + } + + @Override + public void error(String s) { + VpnStatus.logError("[obfsvpn] " + s); + } + + @Override + public void log(String state, String message) { + VpnStatus.logDebug("[obfsvpn] " + state + " " + message); + } + +} |