diff options
Diffstat (limited to 'app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt')
-rw-r--r-- | app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt | 105 |
1 files changed, 66 insertions, 39 deletions
diff --git a/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt b/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt index 12ac17f..cd1dd74 100644 --- a/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt +++ b/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt | |||
@@ -5,7 +5,10 @@ import android.content.Intent | |||
5 | import android.net.Uri | 5 | import android.net.Uri |
6 | import android.net.VpnService | 6 | import android.net.VpnService |
7 | import android.os.ParcelFileDescriptor | 7 | import android.os.ParcelFileDescriptor |
8 | import android.util.Log | ||
9 | import org.apache.commons.configuration2.ex.ConversionException | ||
8 | import org.pacien.tincapp.BuildConfig | 10 | import org.pacien.tincapp.BuildConfig |
11 | import org.pacien.tincapp.R | ||
9 | import org.pacien.tincapp.commands.Tinc | 12 | import org.pacien.tincapp.commands.Tinc |
10 | import org.pacien.tincapp.commands.Tincd | 13 | import org.pacien.tincapp.commands.Tincd |
11 | import org.pacien.tincapp.context.App | 14 | import org.pacien.tincapp.context.App |
@@ -13,9 +16,8 @@ import org.pacien.tincapp.context.AppPaths | |||
13 | import org.pacien.tincapp.data.VpnInterfaceConfiguration | 16 | import org.pacien.tincapp.data.VpnInterfaceConfiguration |
14 | import org.pacien.tincapp.extensions.Java.applyIgnoringException | 17 | import org.pacien.tincapp.extensions.Java.applyIgnoringException |
15 | import org.pacien.tincapp.extensions.VpnServiceBuilder.applyCfg | 18 | import org.pacien.tincapp.extensions.VpnServiceBuilder.applyCfg |
16 | import org.pacien.tincapp.intent.action.ACTION_START_SERVICE | ||
17 | import org.pacien.tincapp.intent.action.ACTION_STOP_SERVICE | ||
18 | import org.pacien.tincapp.intent.action.TINC_SCHEME | 19 | import org.pacien.tincapp.intent.action.TINC_SCHEME |
20 | import java.io.FileNotFoundException | ||
19 | import java.io.IOException | 21 | import java.io.IOException |
20 | 22 | ||
21 | /** | 23 | /** |
@@ -23,65 +25,90 @@ import java.io.IOException | |||
23 | */ | 25 | */ |
24 | class TincVpnService : VpnService() { | 26 | class TincVpnService : VpnService() { |
25 | 27 | ||
26 | override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { | 28 | override fun onDestroy() { |
27 | when (intent.action) { | 29 | stopVpn() |
28 | ACTION_START_SERVICE -> startVpn(intent.data.schemeSpecificPart) | 30 | super.onDestroy() |
29 | ACTION_STOP_SERVICE -> onDestroy() | ||
30 | } | ||
31 | |||
32 | return Service.START_STICKY | ||
33 | } | 31 | } |
34 | 32 | ||
35 | override fun onDestroy() { | 33 | override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { |
36 | connected = false | 34 | if (isConnected()) stopVpn() |
37 | 35 | startVpn(intent.data.schemeSpecificPart) | |
38 | try { | 36 | return Service.START_REDELIVER_INTENT |
39 | if (netName != null) Tinc.stop(netName!!) | ||
40 | fd?.close() | ||
41 | } catch (e: IOException) { | ||
42 | e.printStackTrace() | ||
43 | } finally { | ||
44 | netName = null | ||
45 | interfaceCfg = null | ||
46 | fd = null | ||
47 | super.onDestroy() | ||
48 | } | ||
49 | } | 37 | } |
50 | 38 | ||
51 | private fun startVpn(netName: String) { | 39 | private fun startVpn(netName: String) { |
52 | if (isConnected()) onDestroy() | 40 | if (netName.isBlank()) |
53 | TincVpnService.netName = netName | 41 | return reportError(resources.getString(R.string.message_no_network_name_provided), docTopic = "intent-api") |
54 | TincVpnService.interfaceCfg = VpnInterfaceConfiguration.fromIfaceConfiguration(AppPaths.netConfFile(netName)) | 42 | |
55 | 43 | if (!AppPaths.confDir(netName).exists()) | |
56 | val net = Builder().setSession(netName).applyCfg(TincVpnService.interfaceCfg!!) | 44 | return reportError(resources.getString(R.string.message_no_configuration_for_network_format, netName), docTopic = "configuration") |
57 | applyIgnoringException(net::addDisallowedApplication, BuildConfig.APPLICATION_ID) | 45 | |
58 | 46 | Log.i(TAG, "Starting tinc daemon for network \"$netName\".") | |
59 | try { | 47 | |
60 | fd = net.establish() | 48 | val interfaceCfg = try { |
61 | Tincd.start(netName, fd!!.fd) | 49 | VpnInterfaceConfiguration.fromIfaceConfiguration(AppPaths.existing(AppPaths.netConfFile(netName))) |
62 | } catch (e: IOException) { | 50 | } catch (e: FileNotFoundException) { |
63 | e.printStackTrace() | 51 | return reportError(resources.getString(R.string.message_network_config_not_found_format, e.message!!), e, "configuration") |
52 | } catch (e: ConversionException) { | ||
53 | return reportError(resources.getString(R.string.message_network_config_invalid_format, e.message!!), e, "network-interface") | ||
64 | } | 54 | } |
65 | 55 | ||
66 | connected = true | 56 | val fd = try { |
57 | Builder().setSession(netName) | ||
58 | .applyCfg(interfaceCfg) | ||
59 | .also { applyIgnoringException(it::addDisallowedApplication, BuildConfig.APPLICATION_ID) } | ||
60 | .establish() | ||
61 | } catch (e: IllegalArgumentException) { | ||
62 | return reportError(resources.getString(R.string.message_network_config_invalid_format, e.message!!), e, "network-interface") | ||
63 | } | ||
64 | |||
65 | Tincd.start(netName, fd!!.fd) | ||
66 | setState(true, netName, interfaceCfg, fd) | ||
67 | Log.i(TAG, "tinc daemon started.") | ||
68 | } | ||
69 | |||
70 | private fun reportError(msg: String, e: Throwable? = null, docTopic: String? = null) { | ||
71 | if (e != null) | ||
72 | Log.e(TAG, msg, e) | ||
73 | else | ||
74 | Log.e(TAG, msg) | ||
75 | |||
76 | App.alert(R.string.title_unable_to_start_tinc, msg, | ||
77 | if (docTopic != null) resources.getString(R.string.app_doc_url_format, docTopic) else null) | ||
67 | } | 78 | } |
68 | 79 | ||
69 | companion object { | 80 | companion object { |
70 | 81 | ||
82 | val TAG = this::class.java.canonicalName!! | ||
83 | |||
71 | private var connected: Boolean = false | 84 | private var connected: Boolean = false |
72 | private var netName: String? = null | 85 | private var netName: String? = null |
73 | private var interfaceCfg: VpnInterfaceConfiguration? = null | 86 | private var interfaceCfg: VpnInterfaceConfiguration? = null |
74 | private var fd: ParcelFileDescriptor? = null | 87 | private var fd: ParcelFileDescriptor? = null |
75 | 88 | ||
89 | private fun setState(connected: Boolean, netName: String?, interfaceCfg: VpnInterfaceConfiguration?, fd: ParcelFileDescriptor?) { | ||
90 | TincVpnService.connected = connected | ||
91 | TincVpnService.netName = netName | ||
92 | TincVpnService.interfaceCfg = interfaceCfg | ||
93 | TincVpnService.fd = fd | ||
94 | } | ||
95 | |||
76 | fun startVpn(netName: String) { | 96 | fun startVpn(netName: String) { |
77 | App.getContext().startService(Intent(App.getContext(), TincVpnService::class.java) | 97 | App.getContext().startService(Intent(App.getContext(), TincVpnService::class.java) |
78 | .setAction(ACTION_START_SERVICE) | ||
79 | .setData(Uri.Builder().scheme(TINC_SCHEME).opaquePart(netName).build())) | 98 | .setData(Uri.Builder().scheme(TINC_SCHEME).opaquePart(netName).build())) |
80 | } | 99 | } |
81 | 100 | ||
82 | fun stopVpn() { | 101 | fun stopVpn() { |
83 | App.getContext().startService(Intent(App.getContext(), TincVpnService::class.java) | 102 | try { |
84 | .setAction(ACTION_STOP_SERVICE)) | 103 | Log.i(TAG, "Stopping any running tinc daemon.") |
104 | if (netName != null) Tinc.stop(netName!!) | ||
105 | fd?.close() | ||
106 | Log.i(TAG, "All tinc daemons stopped.") | ||
107 | } catch (e: IOException) { | ||
108 | Log.wtf(TAG, e) | ||
109 | } finally { | ||
110 | setState(false, null, null, null) | ||
111 | } | ||
85 | } | 112 | } |
86 | 113 | ||
87 | fun getCurrentNetName() = netName | 114 | fun getCurrentNetName() = netName |