diff --git a/app/src/main/java/com/genymobile/gnirehtet/GnirehtetActivity.java b/app/src/main/java/com/genymobile/gnirehtet/GnirehtetActivity.java index 602d757..3c33061 100644 --- a/app/src/main/java/com/genymobile/gnirehtet/GnirehtetActivity.java +++ b/app/src/main/java/com/genymobile/gnirehtet/GnirehtetActivity.java @@ -23,6 +23,7 @@ public class GnirehtetActivity extends Activity { public static final String EXTRA_DNS_SERVERS = "dnsServers"; public static final String EXTRA_ROUTES = "routes"; + public static final String EXTRA_STOP_ON_DISCONNECT = "stopOnDisconnect"; private static final int VPN_REQUEST_CODE = 0; @@ -59,7 +60,8 @@ private static VpnConfiguration createConfig(Intent intent) { if (routes == null) { routes = new String[0]; } - return new VpnConfiguration(Net.toInetAddresses(dnsServers), Net.toCIDRs(routes)); + boolean stopOnDisconnect = intent.getBooleanExtra(EXTRA_STOP_ON_DISCONNECT, false); + return new VpnConfiguration(Net.toInetAddresses(dnsServers), Net.toCIDRs(routes), stopOnDisconnect); } private boolean startGnirehtet(VpnConfiguration config) { diff --git a/app/src/main/java/com/genymobile/gnirehtet/GnirehtetService.java b/app/src/main/java/com/genymobile/gnirehtet/GnirehtetService.java index 7e8b716..5374ea6 100644 --- a/app/src/main/java/com/genymobile/gnirehtet/GnirehtetService.java +++ b/app/src/main/java/com/genymobile/gnirehtet/GnirehtetService.java @@ -48,7 +48,7 @@ public class GnirehtetService extends VpnService { private static final int MTU = 0x4000; private final Notifier notifier = new Notifier(this); - private final Handler handler = new RelayTunnelConnectionStateHandler(this); + private final RelayTunnelConnectionStateHandler handler = new RelayTunnelConnectionStateHandler(this); private ParcelFileDescriptor vpnInterface = null; private Forwarder forwarder; @@ -111,6 +111,7 @@ private void startVpn(VpnConfiguration config) { @SuppressWarnings("checkstyle:MagicNumber") private boolean setupVpn(VpnConfiguration config) { + handler.setStopOnDisconnect(config.getIsStopOnDisconnect()); Builder builder = new Builder(); builder.addAddress(VPN_ADDRESS, 32); builder.setSession(getString(R.string.app_name)); @@ -206,6 +207,7 @@ private void close() { private static final class RelayTunnelConnectionStateHandler extends Handler { private final GnirehtetService vpnService; + private boolean stopOnDisconnect = false; private RelayTunnelConnectionStateHandler(GnirehtetService vpnService) { this.vpnService = vpnService; @@ -224,10 +226,18 @@ public void handleMessage(Message message) { break; case RelayTunnelListener.MSG_RELAY_TUNNEL_DISCONNECTED: Log.d(TAG, "Relay tunnel disconnected"); - vpnService.notifier.setFailure(true); + if (stopOnDisconnect) { + stop(vpnService); + } else { + vpnService.notifier.setFailure(true); + } break; default: } } + + public void setStopOnDisconnect(boolean stopOnDisconnect) { + this.stopOnDisconnect = stopOnDisconnect; + } } } diff --git a/app/src/main/java/com/genymobile/gnirehtet/VpnConfiguration.java b/app/src/main/java/com/genymobile/gnirehtet/VpnConfiguration.java index 9027a18..469b225 100644 --- a/app/src/main/java/com/genymobile/gnirehtet/VpnConfiguration.java +++ b/app/src/main/java/com/genymobile/gnirehtet/VpnConfiguration.java @@ -26,15 +26,18 @@ public class VpnConfiguration implements Parcelable { private final InetAddress[] dnsServers; private final CIDR[] routes; + private final boolean stopOnDisconnect; public VpnConfiguration() { this.dnsServers = new InetAddress[0]; this.routes = new CIDR[0]; + this.stopOnDisconnect = false; } - public VpnConfiguration(InetAddress[] dnsServers, CIDR[] routes) { + public VpnConfiguration(InetAddress[] dnsServers, CIDR[] routes, boolean stopOnDisconnect) { this.dnsServers = dnsServers; this.routes = routes; + this.stopOnDisconnect = stopOnDisconnect; } private VpnConfiguration(Parcel source) { @@ -48,6 +51,7 @@ private VpnConfiguration(Parcel source) { throw new AssertionError("Invalid address", e); } routes = source.createTypedArray(CIDR.CREATOR); + stopOnDisconnect = source.readByte() == 1; } public InetAddress[] getDnsServers() { @@ -58,6 +62,10 @@ public CIDR[] getRoutes() { return routes; } + public boolean getIsStopOnDisconnect() { + return stopOnDisconnect; + } + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(dnsServers.length); @@ -65,6 +73,7 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeByteArray(addr.getAddress()); } dest.writeTypedArray(routes, 0); + dest.writeByte(stopOnDisconnect ? (byte) 1 : (byte) 0); } @Override diff --git a/relay-rust/src/cli_args.rs b/relay-rust/src/cli_args.rs index 438427f..3c77599 100644 --- a/relay-rust/src/cli_args.rs +++ b/relay-rust/src/cli_args.rs @@ -19,6 +19,7 @@ pub const PARAM_SERIAL: u8 = 1; pub const PARAM_DNS_SERVERS: u8 = 1 << 1; pub const PARAM_ROUTES: u8 = 1 << 2; pub const PARAM_PORT: u8 = 1 << 3; +pub const PARAM_STOP_ON_DISCONNECT: u8 = 1 << 4; pub const DEFAULT_PORT: u16 = 31416; @@ -27,6 +28,7 @@ pub struct CommandLineArguments { dns_servers: Option, routes: Option, port: u16, + stop_on_disconnect: bool, } impl CommandLineArguments { @@ -36,6 +38,7 @@ impl CommandLineArguments { let mut dns_servers = None; let mut routes = None; let mut port = 0; + let mut stop_on_disconnect = false; let mut iter = args.into_iter(); while let Some(arg) = iter.next() { @@ -70,6 +73,11 @@ impl CommandLineArguments { } else { return Err(String::from("Missing -p parameter")); } + } else if (accepted_parameters & PARAM_STOP_ON_DISCONNECT) != 0 && "-s" == arg { + if stop_on_disconnect { + return Err(String::from("Stop on disconnect already set")); + } + stop_on_disconnect = true; } else if (accepted_parameters & PARAM_SERIAL) != 0 && serial.is_none() { serial = Some(arg); } else { @@ -84,6 +92,7 @@ impl CommandLineArguments { dns_servers, routes, port, + stop_on_disconnect, }) } @@ -102,13 +111,18 @@ impl CommandLineArguments { pub fn port(&self) -> u16 { self.port } + + pub fn stop_on_disconnect(&self) -> bool { + self.stop_on_disconnect + } } #[cfg(test)] mod tests { use super::*; - const ACCEPT_ALL: u8 = PARAM_SERIAL | PARAM_DNS_SERVERS | PARAM_ROUTES; + const ACCEPT_ALL: u8 = + PARAM_SERIAL | PARAM_DNS_SERVERS | PARAM_ROUTES | PARAM_STOP_ON_DISCONNECT; #[test] fn test_no_args() { @@ -178,4 +192,18 @@ mod tests { let raw_args = vec!["-r"]; assert!(CommandLineArguments::parse(ACCEPT_ALL, raw_args).is_err()); } + + #[test] + fn test_stop_on_disconnect_parameter() { + let raw_args = vec!["-s"]; + let args = CommandLineArguments::parse(ACCEPT_ALL, raw_args).unwrap(); + assert!(args.stop_on_disconnect()) + } + + #[test] + fn test_no_stop_on_disconnect_parameter() { + let raw_args = Vec::<&str>::new(); + let args = CommandLineArguments::parse(ACCEPT_ALL, raw_args).unwrap(); + assert!(!args.stop_on_disconnect()) + } } diff --git a/relay-rust/src/main.rs b/relay-rust/src/main.rs index cb80e9c..9831a25 100644 --- a/relay-rust/src/main.rs +++ b/relay-rust/src/main.rs @@ -155,6 +155,7 @@ impl Command for RunCommand { | cli_args::PARAM_DNS_SERVERS | cli_args::PARAM_ROUTES | cli_args::PARAM_PORT + | cli_args::PARAM_STOP_ON_DISCONNECT } fn description(&self) -> &'static str { @@ -171,6 +172,7 @@ impl Command for RunCommand { args.dns_servers(), args.routes(), args.port(), + args.stop_on_disconnect(), ) } } @@ -181,7 +183,10 @@ impl Command for AutorunCommand { } fn accepted_parameters(&self) -> u8 { - cli_args::PARAM_DNS_SERVERS | cli_args::PARAM_ROUTES | cli_args::PARAM_PORT + cli_args::PARAM_DNS_SERVERS + | cli_args::PARAM_ROUTES + | cli_args::PARAM_PORT + | cli_args::PARAM_STOP_ON_DISCONNECT } fn description(&self) -> &'static str { @@ -191,7 +196,12 @@ impl Command for AutorunCommand { } fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError> { - cmd_autorun(args.dns_servers(), args.routes(), args.port()) + cmd_autorun( + args.dns_servers(), + args.routes(), + args.port(), + args.stop_on_disconnect(), + ) } } @@ -205,6 +215,7 @@ impl Command for StartCommand { | cli_args::PARAM_DNS_SERVERS | cli_args::PARAM_ROUTES | cli_args::PARAM_PORT + | cli_args::PARAM_STOP_ON_DISCONNECT } fn description(&self) -> &'static str { @@ -217,6 +228,10 @@ impl Command for StartCommand { Otherwise, use 0.0.0.0/0 (redirect the whole traffic).\n\ If -p is given, then make the relay server listen on the specified\n\ port. Otherwise, use port 31416.\n\ + If -s is given, the Android client will stop if the connecion is\n\ + lost (e.g. cable unplugged). Otherwise, the client will continue\n\ + running, blocking all outgoing connections until connected again\n\ + or stopped manually.\n\ If the client is already started, then do nothing, and ignore\n\ the other parameters.\n\ 10.0.2.2 is mapped to the host 'localhost'." @@ -228,6 +243,7 @@ impl Command for StartCommand { args.dns_servers(), args.routes(), args.port(), + args.stop_on_disconnect(), ) } } @@ -238,7 +254,10 @@ impl Command for AutostartCommand { } fn accepted_parameters(&self) -> u8 { - cli_args::PARAM_DNS_SERVERS | cli_args::PARAM_ROUTES | cli_args::PARAM_PORT + cli_args::PARAM_DNS_SERVERS + | cli_args::PARAM_ROUTES + | cli_args::PARAM_PORT + | cli_args::PARAM_STOP_ON_DISCONNECT } fn description(&self) -> &'static str { @@ -249,7 +268,12 @@ impl Command for AutostartCommand { } fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError> { - cmd_autostart(args.dns_servers(), args.routes(), args.port()) + cmd_autostart( + args.dns_servers(), + args.routes(), + args.port(), + args.stop_on_disconnect(), + ) } } @@ -283,6 +307,7 @@ impl Command for RestartCommand { | cli_args::PARAM_DNS_SERVERS | cli_args::PARAM_ROUTES | cli_args::PARAM_PORT + | cli_args::PARAM_STOP_ON_DISCONNECT } fn description(&self) -> &'static str { @@ -296,6 +321,7 @@ impl Command for RestartCommand { args.dns_servers(), args.routes(), args.port(), + args.stop_on_disconnect(), )?; Ok(()) } @@ -362,9 +388,10 @@ fn cmd_run( dns_servers: Option<&str>, routes: Option<&str>, port: u16, + stop_on_disconnect: bool, ) -> Result<(), CommandExecutionError> { // start in parallel so that the relay server is ready when the client connects - async_start(serial, dns_servers, routes, port); + async_start(serial, dns_servers, routes, port, stop_on_disconnect); let ctrlc_serial = serial.map(String::from); ctrlc::set_handler(move || { @@ -386,6 +413,7 @@ fn cmd_autorun( dns_servers: Option<&str>, routes: Option<&str>, port: u16, + stop_on_disconnect: bool, ) -> Result<(), CommandExecutionError> { { let autostart_dns_servers = dns_servers.map(String::from); @@ -393,7 +421,7 @@ fn cmd_autorun( thread::spawn(move || { let dns_servers = autostart_dns_servers.as_ref().map(String::as_ref); let routes = autostart_routes.as_ref().map(String::as_ref); - if let Err(err) = cmd_autostart(dns_servers, routes, port) { + if let Err(err) = cmd_autostart(dns_servers, routes, port, stop_on_disconnect) { error!(target: TAG, "Cannot auto start clients: {}", err); } }); @@ -407,6 +435,7 @@ fn cmd_start( dns_servers: Option<&str>, routes: Option<&str>, port: u16, + stop_on_disconnect: bool, ) -> Result<(), CommandExecutionError> { if must_install_client(serial)? { cmd_install(serial)?; @@ -433,6 +462,9 @@ fn cmd_start( if let Some(routes) = routes { adb_args.append(&mut vec!["--esa", "routes", routes]); } + if stop_on_disconnect { + adb_args.append(&mut vec!["--ez", "stopOnDisconnect", "true"]); + } exec_adb(serial, adb_args) } @@ -440,13 +472,14 @@ fn cmd_autostart( dns_servers: Option<&str>, routes: Option<&str>, port: u16, + stop_on_disconnect: bool, ) -> Result<(), CommandExecutionError> { let start_dns_servers = dns_servers.map(String::from); let start_routes = routes.map(String::from); let mut adb_monitor = AdbMonitor::new(Box::new(move |serial: &str| { let dns_servers = start_dns_servers.as_ref().map(String::as_ref); let routes = start_routes.as_ref().map(String::as_ref); - async_start(Some(serial), dns_servers, routes, port) + async_start(Some(serial), dns_servers, routes, port, stop_on_disconnect) })); adb_monitor.monitor(); Ok(()) @@ -485,7 +518,13 @@ fn cmd_relay(port: u16) -> Result<(), CommandExecutionError> { Ok(()) } -fn async_start(serial: Option<&str>, dns_servers: Option<&str>, routes: Option<&str>, port: u16) { +fn async_start( + serial: Option<&str>, + dns_servers: Option<&str>, + routes: Option<&str>, + port: u16, + stop_on_disconnect: bool, +) { let start_serial = serial.map(String::from); let start_dns_servers = dns_servers.map(String::from); let start_routes = routes.map(String::from); @@ -493,7 +532,7 @@ fn async_start(serial: Option<&str>, dns_servers: Option<&str>, routes: Option<& let serial = start_serial.as_ref().map(String::as_ref); let dns_servers = start_dns_servers.as_ref().map(String::as_ref); let routes = start_routes.as_ref().map(String::as_ref); - if let Err(err) = cmd_start(serial, dns_servers, routes, port) { + if let Err(err) = cmd_start(serial, dns_servers, routes, port, stop_on_disconnect) { error!(target: TAG, "Cannot start client: {}", err); } }); @@ -605,6 +644,9 @@ fn append_command_usage(msg: &mut String, command: &dyn Command) { if (accepted_parameters & cli_args::PARAM_ROUTES) != 0 { msg.push_str(" [-r ROUTE[,ROUTE2,...]]"); } + if (accepted_parameters & cli_args::PARAM_STOP_ON_DISCONNECT) != 0 { + msg.push_str(" [-s]"); + } msg.push('\n'); for desc_line in command.description().split('\n') { msg.push_str(" ");