Skip to content

Commit fe5d145

Browse files
committed
ipn/pipws: websockets tunnel
1 parent 4b25d62 commit fe5d145

File tree

5 files changed

+271
-0
lines changed

5 files changed

+271
-0
lines changed

go.mod

+2
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ require (
2424
golang.org/x/mobile v0.0.0-20220518205345-8578da9835fd
2525
golang.zx2c4.com/wireguard v0.0.0-20230317141804-7f511c3bb16d
2626
gvisor.dev/gvisor v0.0.0-20230407213733-b6e172e0e152
27+
nhooyr.io/websocket v1.8.7
2728
)
2829

2930
require (
3031
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
3132
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect
3233
github.com/google/btree v1.0.1 // indirect
34+
github.com/klauspost/compress v1.10.3 // indirect
3335
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
3436
github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect
3537
golang.org/x/mod v0.7.0 // indirect

go.sum

+24
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,26 @@ github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819/go.mod h1:Ro8st/El
3333
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
3434
github.com/eycorsican/go-tun2socks v1.16.11 h1:+hJDNgisrYaGEqoSxhdikMgMJ4Ilfwm/IZDrWRrbaH8=
3535
github.com/eycorsican/go-tun2socks v1.16.11/go.mod h1:wgB2BFT8ZaPKyKOQ/5dljMG/YIow+AIXyq4KBwJ5sGQ=
36+
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
37+
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
3638
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
3739
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
3840
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
3941
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
42+
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
43+
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
44+
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
45+
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
4046
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
47+
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
48+
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
49+
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
4150
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
4251
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
4352
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
4453
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
54+
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
55+
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
4556
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
4657
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
4758
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
@@ -55,22 +66,28 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
5566
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
5667
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
5768
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
69+
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
5870
github.com/jedisct1/go-clocksmith v0.0.0-20190707124905-73e087c7979c h1:a/NQUT7AXkEfhaZ+nb7Uzqijo1Qc7C7SZpRrv+6UQDA=
5971
github.com/jedisct1/go-clocksmith v0.0.0-20190707124905-73e087c7979c/go.mod h1:SAINchklztk2jcLWJ4bpNF4KnwDUSUTX+cJbspWC2Rw=
6072
github.com/jedisct1/go-dnsstamps v0.0.0-20200621175006-302248eecc94 h1:O5X61fl3p/dl+7hLDwDamJxRY6z/LwuH1XD+OyNNlxE=
6173
github.com/jedisct1/go-dnsstamps v0.0.0-20200621175006-302248eecc94/go.mod h1:128Ik0lG+DBYL6zaSgN3icmzDASeQgkSy3+Sp10trLc=
6274
github.com/jedisct1/xsecretbox v0.0.0-20190909160646-b731c21297f9 h1:nGfB2s9K0GyHuNkJmXkIjP+m7je6Q6gjirr+weAEtDo=
6375
github.com/jedisct1/xsecretbox v0.0.0-20190909160646-b731c21297f9/go.mod h1:MipBKo+gZlzpd1JXA1OliuwvtQizlFeu4aMAyTLh8bo=
6476
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
77+
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
6578
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
6679
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
6780
github.com/k-sone/critbitgo v1.4.0 h1:l71cTyBGeh6X5ATh6Fibgw3+rtNT80BA0uNNWgkPrbE=
6881
github.com/k-sone/critbitgo v1.4.0/go.mod h1:7E6pyoyADnFxlUBEKcnfS49b7SUAQGMK+OAp/UQvo0s=
82+
github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
83+
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
6984
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
7085
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
7186
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
7287
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
7388
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
89+
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
90+
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
7491
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
7592
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
7693
github.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8=
@@ -123,6 +140,8 @@ github.com/txthinking/socks5 v0.0.0-20220615051428-39268faee3e6 h1:8DkPbOq/EPxbD
123140
github.com/txthinking/socks5 v0.0.0-20220615051428-39268faee3e6/go.mod h1:7NloQcrxaZYKURWph5HLxVDlIwMHJXCPkeWPtpftsIg=
124141
github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe h1:gMWxZxBFRAXqoGkwkYlPX2zvyyKNWJpxOxCrjqJkm5A=
125142
github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe/go.mod h1:WgqbSEmUYSjEV3B1qmee/PpP2NYEz4bL9/+mF1ma+s4=
143+
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
144+
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
126145
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
127146
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
128147
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -179,6 +198,7 @@ golang.org/x/sys v0.0.0-20190909082730-f460065e899a/go.mod h1:h1NjWce9XRLGQEsW7w
179198
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
180199
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
181200
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
201+
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
182202
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
183203
golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
184204
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -194,6 +214,7 @@ golang.org/x/sys v0.5.1-0.20230222185716-a3b23cc77e89 h1:260HNjMTPDya+jq5AM1zZLg
194214
golang.org/x/sys v0.5.1-0.20230222185716-a3b23cc77e89/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
195215
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
196216
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
217+
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
197218
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
198219
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
199220
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
@@ -237,6 +258,7 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
237258
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
238259
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
239260
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
261+
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
240262
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
241263
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
242264
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -246,3 +268,5 @@ gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0 h1:Wobr37noukisGxpKo5jAsLRE
246268
gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0/go.mod h1:Dn5idtptoW1dIos9U6A2rpebLs/MtTwFacjKb8jLdQA=
247269
gvisor.dev/gvisor v0.0.0-20230407213733-b6e172e0e152 h1:ZYILu78u7jC1D1rlKR4Zi6thz/qEye0u5vVvCdC0xec=
248270
gvisor.dev/gvisor v0.0.0-20230407213733-b6e172e0e152/go.mod h1:pzr6sy8gDLfVmDAg8OYrlKvGEHw5C3PGTiBXBTCx76Q=
271+
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
272+
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=

intra/ipn/pipws.go

+242
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
// Copyright (c) 2023 RethinkDNS and its authors.
2+
//
3+
// This Source Code Form is subject to the terms of the Mozilla Public
4+
// License, v. 2.0. If a copy of the MPL was not distributed with this
5+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
7+
package ipn
8+
9+
import (
10+
"context"
11+
"net"
12+
"net/http"
13+
"net/netip"
14+
"net/url"
15+
"strconv"
16+
"strings"
17+
"time"
18+
19+
"github.com/celzero/firestack/intra/core"
20+
"github.com/celzero/firestack/intra/core/ipmap"
21+
"github.com/celzero/firestack/intra/log"
22+
"github.com/celzero/firestack/intra/protect"
23+
"github.com/celzero/firestack/intra/settings"
24+
"github.com/celzero/firestack/intra/split"
25+
"nhooyr.io/websocket"
26+
)
27+
28+
const (
29+
writeTimeout time.Duration = 10 * time.Second
30+
)
31+
32+
type pipws struct {
33+
Proxy
34+
id string // some unique identifier
35+
url string // h2 proxy url
36+
hostname string // h2 proxy hostname
37+
port int // h2 proxy port
38+
ips ipmap.IPMap // h2 proxy working ips
39+
token string // hex, client token
40+
sig string // hex, authorizer signed client token
41+
client http.Client // h2 client
42+
dialer *net.Dialer // h2 dialer
43+
status int // proxy status: TOK, TKO, END
44+
}
45+
46+
var _ core.TCPConn = &pipwsconn{}
47+
48+
// pipwsconn minimally adapts net.Conn to the core.TCPConn interface
49+
type pipwsconn struct {
50+
net.Conn
51+
}
52+
53+
func (c *pipwsconn) CloseRead() error { return c.Close() }
54+
func (c *pipwsconn) CloseWrite() error { return c.Close() }
55+
56+
func (t *pipws) dial(network, addr string) (net.Conn, error) {
57+
log.D("piph2: dialing %s", addr)
58+
domain, portStr, err := net.SplitHostPort(addr)
59+
if err != nil {
60+
return nil, err
61+
}
62+
port, err := strconv.Atoi(portStr)
63+
if err != nil {
64+
return nil, err
65+
}
66+
67+
tcpaddr := func(ip net.IP) *net.TCPAddr {
68+
return &net.TCPAddr{IP: ip, Port: port}
69+
}
70+
71+
var conn net.Conn
72+
ips := t.ips.Get(domain)
73+
confirmed := ips.Confirmed()
74+
if confirmed != nil {
75+
if conn, err = split.DialWithSplitRetry(t.dialer, tcpaddr(confirmed), nil); err == nil {
76+
log.I("piph2: confirmed IP %s worked", confirmed.String())
77+
return conn, nil
78+
}
79+
log.D("piph2: confirmed IP %s failed with err %v", confirmed.String(), err)
80+
ips.Disconfirm(confirmed)
81+
}
82+
83+
log.D("piph2: trying all IPs")
84+
for _, ip := range ips.GetAll() {
85+
if ip.Equal(confirmed) {
86+
continue
87+
}
88+
if conn, err = split.DialWithSplitRetry(t.dialer, tcpaddr(ip), nil); err == nil {
89+
log.I("piph2: found working IP: %s", ip.String())
90+
return conn, nil
91+
}
92+
}
93+
return nil, err
94+
}
95+
96+
func (t *pipws) wsconn(rurl, msg string) (c net.Conn, res *http.Response, err error) {
97+
var ws *websocket.Conn
98+
ctx := context.Background()
99+
hdrs := http.Header{}
100+
hdrs.Set("User-Agent", "")
101+
hdrs.Set("X-Nile-Pip-Claim", t.claim(msg))
102+
hdrs.Set("X-Nile-Pip-Msg", msg)
103+
104+
log.D("connecting to %s", rurl)
105+
106+
ws, res, err = websocket.Dial(ctx, rurl, &websocket.DialOptions{
107+
CompressionMode: websocket.CompressionNoContextTakeover,
108+
HTTPClient: &t.client,
109+
HTTPHeader: hdrs,
110+
})
111+
if err != nil {
112+
log.E("websocket: %v\n", err)
113+
return
114+
}
115+
116+
conn := websocket.NetConn(ctx, ws, websocket.MessageBinary)
117+
c = &pipwsconn{conn}
118+
return
119+
}
120+
121+
func NewPipWsProxy(id string, ctl protect.Controller, po *settings.ProxyOptions) (Proxy, error) {
122+
parsedurl, err := url.Parse(po.Url())
123+
if err != nil {
124+
return nil, err
125+
}
126+
// may be "pipws"
127+
if parsedurl.Scheme != "wss" {
128+
parsedurl.Scheme = "wss"
129+
}
130+
portStr := parsedurl.Port()
131+
var port int
132+
if len(portStr) > 0 {
133+
port, err = strconv.Atoi(portStr)
134+
if err != nil {
135+
return nil, err
136+
}
137+
} else {
138+
port = 443
139+
}
140+
141+
dialer := protect.MakeNsDialer(ctl)
142+
t := &pipws{
143+
id: id,
144+
url: parsedurl.String(),
145+
hostname: parsedurl.Hostname(),
146+
port: port,
147+
dialer: dialer,
148+
token: po.Auth.User,
149+
sig: po.Auth.Password,
150+
ips: ipmap.NewIPMap(dialer.Resolver),
151+
status: TOK,
152+
}
153+
154+
ipset := t.ips.Of(t.hostname, po.Addrs) // po.Addrs may be nil or empty
155+
if ipset.Empty() {
156+
log.W("pipws: zero bootstrap ips %s", t.hostname)
157+
}
158+
159+
t.client.Transport = &http.Transport{
160+
Dial: t.dial,
161+
TLSHandshakeTimeout: writeTimeout,
162+
ResponseHeaderTimeout: writeTimeout,
163+
}
164+
return t, nil
165+
}
166+
167+
func (t *pipws) ID() string {
168+
return t.id
169+
}
170+
171+
func (t *pipws) Type() string {
172+
return PIPWS
173+
}
174+
175+
func (t *pipws) GetAddr() string {
176+
return t.hostname + ":" + strconv.Itoa(t.port)
177+
}
178+
179+
func (t *pipws) Stop() error {
180+
t.status = END
181+
return nil
182+
}
183+
184+
func (t *pipws) Status() int {
185+
return t.status
186+
}
187+
188+
func (t *pipws) claim(msg string) string {
189+
if len(t.token) == 0 || len(t.sig) == 0 {
190+
return ""
191+
}
192+
// hmac msg keyed by token's sig
193+
msgmac := hmac256(hex2byte(msg), hex2byte(t.sig))
194+
return claimprefix + t.token + ":" + t.sig + ":" + byte2hex(msgmac)
195+
}
196+
197+
func (t *pipws) Dial(network, addr string) (Conn, error) {
198+
if t.status == END {
199+
return nil, errProxyStopped
200+
}
201+
if network != "tcp" {
202+
return nil, errUnexpectedProxy
203+
}
204+
205+
u, err := url.Parse(t.url)
206+
if err != nil {
207+
return nil, err
208+
}
209+
ipp, err := netip.ParseAddrPort(addr)
210+
if err != nil {
211+
return nil, err
212+
}
213+
214+
if !strings.HasSuffix(u.Path, "/") {
215+
u.Path += "/"
216+
}
217+
u.Path += ipp.Addr().String() + "/" + strconv.Itoa(int(ipp.Port())) + "/" + network
218+
219+
msg, err := hexnonce(ipp)
220+
if err != nil {
221+
log.E("pipws: nonce err: %v", err)
222+
return nil, err
223+
}
224+
225+
rurl := u.String()
226+
c, res, err := t.wsconn(rurl, msg)
227+
if err != nil {
228+
log.E("pipws: req err: %v", err)
229+
t.status = TKO
230+
return nil, err
231+
}
232+
if res.StatusCode != 101 {
233+
log.E("pipws: res not ws %d", res.StatusCode)
234+
t.status = TKO
235+
return nil, err
236+
}
237+
238+
log.D("pipws: duplex %s", rurl)
239+
240+
t.status = TOK
241+
return c, nil
242+
}

intra/ipn/proxies.go

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const (
2727
HTTP1 = "http1" // HTTP/1.1 proxy
2828
WG = "wg" // WireGuard-as-a-proxy
2929
PIPH2 = "piph2" // PIP: HTTP/2 proxy
30+
PIPWS = "pipws" // PIP: WebSockets proxy
3031
NOOP = "noop" // No proxy
3132

3233
// status of proxies

intra/ipn/proxy.go

+2
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ func (pxr *proxifier) AddProxy(id, txt string) (p Proxy, err error) {
6464
p, err = NewHTTPProxy(id, pxr.ctl, opts)
6565
case "piph2":
6666
p, err = NewPipProxy(id, pxr.ctl, opts)
67+
case "pipws":
68+
p, err = NewPipWsProxy(id, pxr.ctl, opts)
6769
case "wg":
6870
err = fmt.Errorf("proxy: id must be prefixed with %s in %s for [%s]", WG, id, txt)
6971
default:

0 commit comments

Comments
 (0)