From 97a117cb3bbc022ee16008dea9896a8dfea7c681 Mon Sep 17 00:00:00 2001 From: cyberta Date: Thu, 30 Jan 2020 16:14:22 -0600 Subject: implement usb tethering --- .../bitmaskclient/firewall/FirewallManager.java | 2 +- .../bitmaskclient/firewall/SetupTetheringTask.java | 104 +++++++++++------ .../firewall/ShutdownTetheringTask.java | 35 +++--- .../bitmaskclient/fragments/TetheringDialog.java | 13 ++- .../tethering/TetheringObservable.java | 37 +++--- .../bitmaskclient/tethering/TetheringState.java | 12 +- .../tethering/TetheringStateManager.java | 127 +++++++++++---------- 7 files changed, 199 insertions(+), 131 deletions(-) diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java index 82888668..67cc4625 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/FirewallManager.java @@ -108,7 +108,7 @@ public class FirewallManager implements FirewallCallback, Observer { task.execute(); } - private void stopTethering() { + public void stopTethering() { ShutdownTetheringTask task = new ShutdownTetheringTask(this); task.execute(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java b/app/src/main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java index 49febc24..7abd01a8 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/SetupTetheringTask.java @@ -21,6 +21,7 @@ import android.util.Log; import java.lang.ref.WeakReference; import java.net.NetworkInterface; +import java.util.ArrayList; import java.util.Enumeration; import se.leap.bitmaskclient.tethering.TetheringObservable; @@ -70,20 +71,28 @@ public class SetupTetheringTask extends AsyncTask { if (tetheringState.tetherWifiVpn()) { log = new StringBuilder(); success = addWifiTetheringRules(tetheringState, log); + logError(success, log); } else if (!tetheringState.isVpnWifiTetheringAllowed){ success = removeWifiTetheringRules(tetheringState, log); + logError(success, log); } + log = new StringBuilder(); if (tetheringState.tetherUsbVpn()) { success = success && addUsbTetheringRules(tetheringState, log); + logError(success, log); } else if (!tetheringState.isVpnUsbTetheringAllowed) { success = success && removeUsbTetheringRules(tetheringState, log); + logError(success, log); } + log = new StringBuilder(); if (tetheringState.tetherBluetoothVpn()) { success = success && addBluetoothTetheringRules(tetheringState, log); + logError(success, log); } else if (!tetheringState.isVpnBluetoothTetheringAllowed) { success = success && removeBluetoothTetheringRules(tetheringState, log); + logError(success, log); } return success; } catch (Exception e) { @@ -93,22 +102,10 @@ public class SetupTetheringTask extends AsyncTask { return false; } - - //TODO: implement the follwing methods -v - private boolean removeBluetoothTetheringRules(TetheringState tetheringState, StringBuilder log) { - return true; - } - - private boolean removeUsbTetheringRules(TetheringState tetheringState, StringBuilder log) { - return true; - } - - private boolean addBluetoothTetheringRules(TetheringState tetheringState, StringBuilder log) { - return true; - } - - private boolean addUsbTetheringRules(TetheringState tetheringState, StringBuilder log) { - return true; + private void logError(boolean success, StringBuilder log) { + if (!success) { + Log.e(TAG, log.toString()); + } } @@ -128,43 +125,76 @@ public class SetupTetheringTask extends AsyncTask { private boolean addWifiTetheringRules(TetheringState state, StringBuilder log) throws Exception { Log.d(TAG, "add Wifi tethering Rules"); - String[] addRules = new String[] { + String[] addRules = getAdditionRules(state.wifiAddress, state.wifiInterface); + return runBlockingCmd(addRules, log) == 0; + } + + private boolean removeWifiTetheringRules(TetheringState state, StringBuilder log) throws Exception { + Log.d(TAG, "add Wifi tethering Rules"); + String[] removeRules = getDeletionRules(state, state.lastSeenWifiAddress, state.lastSeenWifiInterface); + return runBlockingCmd(removeRules, log) == 0; + } + + private boolean addUsbTetheringRules(TetheringState state, StringBuilder log) throws Exception { + Log.d(TAG, "add usb tethering rules"); + String[] addRules = getAdditionRules(state.usbAddress, state.usbInterface); + return runBlockingCmd(addRules, log) == 0; + } + + private boolean removeUsbTetheringRules(TetheringState state, StringBuilder log) throws Exception { + Log.d(TAG, "add usb tethering rules"); + String[] addRules = getDeletionRules(state, state.lastSeenUsbAddress, state.lastSeenUsbInterface); + return runBlockingCmd(addRules, log) == 0; + } + + //TODO: implement the follwing methods -v + private boolean removeBluetoothTetheringRules(TetheringState state, StringBuilder log) { + return true; + } + + private boolean addBluetoothTetheringRules(TetheringState state, StringBuilder log) { + return true; + } + + private String[] getAdditionRules(String addressRange, String interfaceName) { + return new String[] { "su", "iptables -t filter --flush " + BITMASK_FORWARD, "iptables -t nat --flush " + BITMASK_POSTROUTING, "iptables -t filter --append " + BITMASK_FORWARD + " --jump ACCEPT", "iptables -t nat --append " + BITMASK_POSTROUTING + " --jump MASQUERADE", - "if [[ ! `ip rule show from "+ state.wifiAddress+" lookup 61` ]]; " + - "then ip rule add from " + state.wifiAddress + " lookup 61; " + + "if [[ ! `ip rule show from "+ addressRange+" lookup 61` ]]; " + + "then ip rule add from " + addressRange + " lookup 61; " + "fi", "if [[ ! `ip route list table 61 | grep 'default dev " + getTunName() + " scope link'` ]]; " + "then ip route add default dev " + getTunName() + " scope link table 61; " + "fi", - "if [[ ! `ip route list table 61 | grep '"+ state.wifiAddress+" dev "+ state.wifiInterface+" scope link'` ]]; " + - "then ip route add " + state.wifiAddress + " dev " + state.wifiInterface + " scope link table 61; " + + "if [[ ! `ip route list table 61 | grep '"+ addressRange +" dev "+ interfaceName +" scope link'` ]]; " + + "then ip route add " + addressRange + " dev " + interfaceName + " scope link table 61; " + "fi", - "if [[ ! `ip route list table 61 | grep 'broadcast 255.255.255.255 dev " + state.wifiInterface + " scope link'` ]]; " + - "then ip route add broadcast 255.255.255.255 dev " + state.wifiInterface + " scope link table 61; " + + "if [[ ! `ip route list table 61 | grep 'broadcast 255.255.255.255 dev " + interfaceName + " scope link'` ]]; " + + "then ip route add broadcast 255.255.255.255 dev " + interfaceName + " scope link table 61; " + "fi" }; - - return runBlockingCmd(addRules, log) == 0; } - private boolean removeWifiTetheringRules(TetheringState state, StringBuilder log) throws Exception { - Log.d(TAG, "add Wifi tethering Rules"); - String[] removeRules = new String[] { - "su", - "ip route delete broadcast 255.255.255.255 dev " + state.wifiInterface +" scope link table 61", - "ip route delete " + state.lastWifiAddress + " dev " + state.wifiInterface +" scope link table 61", - "ip route delete default dev " + getTunName() + " scope link table 61", - "if [[ `ip rule show from " + state.lastWifiAddress+ " lookup 61` ]]; " + - "then ip rule del from " + state.lastWifiAddress + " lookup 61; " + - "fi", - }; - return runBlockingCmd(removeRules, log) == 0; + private String[] getDeletionRules(TetheringState state, String addressRange, String interfaceName) { + ArrayList list = new ArrayList<>(); + list.add("su"); + list.add("ip route delete broadcast 255.255.255.255 dev " + addressRange +" scope link table 61"); + list.add("ip route delete " + addressRange + " dev " + interfaceName +" scope link table 61"); + if (!state.hasAnyVpnTetheringAllowed() || !state.hasAnyDeviceTetheringEnabled()) { + list.add("ip route delete default dev " + getTunName() + " scope link table 61"); + } + list.add("if [[ `ip rule show from " + addressRange + " lookup 61` ]]; " + + "then ip rule del from " + addressRange + " lookup 61; " + + "fi"); + + return list.toArray(new String[0]); } + + private String getTunName() { try { for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) { diff --git a/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownTetheringTask.java b/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownTetheringTask.java index 6c15c3e3..dcb3ccba 100644 --- a/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownTetheringTask.java +++ b/app/src/main/java/se/leap/bitmaskclient/firewall/ShutdownTetheringTask.java @@ -20,6 +20,7 @@ import android.os.AsyncTask; import android.util.Log; import java.lang.ref.WeakReference; +import java.util.ArrayList; import se.leap.bitmaskclient.tethering.TetheringObservable; import se.leap.bitmaskclient.tethering.TetheringState; @@ -50,26 +51,30 @@ public class ShutdownTetheringTask extends AsyncTask { boolean hasBitmaskChain = runBlockingCmd(bitmaskChain, log) == 0; boolean allowSu = log.toString().contains("uid=0"); callbackWeakReference.get().onSuRequested(allowSu); - if (!allowSu || !hasBitmaskChain) { + if (!allowSu) { return false; } log = new StringBuilder(); - String[] removeChains = new String[] { - "su", - "ip route flush table 61", - "if [[ `ip rule show from " + tetheringState.wifiAddress+ " lookup 61` ]]; " + - "then ip rule del from " + tetheringState.wifiAddress + " lookup 61; " + - "fi", - "iptables -t filter --delete FORWARD --jump " + BITMASK_FORWARD, - "iptables -t nat --delete POSTROUTING --jump " + BITMASK_POSTROUTING, - "iptables -t filter --flush " + BITMASK_FORWARD, - "iptables -t nat --flush " + BITMASK_POSTROUTING, - "iptables -t filter --delete-chain " + BITMASK_FORWARD, - "iptables -t nat --delete-chain " + BITMASK_POSTROUTING, - }; - return runBlockingCmd(removeChains, log) == 0; + ArrayList removeChains = new ArrayList<>(); + removeChains.add("su"); + removeChains.add("ip route flush table 61"); + removeChains.add("if [[ `ip rule show from " + tetheringState.lastSeenWifiAddress+ " lookup 61` ]]; " + + "then ip rule del from " + tetheringState.lastSeenWifiAddress + " lookup 61; " + + "fi"); + removeChains.add("if [[ `ip rule show from " + tetheringState.lastSeenUsbAddress+ " lookup 61` ]]; " + + "then ip rule del from " + tetheringState.lastSeenUsbAddress + " lookup 61; " + + "fi"); + if (hasBitmaskChain) { + removeChains.add("iptables -t filter --delete FORWARD --jump " + BITMASK_FORWARD); + removeChains.add("iptables -t nat --delete POSTROUTING --jump " + BITMASK_POSTROUTING); + removeChains.add("iptables -t filter --flush " + BITMASK_FORWARD); + removeChains.add("iptables -t nat --flush " + BITMASK_POSTROUTING); + removeChains.add("iptables -t filter --delete-chain " + BITMASK_FORWARD); + removeChains.add("iptables -t nat --delete-chain " + BITMASK_POSTROUTING); + } + return runBlockingCmd(removeChains.toArray(new String[0]), log) == 0; } catch (Exception e) { e.printStackTrace(); diff --git a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java index 2e1e8f95..52ed16f7 100644 --- a/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/fragments/TetheringDialog.java @@ -25,6 +25,7 @@ import java.util.Observer; import butterknife.ButterKnife; import butterknife.InjectView; +import de.blinkt.openvpn.core.VpnStatus; import se.leap.bitmaskclient.R; import se.leap.bitmaskclient.firewall.FirewallManager; import se.leap.bitmaskclient.tethering.TetheringObservable; @@ -141,9 +142,15 @@ public class TetheringDialog extends AppCompatDialogFragment implements Observer TetheringObservable.allowVpnUsbTethering(dataset[1].checked); TetheringObservable.allowVpnBluetoothTethering(dataset[2].checked); FirewallManager firewallManager = new FirewallManager(getContext().getApplicationContext(), false); - firewallManager.startTethering(); - }) - .setNegativeButton(R.string.cancel, (dialog, id) -> dialog.cancel()); + if (VpnStatus.isVPNActive()) { + if (TetheringObservable.getInstance().getTetheringState().hasAnyDeviceTetheringEnabled() && + TetheringObservable.getInstance().getTetheringState().hasAnyVpnTetheringAllowed()) { + firewallManager.startTethering(); + } else { + firewallManager.stopTethering(); + } + } + }).setNegativeButton(R.string.cancel, (dialog, id) -> dialog.cancel()); return builder.create(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java index b84f3494..75d29417 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java @@ -16,6 +16,8 @@ */ package se.leap.bitmaskclient.tethering; +import android.support.annotation.NonNull; + import java.util.Observable; public class TetheringObservable extends Observable { @@ -43,40 +45,47 @@ public class TetheringObservable extends Observable { } public static void allowVpnUsbTethering(boolean enabled) { - if (getInstance().tetheringState.isUsbTetheringEnabled != enabled) { - getInstance().tetheringState.isUsbTetheringEnabled = enabled; + if (getInstance().tetheringState.isVpnUsbTetheringAllowed != enabled) { + getInstance().tetheringState.isVpnUsbTetheringAllowed = enabled; getInstance().setChanged(); getInstance().notifyObservers(); } } public static void allowVpnBluetoothTethering(boolean enabled) { - if (getInstance().tetheringState.isBluetoothTetheringEnabled != enabled) { - getInstance().tetheringState.isBluetoothTetheringEnabled = enabled; + if (getInstance().tetheringState.isVpnBluetoothTetheringAllowed != enabled) { + getInstance().tetheringState.isVpnBluetoothTetheringAllowed = enabled; getInstance().setChanged(); getInstance().notifyObservers(); } } - static void setWifiTethering(boolean enabled, String address, String interfaceName) { + static void setWifiTethering(boolean enabled, @NonNull String address, @NonNull String interfaceName) { if (getInstance().tetheringState.isWifiTetheringEnabled != enabled || !getInstance().tetheringState.wifiInterface.equals(interfaceName) || !getInstance().tetheringState.wifiAddress.equals(address)) { - getInstance().tetheringState.isWifiTetheringEnabled = enabled; - getInstance().tetheringState.wifiInterface = interfaceName; - getInstance().tetheringState.wifiAddress = address; - if ("".equals(address)) { - getInstance().tetheringState.lastWifiAddress = address; - } + TetheringState state = getInstance().tetheringState; + state.isWifiTetheringEnabled = enabled; + state.wifiInterface = interfaceName; + state.wifiAddress = address; + state.lastSeenWifiAddress = address.isEmpty() ? state.lastSeenWifiAddress : address; + state.lastSeenWifiInterface = interfaceName.isEmpty() ? state.lastSeenWifiInterface : interfaceName; getInstance().setChanged(); getInstance().notifyObservers(); } } - static void setUsbTethering(boolean enabled) { - if (getInstance().tetheringState.isUsbTetheringEnabled != enabled) { - getInstance().tetheringState.isUsbTetheringEnabled = enabled; + static void setUsbTethering(boolean enabled, @NonNull String address, @NonNull String interfaceName) { + if (getInstance().tetheringState.isUsbTetheringEnabled != enabled || + !getInstance().tetheringState.usbAddress.equals(address) || + !getInstance().tetheringState.usbInterface.equals(interfaceName)) { + TetheringState state = getInstance().tetheringState; + state.isUsbTetheringEnabled = enabled; + state.usbAddress = address; + state.usbInterface = interfaceName; + state.lastSeenUsbAddress = address.isEmpty() ? state.lastSeenUsbAddress : address; + state.lastSeenUsbInterface = interfaceName.isEmpty() ? state.lastSeenUsbInterface : interfaceName; getInstance().setChanged(); getInstance().notifyObservers(); } diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringState.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringState.java index 9708639a..8ef237c6 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringState.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringState.java @@ -8,12 +8,18 @@ public class TetheringState implements Cloneable { public boolean isVpnUsbTetheringAllowed; public boolean isVpnBluetoothTetheringAllowed; public String wifiInterface = ""; - public String usbInterface = ""; - public String bluetoothInterface = ""; + public String lastSeenWifiInterface = ""; public String wifiAddress = ""; - public String lastWifiAddress = ""; + public String lastSeenWifiAddress = ""; + public String usbInterface = ""; + public String lastSeenUsbInterface = ""; public String usbAddress = ""; + public String lastSeenUsbAddress = ""; + public String bluetoothInterface = ""; + public String lastSeenBluetoothInterface = ""; public String bluetoothAddress = ""; + public String lastSeenBluetoothAddress = ""; + public boolean tetherWifiVpn() { return isWifiTetheringEnabled && isVpnWifiTetheringAllowed; diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java index 11e1a83d..4a266d87 100644 --- a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java @@ -19,7 +19,6 @@ package se.leap.bitmaskclient.tethering; import android.content.Context; import android.content.IntentFilter; import android.support.annotation.VisibleForTesting; -import android.util.Log; import java.net.Inet4Address; import java.net.InterfaceAddress; @@ -72,26 +71,68 @@ public class TetheringStateManager { updateBluetoothTetheringState(); } + static void updateWifiTetheringState() { + WifiManagerWrapper manager = getInstance().wifiManager; + try { + TetheringObservable.setWifiTethering(manager.isWifiAPEnabled(), getWifiAddressRange(), getWlanInterfaceName()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + static void updateUsbTetheringState() { + TetheringObservable.setUsbTethering(isUsbTetheringEnabled(), getUsbAddressRange(), getUsbInterfaceName()); + } + + static void updateBluetoothTetheringState() { + TetheringObservable.setBluetoothTethering(isBluetoothTetheringEnabled()); + } + + private static String getWifiAddressRange() { + String interfaceAddress = getInterfaceAddress(getWlanInterface()); + return getAddressRange(interfaceAddress); + } + + private static String getUsbAddressRange() { + String interfaceAddress = getInterfaceAddress(getUsbInterface()); + return getAddressRange(interfaceAddress); + } + + private static String getWlanInterfaceName() { + return getInterfaceName(getWlanInterface()); + } - private static boolean getUsbTetheringState() { + private static String getUsbInterfaceName() { + return getInterfaceName(getUsbInterface()); + } + + private static NetworkInterface getWlanInterface() { + return getNetworkInterface(new String[]{"wlan", "eth"}); + } + + private static NetworkInterface getUsbInterface() { + return getNetworkInterface(new String[]{"rndis", "usb"}); + } + + private static boolean isBluetoothTetheringEnabled() { + StringBuilder log = new StringBuilder(); + boolean hasBtPan = false; try { - for(Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { - NetworkInterface networkInterface = en.nextElement(); - if(!networkInterface.isLoopback()){ - if(networkInterface.getName().contains("rndis") || networkInterface.getName().contains("usb")){ - return true; - } - } - } - } catch(Exception e){ + hasBtPan = Cmd.runBlockingCmd(new String[] {"ifconfig bt-pan"}, log) == 0; + //Log.d(TAG, "ifconfig result: " + log.toString()); + } catch (Exception e) { e.printStackTrace(); } + return hasBtPan; - return false; } - public static String getWifiAddressRange() { - String interfaceAddress = getWifiInterfaceAddress(); + private static boolean isUsbTetheringEnabled() { + return getUsbInterface() != null; + } + + @VisibleForTesting + static String getAddressRange(String interfaceAddress) { if (interfaceAddress.split("\\.").length == 4) { String result = interfaceAddress.substring(0, interfaceAddress.lastIndexOf(".")); result = result + ".0/24"; @@ -100,72 +141,42 @@ public class TetheringStateManager { return ""; } - @VisibleForTesting - static String getWifiInterfaceAddress() { - NetworkInterface networkInterface = getWlanInterface(); + private static String getInterfaceAddress(NetworkInterface networkInterface) { if (networkInterface != null) { - List ifaceAddresses = networkInterface.getInterfaceAddresses(); - for (InterfaceAddress ifaceAddres : ifaceAddresses) { - if (ifaceAddres.getAddress() instanceof Inet4Address) { - return ifaceAddres.getAddress().getHostAddress(); - } - } + List ifaceAddresses = networkInterface.getInterfaceAddresses(); + for (InterfaceAddress ifaceAddres : ifaceAddresses) { + if (ifaceAddres.getAddress() instanceof Inet4Address) { + return ifaceAddres.getAddress().getHostAddress(); + } + } } return ""; } - private static String getWifiInterfaceName() { - NetworkInterface networkInterface = getWlanInterface(); + private static String getInterfaceName(NetworkInterface networkInterface) { if (networkInterface != null) { return networkInterface.getName(); } return ""; } - private static NetworkInterface getWlanInterface() { + private static NetworkInterface getNetworkInterface(String[] interfaceNames) { try { for(Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { NetworkInterface networkInterface = en.nextElement(); if(!networkInterface.isLoopback()){ - if(networkInterface.getName().contains("wlan") || networkInterface.getName().contains("eth")){ - return networkInterface; + for (String interfaceName : interfaceNames) { + if (networkInterface.getName().contains(interfaceName)) { + return networkInterface; + } } } } } catch(Exception e){ e.printStackTrace(); } - return null; - } - - private static boolean isBluetoothTetheringEnabled() { - StringBuilder log = new StringBuilder(); - boolean hasBtPan = false; - try { - hasBtPan = Cmd.runBlockingCmd(new String[] {"ifconfig bt-pan"}, log) == 0; - //Log.d(TAG, "ifconfig result: " + log.toString()); - } catch (Exception e) { - e.printStackTrace(); - } - return hasBtPan; - - } - static void updateUsbTetheringState() { - TetheringObservable.setUsbTethering(getUsbTetheringState()); - } - - static void updateBluetoothTetheringState() { - TetheringObservable.setBluetoothTethering(isBluetoothTetheringEnabled()); - } - - static void updateWifiTetheringState() { - WifiManagerWrapper manager = getInstance().wifiManager; - try { - TetheringObservable.setWifiTethering(manager.isWifiAPEnabled(), getWifiAddressRange(), getWifiInterfaceName()); - } catch (Exception e) { - e.printStackTrace(); - } + return null; } } -- cgit v1.2.3