Skip to content

Commit fe0072d

Browse files
committed
fix(mobile): propagate SOCKS5 user/pass auth through FakeDNS proxy dial
1 parent 9f000d8 commit fe0072d

4 files changed

Lines changed: 111 additions & 32 deletions

File tree

android/app/src/main/java/com/gooserelay/gooserelayvpn/service/GooseRelayVpnService.kt

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -340,10 +340,27 @@ class GooseRelayVpnService : VpnService() {
340340

341341
// TUN module is included in mobile package
342342
val tunClass = Class.forName("mobile.Mobile")
343-
val startMethod = tunClass.getMethod("startTunBridge", Long::class.java, Long::class.java, String::class.java)
343+
val startMethod = tunClass.getMethod(
344+
"startTunBridge",
345+
Long::class.java,
346+
Long::class.java,
347+
String::class.java,
348+
String::class.java,
349+
String::class.java,
350+
)
344351

345-
// Start TUN bridge: fd, mtu, socksAddr
346-
startMethod.invoke(null, vpnInterface!!.fd.toLong(), 1500L, "127.0.0.1:$socksPort")
352+
// Start TUN bridge: fd, mtu, socksAddr, socksUser, socksPass
353+
// The Go core's SOCKS5 server enforces user/pass auth when
354+
// both fields are non-empty; we forward them so the FakeDNS
355+
// proxy can negotiate through it.
356+
startMethod.invoke(
357+
null,
358+
vpnInterface!!.fd.toLong(),
359+
1500L,
360+
"127.0.0.1:$socksPort",
361+
profile.socksUser,
362+
profile.socksPass,
363+
)
347364

348365
tunBridgeActive = true
349366
VpnManager.appendLog("Go TUN bridge started (DNS will be resolved remotely)")

mobile/mobile.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -303,12 +303,12 @@ func StopTun() {
303303
// TUN Bridge wrapper functions (calls mobile/tun subpackage)
304304

305305
// StartTunBridge starts the TUN bridge with DNS interception using FakeDNS proxy.
306-
func StartTunBridge(tunFd int64, mtu int64, socksAddr string) error {
307-
proxyAddr, err := tun.StartFakeDNSProxy(socksAddr)
306+
func StartTunBridge(tunFd int64, mtu int64, socksAddr, socksUser, socksPass string) error {
307+
proxyAddr, err := tun.StartFakeDNSProxy(socksAddr, socksUser, socksPass)
308308
if err != nil {
309309
return err
310310
}
311-
311+
312312
safeFd := dupFd(int(tunFd))
313313

314314
key := &engine.Key{
@@ -325,7 +325,7 @@ func StartTunBridge(tunFd int64, mtu int64, socksAddr string) error {
325325
engine.Start()
326326
tunOwnedFd = int32(safeFd)
327327
engineMu.Unlock()
328-
328+
329329
mu.Lock()
330330
tunBridgeRunning = true
331331
mu.Unlock()

mobile/tun/fakedns_proxy.go

Lines changed: 84 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@ package tun
33
import (
44
"context"
55
"encoding/binary"
6+
"fmt"
67
"io"
78
"net"
89
"sync"
910
)
1011

1112
type FakeDNSProxy struct {
1213
RealSocksAddr string
14+
SocksUser string
15+
SocksPass string
1316
LocalPort int
1417
dnsMap *DNSMapper
1518
listener net.Listener
@@ -18,16 +21,91 @@ type FakeDNSProxy struct {
1821
wg sync.WaitGroup
1922
}
2023

21-
func NewFakeDNSProxy(realSocksAddr string, dnsMap *DNSMapper) *FakeDNSProxy {
24+
func NewFakeDNSProxy(realSocksAddr, socksUser, socksPass string, dnsMap *DNSMapper) *FakeDNSProxy {
2225
ctx, cancel := context.WithCancel(context.Background())
2326
return &FakeDNSProxy{
2427
RealSocksAddr: realSocksAddr,
28+
SocksUser: socksUser,
29+
SocksPass: socksPass,
2530
dnsMap: dnsMap,
2631
ctx: ctx,
2732
cancel: cancel,
2833
}
2934
}
3035

36+
// dialRealSocks opens a TCP connection to the upstream SOCKS5 server and
37+
// completes the auth handshake. If [SocksUser] and [SocksPass] are both
38+
// set, the user/pass method (0x02) is offered; otherwise NO_AUTH (0x00).
39+
// Returns the negotiated upstream connection, or nil + an error.
40+
func (p *FakeDNSProxy) dialRealSocks() (net.Conn, error) {
41+
realConn, err := net.Dial("tcp", p.RealSocksAddr)
42+
if err != nil {
43+
return nil, err
44+
}
45+
46+
// Greeting: offer NO_AUTH and (if configured) USERNAME/PASSWORD.
47+
// RFC 1928 + RFC 1929.
48+
var greeting []byte
49+
if p.SocksUser != "" && p.SocksPass != "" {
50+
greeting = []byte{5, 2, 0, 2} // VER=5, NMETHODS=2, NO_AUTH, USER/PASS
51+
} else {
52+
greeting = []byte{5, 1, 0} // VER=5, NMETHODS=1, NO_AUTH
53+
}
54+
if _, err := realConn.Write(greeting); err != nil {
55+
realConn.Close()
56+
return nil, err
57+
}
58+
59+
resp := make([]byte, 2)
60+
if _, err := io.ReadFull(realConn, resp); err != nil {
61+
realConn.Close()
62+
return nil, err
63+
}
64+
if resp[0] != 5 {
65+
realConn.Close()
66+
return nil, fmt.Errorf("upstream SOCKS5 version mismatch: %d", resp[0])
67+
}
68+
69+
switch resp[1] {
70+
case 0:
71+
// NO_AUTH selected. Done.
72+
return realConn, nil
73+
case 2:
74+
// USERNAME/PASSWORD selected — proceed with RFC 1929 sub-negotiation.
75+
if p.SocksUser == "" || p.SocksPass == "" {
76+
realConn.Close()
77+
return nil, fmt.Errorf("upstream requires auth but no credentials configured")
78+
}
79+
user := []byte(p.SocksUser)
80+
pass := []byte(p.SocksPass)
81+
if len(user) > 255 || len(pass) > 255 {
82+
realConn.Close()
83+
return nil, fmt.Errorf("socks credentials too long")
84+
}
85+
req := []byte{1, byte(len(user))}
86+
req = append(req, user...)
87+
req = append(req, byte(len(pass)))
88+
req = append(req, pass...)
89+
if _, err := realConn.Write(req); err != nil {
90+
realConn.Close()
91+
return nil, err
92+
}
93+
authResp := make([]byte, 2)
94+
if _, err := io.ReadFull(realConn, authResp); err != nil {
95+
realConn.Close()
96+
return nil, err
97+
}
98+
if authResp[1] != 0 {
99+
realConn.Close()
100+
return nil, fmt.Errorf("socks auth rejected (status %d)", authResp[1])
101+
}
102+
return realConn, nil
103+
default:
104+
realConn.Close()
105+
return nil, fmt.Errorf("upstream selected unsupported auth method %d", resp[1])
106+
}
107+
}
108+
31109
func (p *FakeDNSProxy) Start() (string, error) {
32110
l, err := net.Listen("tcp", "127.0.0.1:0")
33111
if err != nil {
@@ -139,22 +217,14 @@ func (p *FakeDNSProxy) handleConnection(conn net.Conn) {
139217
return
140218
}
141219

142-
// Dial Real SOCKS
143-
realConn, err := net.Dial("tcp", p.RealSocksAddr)
220+
// Dial Real SOCKS (with auth if configured)
221+
realConn, err := p.dialRealSocks()
144222
if err != nil {
145-
conn.Write([]byte{5, 1, 0, 1, 0, 0, 0, 0, 0, 0})
223+
_, _ = conn.Write([]byte{5, 1, 0, 1, 0, 0, 0, 0, 0, 0})
146224
return
147225
}
148226
defer realConn.Close()
149227

150-
if _, err := realConn.Write([]byte{5, 1, 0}); err != nil {
151-
return
152-
}
153-
authResp := make([]byte, 2)
154-
if _, err := io.ReadFull(realConn, authResp); err != nil {
155-
return
156-
}
157-
158228
req := []byte{5, 1, 0, atyp}
159229
req = append(req, targetAddr...)
160230
req = append(req, targetPort...)
@@ -195,21 +265,13 @@ func (p *FakeDNSProxy) handleConnection(conn net.Conn) {
195265
}
196266

197267
func (p *FakeDNSProxy) handleUDPAssociate(tcpConn net.Conn, atyp byte, targetAddr []byte, targetPort []byte) {
198-
realConn, err := net.Dial("tcp", p.RealSocksAddr)
268+
realConn, err := p.dialRealSocks()
199269
if err != nil {
200-
tcpConn.Write([]byte{5, 1, 0, 1, 0, 0, 0, 0, 0, 0})
270+
_, _ = tcpConn.Write([]byte{5, 1, 0, 1, 0, 0, 0, 0, 0, 0})
201271
return
202272
}
203273
defer realConn.Close()
204274

205-
if _, err := realConn.Write([]byte{5, 1, 0}); err != nil {
206-
return
207-
}
208-
authResp := make([]byte, 2)
209-
if _, err := io.ReadFull(realConn, authResp); err != nil {
210-
return
211-
}
212-
213275
req := []byte{5, 3, 0, atyp}
214276
req = append(req, targetAddr...)
215277
req = append(req, targetPort...)

mobile/tun/tun_api.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ var (
1616
sharedDnsMap *DNSMapper
1717
)
1818

19-
func StartFakeDNSProxy(socksAddr string) (string, error) {
19+
func StartFakeDNSProxy(socksAddr, socksUser, socksPass string) (string, error) {
2020
bridgeMu.Lock()
2121
defer bridgeMu.Unlock()
2222

@@ -28,10 +28,10 @@ func StartFakeDNSProxy(socksAddr string) (string, error) {
2828
return "", fmt.Errorf("socksAddr cannot be empty")
2929
}
3030

31-
log.Printf("[TUN-API] Starting FakeDNS SOCKS5 proxy pointing to %s", socksAddr)
31+
log.Printf("[TUN-API] Starting FakeDNS SOCKS5 proxy pointing to %s (auth=%v)", socksAddr, socksUser != "")
3232

3333
sharedDnsMap = NewDNSMapper()
34-
proxy := NewFakeDNSProxy(socksAddr, sharedDnsMap)
34+
proxy := NewFakeDNSProxy(socksAddr, socksUser, socksPass, sharedDnsMap)
3535

3636
addr, err := proxy.Start()
3737
if err != nil {

0 commit comments

Comments
 (0)