1
1
package org.amnezia.vpn.protocol.wireguard
2
2
3
3
import android.net.VpnService.Builder
4
- import java.io.IOException
5
- import java.util.Locale
4
+ import kotlinx.coroutines.CoroutineScope
6
5
import kotlinx.coroutines.Dispatchers
6
+ import kotlinx.coroutines.Job
7
+ import kotlinx.coroutines.cancel
7
8
import kotlinx.coroutines.delay
8
- import kotlinx.coroutines.withContext
9
+ import kotlinx.coroutines.launch
9
10
import org.amnezia.awg.GoBackend
10
11
import org.amnezia.vpn.protocol.Protocol
11
12
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
@@ -27,6 +28,8 @@ open class Wireguard : Protocol() {
27
28
28
29
private var tunnelHandle: Int = - 1
29
30
protected open val ifName: String = " amn0"
31
+ private lateinit var scope: CoroutineScope
32
+ private var statusJob: Job ? = null
30
33
31
34
override val statistics: Statistics
32
35
get() {
@@ -49,46 +52,17 @@ open class Wireguard : Protocol() {
49
52
50
53
override fun internalInit () {
51
54
if (! isInitialized) loadSharedLibrary(context, " wg-go" )
55
+ if (this ::scope.isInitialized) {
56
+ scope.cancel()
57
+ }
58
+ scope = CoroutineScope (Dispatchers .IO )
52
59
}
53
60
54
61
override suspend fun startVpn (config : JSONObject , vpnBuilder : Builder , protect : (Int ) -> Boolean ) {
55
62
val wireguardConfig = parseConfig(config)
56
- val startTime = System .currentTimeMillis()
57
63
start(wireguardConfig, vpnBuilder, protect)
58
- waitForConnection(startTime)
59
- state.value = CONNECTED
60
64
}
61
65
62
- private suspend fun waitForConnection (startTime : Long ) {
63
- Log .d(TAG , " Waiting for connection" )
64
- withContext(Dispatchers .IO ) {
65
- val time = String .format(Locale .ROOT ," %.3f" , startTime / 1000.0 )
66
- try {
67
- delay(1000 )
68
- var log = getLogcat(time)
69
- Log .v(TAG , " First waiting log: $log " )
70
- // check that there is a connection log,
71
- // to avoid infinite connection
72
- if (! log.contains(" Attaching to interface" )) {
73
- Log .w(TAG , " Logs do not contain a connection log" )
74
- return @withContext
75
- }
76
- while (! log.contains(" Received handshake response" )) {
77
- delay(1000 )
78
- log = getLogcat(time)
79
- }
80
- } catch (e: IOException ) {
81
- Log .e(TAG , " Failed to get logcat: $e " )
82
- }
83
- }
84
- }
85
-
86
- private fun getLogcat (time : String ): String =
87
- ProcessBuilder (" logcat" , " --buffer=main" , " --format=raw" , " *:S AmneziaWG/awg0" , " -t" , time)
88
- .redirectErrorStream(true )
89
- .start()
90
- .inputStream.reader().readText()
91
-
92
66
protected open fun parseConfig (config : JSONObject ): WireguardConfig {
93
67
val configData = config.getJSONObject(" wireguard_config_data" )
94
68
return WireguardConfig .build {
@@ -178,13 +152,52 @@ open class Wireguard : Protocol() {
178
152
tunnelHandle = - 1
179
153
throw VpnStartException (" Protect VPN interface: permission not granted or revoked" )
180
154
}
155
+ launchStatusJob()
156
+ }
157
+
158
+ private fun launchStatusJob () {
159
+ Log .d(TAG , " Launch status job" )
160
+ statusJob = scope.launch {
161
+ while (true ) {
162
+ val lastHandshake = getLastHandshake()
163
+ Log .v(TAG , " lastHandshake=$lastHandshake " )
164
+ if (lastHandshake == 0L ) {
165
+ delay(1000 )
166
+ continue
167
+ }
168
+ if (lastHandshake == - 2L || lastHandshake > 0L ) state.value = CONNECTED
169
+ else if (lastHandshake == - 1L ) state.value = DISCONNECTED
170
+ statusJob = null
171
+ break
172
+ }
173
+ }
174
+ }
175
+
176
+ private fun getLastHandshake (): Long {
177
+ if (tunnelHandle == - 1 ) {
178
+ Log .e(TAG , " Trying to get config of a non-existent tunnel" )
179
+ return - 1
180
+ }
181
+ val config = GoBackend .awgGetConfig(tunnelHandle)
182
+ if (config == null ) {
183
+ Log .e(TAG , " Failed to get tunnel config" )
184
+ return - 2
185
+ }
186
+ val lastHandshake = config.lines().find { it.startsWith(" last_handshake_time_sec=" ) }?.substring(24 )?.toLong()
187
+ if (lastHandshake == null ) {
188
+ Log .e(TAG , " Failed to get last_handshake_time_sec" )
189
+ return - 2
190
+ }
191
+ return lastHandshake
181
192
}
182
193
183
194
override fun stopVpn () {
184
195
if (tunnelHandle == - 1 ) {
185
196
Log .w(TAG , " Tunnel already down" )
186
197
return
187
198
}
199
+ statusJob?.cancel()
200
+ statusJob = null
188
201
val handleToClose = tunnelHandle
189
202
tunnelHandle = - 1
190
203
GoBackend .awgTurnOff(handleToClose)
0 commit comments