diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/event/TunnelErrorEvent.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/event/TunnelErrorEvent.kt
index 8e6fc6c8..d219e6b8 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/event/TunnelErrorEvent.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/event/TunnelErrorEvent.kt
@@ -7,13 +7,30 @@ sealed interface TunnelErrorEvent {
data class StateConflict(val tunnelId: Int, val message: String) : TunnelErrorEvent
- data class InternalFailure(val tunnelId: Int?, val message: String) : TunnelErrorEvent
+ data class InternalFailure(val tunnelId: Int, val message: String) : TunnelErrorEvent
+
+ data class Socks5PortUnavailable(val tunnelId: Int, val port: Int) : TunnelErrorEvent
+
+ data class HttpPortUnavailable(val tunnelId: Int, val port: Int) : TunnelErrorEvent
companion object {
- fun from(throwable: Throwable, id: Int?): TunnelErrorEvent {
+ fun from(throwable: Throwable, id: Int): TunnelErrorEvent {
return when (throwable) {
- is BackendException.StateConflict -> StateConflict(id ?: -1, throwable.message)
- is BackendException.Unauthorized -> InternalFailure(id, "Unauthorized")
+ is BackendException.StateConflict -> {
+ StateConflict(id, throwable.message)
+ }
+ is BackendException.Unauthorized -> {
+ VpnPermissionDenied(id)
+ }
+ is BackendException.InternalError -> {
+ InternalFailure(id, throwable.message)
+ }
+ is BackendException.Socks5PortUnavailable -> {
+ Socks5PortUnavailable(id, throwable.port)
+ }
+ is BackendException.HttpPortUnavailable -> {
+ HttpPortUnavailable(id, throwable.port)
+ }
else -> InternalFailure(id, throwable.message ?: "Unknown")
}
}
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/event/TunnelEventDispatcher.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/event/TunnelEventDispatcher.kt
index a09b8020..11aeb885 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/event/TunnelEventDispatcher.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/event/TunnelEventDispatcher.kt
@@ -61,6 +61,14 @@ class TunnelEventDispatcher(private val notificationManager: TunnelNotificationS
is TunnelErrorEvent.InternalFailure -> {
notificationManager.showError(error.message)
}
+
+ is TunnelErrorEvent.Socks5PortUnavailable -> {
+ notificationManager.showSocks5PortUnavailable(error.port)
+ }
+
+ is TunnelErrorEvent.HttpPortUnavailable -> {
+ notificationManager.showHttpPortUnavailable(error.port)
+ }
}
}
.launchIn(scope)
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/notification/AndroidTunnelNotificationService.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/notification/AndroidTunnelNotificationService.kt
index 85485fa0..9de46f0a 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/notification/AndroidTunnelNotificationService.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/notification/AndroidTunnelNotificationService.kt
@@ -150,6 +150,20 @@ class AndroidTunnelNotificationService(
showError(context.getString(R.string.error_root_denied))
}
+ override suspend fun showSocks5PortUnavailable(port: Int) {
+ val context = notificationService.context
+ val message = context.getString(R.string.error_socks5_port_unavailable, port)
+
+ showError(message)
+ }
+
+ override suspend fun showHttpPortUnavailable(port: Int) {
+ val context = notificationService.context
+ val message = context.getString(R.string.error_http_port_unavailable, port)
+
+ showError(message)
+ }
+
override suspend fun showError(message: String) {
val notification =
diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/notification/TunnelNotificationService.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/notification/TunnelNotificationService.kt
index c48c97c7..042d2da1 100644
--- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/notification/TunnelNotificationService.kt
+++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/notification/TunnelNotificationService.kt
@@ -16,6 +16,10 @@ interface TunnelNotificationService {
suspend fun showStateConflict(tunnelId: Int)
+ suspend fun showSocks5PortUnavailable(port: Int)
+
+ suspend fun showHttpPortUnavailable(port: Int)
+
suspend fun showRootShellAccess()
suspend fun showError(message: String)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 023af939..92e66c44 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -567,4 +567,7 @@
Pre/Post script support
Tunnel name cannot be empty
More options
+
+ SOCKS5 port %1$d is already in use.\nPlease choose a different port.
+ HTTP listener port %1$d is already in use.\nPlease choose a different port.
diff --git a/tunnel/src/main/java/com/zaneschepke/tunnel/ProxyBackend.kt b/tunnel/src/main/java/com/zaneschepke/tunnel/ProxyBackend.kt
index 26d69af6..46a755ca 100644
--- a/tunnel/src/main/java/com/zaneschepke/tunnel/ProxyBackend.kt
+++ b/tunnel/src/main/java/com/zaneschepke/tunnel/ProxyBackend.kt
@@ -10,8 +10,6 @@ internal object ProxyBackend {
external fun awgUpdateProxyTunnelPeers(handle: Int, settings: String): Int
- external fun awgStopProxy()
-
external fun awgTurnProxyTunnelOff(handle: Int)
external fun awgGetProxyConfig(handle: Int): String
diff --git a/tunnel/src/main/java/com/zaneschepke/tunnel/backend/WireGuardTunnelEngine.kt b/tunnel/src/main/java/com/zaneschepke/tunnel/backend/WireGuardTunnelEngine.kt
index 2855282b..686686e8 100644
--- a/tunnel/src/main/java/com/zaneschepke/tunnel/backend/WireGuardTunnelEngine.kt
+++ b/tunnel/src/main/java/com/zaneschepke/tunnel/backend/WireGuardTunnelEngine.kt
@@ -41,6 +41,24 @@ internal class WireGuardTunnelEngine(
}
is BackendMode.Proxy.Standard -> {
val proxyConfig = mode.proxyConfig
+
+ proxyConfig.socks5?.port?.let { port ->
+ if (!isPortAvailable(port)) {
+ throw BackendException.Socks5PortUnavailable(
+ "SOCKS5 port $port is already in use.",
+ port,
+ )
+ }
+ }
+
+ proxyConfig.http?.port?.let { port ->
+ if (!isPortAvailable(port)) {
+ throw BackendException.HttpPortUnavailable(
+ "HTTP listener port $port is already in use.",
+ port,
+ )
+ }
+ }
startProxyTunnel(ifName, config, proxyConfig, false)
}
is BackendMode.Vpn -> {
@@ -61,6 +79,15 @@ internal class WireGuardTunnelEngine(
)
}
+ private fun isPortAvailable(port: Int): Boolean {
+ if (port !in 1..65_535) return false
+ return try {
+ ServerSocket(port).use { true }
+ } catch (e: IOException) {
+ false
+ }
+ }
+
private fun buildConfig(mode: BackendMode): Pair {
var removedPeerEndpoint = false
return mode.config.copy(
diff --git a/tunnel/src/main/java/com/zaneschepke/tunnel/util/BackendException.kt b/tunnel/src/main/java/com/zaneschepke/tunnel/util/BackendException.kt
index 215be876..8f244726 100644
--- a/tunnel/src/main/java/com/zaneschepke/tunnel/util/BackendException.kt
+++ b/tunnel/src/main/java/com/zaneschepke/tunnel/util/BackendException.kt
@@ -6,4 +6,8 @@ sealed class BackendException : Exception() {
class InternalError(override val message: String) : BackendException()
class Unauthorized(override val message: String) : BackendException()
+
+ class Socks5PortUnavailable(override val message: String, val port: Int) : BackendException()
+
+ class HttpPortUnavailable(override val message: String, val port: Int) : BackendException()
}
diff --git a/tunnel/tools/libwg-go/proxy/proxy.go b/tunnel/tools/libwg-go/proxy/proxy.go
index ce471b5f..bc126717 100644
--- a/tunnel/tools/libwg-go/proxy/proxy.go
+++ b/tunnel/tools/libwg-go/proxy/proxy.go
@@ -9,6 +9,7 @@ import (
"net"
"sync"
"syscall"
+ "time"
"github.com/amnezia-vpn/amneziawg-go/conn"
"github.com/amnezia-vpn/amneziawg-go/device"
@@ -21,8 +22,7 @@ import (
import "C"
var (
- ctx context.Context
- cancelFunc context.CancelFunc
+ cancelFuncs map[int32]context.CancelFunc
tag string
virtualTunnelHandles map[int32]*wireproxyawg.VirtualTun
)
@@ -30,6 +30,7 @@ var (
func init() {
tag = "AwgProxy"
virtualTunnelHandles = make(map[int32]*wireproxyawg.VirtualTun)
+ cancelFuncs = make(map[int32]context.CancelFunc)
}
//export awgStartProxy
@@ -134,13 +135,14 @@ func awgStartProxy(interfaceName string, config string, uapiPath string, bypass
virtualTunnelHandles[handle] = virtualTun
// Create cancellable context
- ctx, cancelFunc = context.WithCancel(context.Background())
+ tunnelCtx, tunnelCancel := context.WithCancel(context.Background())
+ cancelFuncs[handle] = tunnelCancel
// Spawn all routines with context
for _, spawner := range conf.Routines {
shared.LogDebug(tag, "Spawning routine..")
go func(s wireproxyawg.RoutineSpawner) {
- if err := s.SpawnRoutine(ctx, virtualTun); err != nil {
+ if err := s.SpawnRoutine(tunnelCtx, virtualTun); err != nil {
shared.LogError(tag, "Routine failed: %v", err)
}
}(spawner)
@@ -195,24 +197,6 @@ func awgGetProxyConfig(tunnelHandle int32) *C.char {
return C.CString(settings)
}
-//export awgStopProxy
-func awgStopProxy() {
- if cancelFunc != nil {
- shared.LogDebug(tag, "Stopping proxy routines..")
- cancelFunc()
- cancelFunc = nil
- }
- handles := make([]int32, 0, len(virtualTunnelHandles))
- for h := range virtualTunnelHandles {
- handles = append(handles, h)
- }
- for _, handle := range handles {
- awgTurnProxyTunnelOff(handle)
- }
- virtualTunnelHandles = make(map[int32]*wireproxyawg.VirtualTun)
- shared.LogDebug(tag, "Proxy fully reset: %d handles closed", len(handles))
-}
-
// control hook to bypass sockets
func protectControlFunc(network, address string, c syscall.RawConn) error {
var opErr error
@@ -239,6 +223,12 @@ func awgTurnProxyTunnelOff(virtualTunnelHandle int32) {
}
shared.LogDebug(tag, "Tearing down tunnel %d", virtualTunnelHandle)
+ if cancel, exists := cancelFuncs[virtualTunnelHandle]; exists {
+ cancel()
+ delete(cancelFuncs, virtualTunnelHandle)
+ time.Sleep(50 * time.Millisecond)
+ }
+
// Close UAPI listener and underlying file
if virtualTun.Uapi != nil {
virtualTun.Uapi.Close()
diff --git a/tunnel/tools/libwg-go/proxy/proxy_jni.c b/tunnel/tools/libwg-go/proxy/proxy_jni.c
index fd91182a..747b0eda 100644
--- a/tunnel/tools/libwg-go/proxy/proxy_jni.c
+++ b/tunnel/tools/libwg-go/proxy/proxy_jni.c
@@ -76,11 +76,6 @@ JNIEXPORT jint JNICALL Java_com_zaneschepke_tunnel_ProxyBackend_awgStartProxy(JN
return ret;
}
-JNIEXPORT void JNICALL Java_com_zaneschepke_tunnel_ProxyBackend_awgStopProxy(JNIEnv *env, jclass c)
-{
- awgStopProxy();
-}
-
JNIEXPORT void JNICALL Java_com_zaneschepke_tunnel_ProxyBackend_awgTurnProxyTunnelOff(JNIEnv *env, jclass c, jint handle)
{
awgTurnProxyTunnelOff(handle);