Skip to content

Commit ea84c58

Browse files
committed
socks5: halve connection TTFB latency by removing unnecessary auth round-trip
Move SOCKS5 auth negotiation to localhost, eliminating one RTT. Auth was always NoAuth, so the remote exchange only added latency.
1 parent e43df8e commit ea84c58

13 files changed

Lines changed: 116 additions & 13 deletions

File tree

application.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ func (a *Application) Init(ctx context.Context, tunDevice tun.Device) error {
135135
p2pHost.SetStreamHandler(protocol.TunnelPacketMethod, a.Tunnel.StreamHandler)
136136
}
137137
p2pHost.SetStreamHandler(protocol.Socks5PacketMethod, a.SOCKS5.ProxyStreamHandler)
138+
p2pHost.SetStreamHandler(protocol.Socks5NoAuthMethod, a.SOCKS5.ProxyStreamHandler)
138139

139140
if a.Tunnel != nil {
140141
awlevent.WrapSubscriptionToCallback(a.ctx, func(_ interface{}) {

application_simnet_test.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import (
1616
"github.com/anywherelan/awl/vpn"
1717
)
1818

19+
const EnvRunPerfTests = "AWL_PERF_TESTS"
20+
1921
/*
2022
TestSimulatedTunnelPerformance performs a benchmark of the AWL VPN tunnel under simulated network conditions.
2123
@@ -32,8 +34,8 @@ It uses:
3234
func TestSimulatedTunnelPerformance(t *testing.T) {
3335
const Mbps = 1_000_000
3436

35-
if os.Getenv("CI") != "" {
36-
t.Skip("skip in CI because it's a benchmark")
37+
if os.Getenv(EnvRunPerfTests) == "" {
38+
t.Skipf("skip perf test because %s env is empty", EnvRunPerfTests)
3739
}
3840

3941
scenarios := []struct {
@@ -230,8 +232,8 @@ func TestSimulatedSOCKS5ProxyPerformance(t *testing.T) {
230232

231233
const Mbps = 1_000_000
232234

233-
if os.Getenv("CI") != "" {
234-
t.Skip("skip in CI because it's a benchmark")
235+
if os.Getenv(EnvRunPerfTests) == "" {
236+
t.Skipf("skip perf test because %s env is empty", EnvRunPerfTests)
235237
}
236238

237239
scenarios := []struct {

application_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/anywherelan/awl/api"
2222
"github.com/anywherelan/awl/config"
2323
"github.com/anywherelan/awl/entity"
24+
"github.com/anywherelan/awl/protocol"
2425
)
2526

2627
func TestMakeFriends(t *testing.T) {
@@ -391,6 +392,43 @@ func TestUpdateUseAsExitNodeConfig(t *testing.T) {
391392
ts.Equal("", info.SOCKS5.UsingPeerID)
392393
}
393394

395+
func TestSOCKS5ProxyFallbackToOldProtocol(t *testing.T) {
396+
ts := NewTestSuite(t)
397+
398+
peer1 := ts.NewTestPeer(false) // client
399+
peer2 := ts.NewTestPeer(false) // server (simulates old version)
400+
401+
ts.makeFriends(peer2, peer1)
402+
403+
// Remove new protocol handler from peer2 to simulate old peer
404+
peer2.app.P2p.Host().RemoveStreamHandler(protocol.Socks5NoAuthMethod)
405+
406+
// Allow peer1 to use peer2 as exit node
407+
peer1Config, err := peer2.api.KnownPeerConfig(peer1.PeerID())
408+
ts.NoError(err)
409+
410+
err = peer2.api.UpdatePeerSettings(entity.UpdatePeerSettingsRequest{
411+
PeerID: peer1.PeerID(),
412+
Alias: peer1Config.Alias,
413+
DomainName: peer1Config.DomainName,
414+
IPAddr: peer1Config.IPAddr,
415+
AllowUsingAsExitNode: true,
416+
})
417+
ts.NoError(err)
418+
419+
ts.Eventually(func() bool {
420+
peer2Config, err := peer1.api.KnownPeerConfig(peer2.PeerID())
421+
ts.NoError(err)
422+
return peer2Config.AllowedUsingAsExitNode
423+
}, 15*time.Second, 100*time.Millisecond)
424+
425+
peer1.app.SOCKS5.SetProxyPeerID(peer2.PeerID())
426+
peer2.app.SOCKS5.SetProxyingLocalhostEnabled(true)
427+
428+
// Verify SOCKS5 still works via fallback to old protocol
429+
testSOCKS5Proxy(ts, peer1.app.Conf.SOCKS5.ListenAddress, "")
430+
}
431+
394432
func TestUpdatePeerSettingsIPAddr(t *testing.T) {
395433
ts := NewTestSuite(t)
396434

cmd/awl-tray/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.25.0
44

55
replace (
66
github.com/anywherelan/awl => ../../
7-
github.com/haxii/socks5 => github.com/anywherelan/socks5 v0.0.0-20260112065346-bea9abfe9bdc
7+
github.com/haxii/socks5 => github.com/anywherelan/socks5 v0.0.0-20260404130029-d414a53712fc
88
github.com/ipfs/go-log/v2 => github.com/anywherelan/go-log/v2 v2.0.3-0.20221101180049-46e3967f6fe5
99
github.com/ncruces/zenity => github.com/pymq/zenity v0.0.0-20230509161854-c117c448544d
1010
)

cmd/awl-tray/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ github.com/akavel/rsrc v0.10.2 h1:Zxm8V5eI1hW4gGaYsJQUhxpjkENuG91ki8B4zCrvEsw=
1111
github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
1212
github.com/anywherelan/go-log/v2 v2.0.3-0.20221101180049-46e3967f6fe5 h1:uQsw+HnQo6Ru5eFgUdEQYKMwkiYkoNZDQmY2ob1E58Y=
1313
github.com/anywherelan/go-log/v2 v2.0.3-0.20221101180049-46e3967f6fe5/go.mod h1:r8UEDyeHO6bYVcP9R2/HnK2ZSZ5CJp89gubcHLKfRv0=
14-
github.com/anywherelan/socks5 v0.0.0-20260112065346-bea9abfe9bdc h1:BxUih+wVbxEfAAC4+vzXSoDXHNFLgnLR9AmeXRtJbuk=
15-
github.com/anywherelan/socks5 v0.0.0-20260112065346-bea9abfe9bdc/go.mod h1:KpbKhNH2RqojJGPVT9faDXnYWkqzfgpKeehC8xTKaeY=
14+
github.com/anywherelan/socks5 v0.0.0-20260404130029-d414a53712fc h1:icO+nO4NZtJLZS8JnrCpGk8GGaaKNeleuY7WH7qzVik=
15+
github.com/anywherelan/socks5 v0.0.0-20260404130029-d414a53712fc/go.mod h1:KpbKhNH2RqojJGPVT9faDXnYWkqzfgpKeehC8xTKaeY=
1616
github.com/anywherelan/ts-dns v0.0.0-20240721135326-6d6b7b811853 h1:RVKWGnppAfxgD2wphkq+OYDOqqI8zgbymBLl2pxYKzY=
1717
github.com/anywherelan/ts-dns v0.0.0-20240721135326-6d6b7b811853/go.mod h1:ly7HpPle1G3D0jwrr12uolTGWKN3DPgxzBYNR086BLo=
1818
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/anywherelan/awl
33
go 1.25.0
44

55
replace (
6-
github.com/haxii/socks5 => github.com/anywherelan/socks5 v0.0.0-20260112065346-bea9abfe9bdc
6+
github.com/haxii/socks5 => github.com/anywherelan/socks5 v0.0.0-20260404130029-d414a53712fc
77
github.com/ipfs/go-log/v2 => github.com/anywherelan/go-log/v2 v2.0.3-0.20221101180049-46e3967f6fe5
88
)
99

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ github.com/GrigoryKrasnochub/updaterini v0.1.0 h1:csl3bqQIShuI8RnXzKprTgEhqi3I59
33
github.com/GrigoryKrasnochub/updaterini v0.1.0/go.mod h1:w+8blQZCZzGmJGXqmXbeSOsUOF6LqyeqckxSiZah7RI=
44
github.com/anywherelan/go-log/v2 v2.0.3-0.20221101180049-46e3967f6fe5 h1:uQsw+HnQo6Ru5eFgUdEQYKMwkiYkoNZDQmY2ob1E58Y=
55
github.com/anywherelan/go-log/v2 v2.0.3-0.20221101180049-46e3967f6fe5/go.mod h1:r8UEDyeHO6bYVcP9R2/HnK2ZSZ5CJp89gubcHLKfRv0=
6-
github.com/anywherelan/socks5 v0.0.0-20260112065346-bea9abfe9bdc h1:BxUih+wVbxEfAAC4+vzXSoDXHNFLgnLR9AmeXRtJbuk=
7-
github.com/anywherelan/socks5 v0.0.0-20260112065346-bea9abfe9bdc/go.mod h1:KpbKhNH2RqojJGPVT9faDXnYWkqzfgpKeehC8xTKaeY=
6+
github.com/anywherelan/socks5 v0.0.0-20260404130029-d414a53712fc h1:icO+nO4NZtJLZS8JnrCpGk8GGaaKNeleuY7WH7qzVik=
7+
github.com/anywherelan/socks5 v0.0.0-20260404130029-d414a53712fc/go.mod h1:KpbKhNH2RqojJGPVT9faDXnYWkqzfgpKeehC8xTKaeY=
88
github.com/anywherelan/ts-dns v0.0.0-20240721135326-6d6b7b811853 h1:RVKWGnppAfxgD2wphkq+OYDOqqI8zgbymBLl2pxYKzY=
99
github.com/anywherelan/ts-dns v0.0.0-20240721135326-6d6b7b811853/go.mod h1:ly7HpPle1G3D0jwrr12uolTGWKN3DPgxzBYNR086BLo=
1010
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=

p2p/p2p.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,11 @@ func (p *P2p) NewStream(ctx context.Context, id peer.ID, proto protocol.ID) (net
248248
return p.host.NewStream(ctx, id, proto)
249249
}
250250

251+
func (p *P2p) NewStreamMulti(ctx context.Context, id peer.ID, protos ...protocol.ID) (network.Stream, error) {
252+
ctx = network.WithAllowLimitedConn(ctx, "awl")
253+
return p.host.NewStream(ctx, id, protos...)
254+
}
255+
251256
func (p *P2p) NewStreamWithDedicatedConn(ctx context.Context, id peer.ID, proto protocol.ID) (network.Stream, error) {
252257
ctx = network.WithAllowLimitedConn(ctx, "awl")
253258

protocol/protocol.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const (
1717
GetStatusMethod protocol.ID = basePath + "/status/"
1818
TunnelPacketMethod protocol.ID = basePath + "/tunnel/"
1919
Socks5PacketMethod protocol.ID = basePath + "/socks5/"
20+
Socks5NoAuthMethod protocol.ID = basePath + "/socks5-noauth/"
2021
)
2122

2223
type (

service/auth_status.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type P2p interface {
2929
ConnectPeer(ctx context.Context, peerID peer.ID) error
3030
IsConnected(peerID peer.ID) bool
3131
NewStream(ctx context.Context, id peer.ID, proto libp2pProtocol.ID) (network.Stream, error)
32+
NewStreamMulti(ctx context.Context, id peer.ID, protos ...libp2pProtocol.ID) (network.Stream, error)
3233
NewStreamWithDedicatedConn(ctx context.Context, id peer.ID, proto libp2pProtocol.ID) (network.Stream, error)
3334
SubscribeConnectionEvents(onConnected, onDisconnected func(network.Network, network.Conn))
3435
RecordPeerLatency(id peer.ID, rtt time.Duration)

0 commit comments

Comments
 (0)