Skip to content

Commit b9aee25

Browse files
authored
Refactor(proxy): registrable proxy protocol (#517)
1 parent 27e493b commit b9aee25

File tree

21 files changed

+375
-399
lines changed

21 files changed

+375
-399
lines changed

engine/engine.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ func netstack(k *Key) (err error) {
193193
if _defaultProxy, err = parseProxy(k.Proxy); err != nil {
194194
return err
195195
}
196-
tunnel.T().SetDialer(_defaultProxy)
196+
tunnel.T().SetProxy(_defaultProxy)
197197

198198
if _defaultDevice, err = parseDevice(k.Device, uint32(k.MTU)); err != nil {
199199
return err
@@ -234,10 +234,6 @@ func netstack(k *Key) (err error) {
234234
return err
235235
}
236236

237-
log.Infof(
238-
"[STACK] %s://%s <-> %s://%s",
239-
_defaultDevice.Type(), _defaultDevice.Name(),
240-
_defaultProxy.Proto(), _defaultProxy.Addr(),
241-
)
237+
log.Infof("[STACK] %s <-> %s", k.Device, k.Proxy)
242238
return nil
243239
}

engine/parse.go

Lines changed: 8 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
package engine
22

33
import (
4-
"encoding/base64"
54
"fmt"
65
"net"
76
"net/netip"
87
"net/url"
98
"runtime"
109
"strings"
1110

12-
"github.com/gorilla/schema"
13-
1411
"github.com/xjasonlyu/tun2socks/v2/core/device"
1512
"github.com/xjasonlyu/tun2socks/v2/core/device/fdbased"
1613
"github.com/xjasonlyu/tun2socks/v2/core/device/tun"
1714
"github.com/xjasonlyu/tun2socks/v2/proxy"
18-
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
15+
)
16+
17+
const (
18+
defaultDeviceType = "tun"
19+
defaultProxyType = "socks5"
1920
)
2021

2122
func parseRestAPI(s string) (*url.URL, error) {
@@ -47,7 +48,7 @@ func parseRestAPI(s string) (*url.URL, error) {
4748

4849
func parseDevice(s string, mtu uint32) (device.Device, error) {
4950
if !strings.Contains(s, "://") {
50-
s = fmt.Sprintf("%s://%s", tun.Driver /* default driver */, s)
51+
s = fmt.Sprintf("%s://%s", defaultDeviceType, s)
5152
}
5253

5354
u, err := url.Parse(s)
@@ -79,111 +80,14 @@ func parseFD(u *url.URL, mtu uint32) (device.Device, error) {
7980

8081
func parseProxy(s string) (proxy.Proxy, error) {
8182
if !strings.Contains(s, "://") {
82-
s = fmt.Sprintf("%s://%s", proto.Socks5 /* default protocol */, s)
83+
s = fmt.Sprintf("%s://%s", defaultProxyType, s)
8384
}
8485

8586
u, err := url.Parse(s)
8687
if err != nil {
8788
return nil, err
8889
}
89-
90-
protocol := strings.ToLower(u.Scheme)
91-
92-
switch protocol {
93-
case proto.Direct.String():
94-
return proxy.NewDirect(), nil
95-
case proto.Reject.String():
96-
return proxy.NewReject(), nil
97-
case proto.HTTP.String():
98-
return parseHTTP(u)
99-
case proto.Socks4.String():
100-
return parseSocks4(u)
101-
case proto.Socks5.String():
102-
return parseSocks5(u)
103-
case proto.Shadowsocks.String():
104-
return parseShadowsocks(u)
105-
case proto.Relay.String():
106-
return parseRelay(u)
107-
default:
108-
return nil, fmt.Errorf("unsupported protocol: %s", protocol)
109-
}
110-
}
111-
112-
func parseHTTP(u *url.URL) (proxy.Proxy, error) {
113-
address, username := u.Host, u.User.Username()
114-
password, _ := u.User.Password()
115-
return proxy.NewHTTP(address, username, password)
116-
}
117-
118-
func parseSocks4(u *url.URL) (proxy.Proxy, error) {
119-
address, userID := u.Host, u.User.Username()
120-
return proxy.NewSocks4(address, userID)
121-
}
122-
123-
func parseSocks5(u *url.URL) (proxy.Proxy, error) {
124-
address, username := u.Host, u.User.Username()
125-
password, _ := u.User.Password()
126-
127-
// Socks5 over UDS
128-
if address == "" {
129-
address = u.Path
130-
}
131-
return proxy.NewSocks5(address, username, password)
132-
}
133-
134-
func parseShadowsocks(u *url.URL) (proxy.Proxy, error) {
135-
var (
136-
address = u.Host
137-
method, password string
138-
obfsMode, obfsHost string
139-
)
140-
141-
if ss := u.User.String(); ss == "" {
142-
method = "dummy" // none cipher mode
143-
} else if pass, set := u.User.Password(); set {
144-
method = u.User.Username()
145-
password = pass
146-
} else {
147-
data, _ := base64.RawURLEncoding.DecodeString(ss)
148-
userInfo := strings.SplitN(string(data), ":", 2)
149-
if len(userInfo) == 2 {
150-
method = userInfo[0]
151-
password = userInfo[1]
152-
}
153-
}
154-
155-
rawQuery, _ := url.QueryUnescape(u.RawQuery)
156-
for _, s := range strings.Split(rawQuery, ";") {
157-
data := strings.SplitN(s, "=", 2)
158-
if len(data) != 2 {
159-
continue
160-
}
161-
key := data[0]
162-
value := data[1]
163-
164-
switch key {
165-
case "obfs":
166-
obfsMode = value
167-
case "obfs-host":
168-
obfsHost = value
169-
}
170-
}
171-
172-
return proxy.NewShadowsocks(address, method, password, obfsMode, obfsHost)
173-
}
174-
175-
func parseRelay(u *url.URL) (proxy.Proxy, error) {
176-
address, username := u.Host, u.User.Username()
177-
password, _ := u.User.Password()
178-
179-
opts := struct {
180-
NoDelay bool
181-
}{}
182-
if err := schema.NewDecoder().Decode(&opts, u.Query()); err != nil {
183-
return nil, err
184-
}
185-
186-
return proxy.NewRelay(address, username, password, opts.NoDelay)
90+
return proxy.Parse(u)
18791
}
18892

18993
func parseMulticastGroups(s string) (multicastGroups []netip.Addr, _ error) {

engine/register.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package engine
2+
3+
import (
4+
_ "github.com/xjasonlyu/tun2socks/v2/proxy/direct"
5+
_ "github.com/xjasonlyu/tun2socks/v2/proxy/http"
6+
_ "github.com/xjasonlyu/tun2socks/v2/proxy/reject"
7+
_ "github.com/xjasonlyu/tun2socks/v2/proxy/relay"
8+
_ "github.com/xjasonlyu/tun2socks/v2/proxy/shadowsocks"
9+
_ "github.com/xjasonlyu/tun2socks/v2/proxy/socks4"
10+
_ "github.com/xjasonlyu/tun2socks/v2/proxy/socks5"
11+
)

proxy/base.go

Lines changed: 0 additions & 33 deletions
This file was deleted.

proxy/direct.go renamed to proxy/direct/direct.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,28 @@
1-
package proxy
1+
package direct
22

33
import (
44
"context"
55
"net"
6+
"net/url"
67

78
"github.com/xjasonlyu/tun2socks/v2/dialer"
89
M "github.com/xjasonlyu/tun2socks/v2/metadata"
9-
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
10+
"github.com/xjasonlyu/tun2socks/v2/proxy"
11+
"github.com/xjasonlyu/tun2socks/v2/proxy/internal/utils"
1012
)
1113

12-
var _ Proxy = (*Direct)(nil)
14+
var _ proxy.Proxy = (*Direct)(nil)
1315

14-
type Direct struct {
15-
*Base
16-
}
16+
type Direct struct{}
1717

18-
func NewDirect() *Direct {
19-
return &Direct{
20-
Base: &Base{
21-
proto: proto.Direct,
22-
},
23-
}
24-
}
18+
func New() (*Direct, error) { return &Direct{}, nil }
2519

2620
func (d *Direct) DialContext(ctx context.Context, metadata *M.Metadata) (net.Conn, error) {
2721
c, err := dialer.DialContext(ctx, "tcp", metadata.DestinationAddress())
2822
if err != nil {
2923
return nil, err
3024
}
31-
setKeepAlive(c)
25+
utils.SetKeepAlive(c)
3226
return c, nil
3327
}
3428

@@ -55,3 +49,9 @@ func (pc *directPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
5549
}
5650
return pc.PacketConn.WriteTo(b, udpAddr)
5751
}
52+
53+
func Parse(*url.URL) (proxy.Proxy, error) { return New() }
54+
55+
func init() {
56+
proxy.RegisterProtocol("direct", Parse)
57+
}

proxy/http.go renamed to proxy/http/http.go

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package proxy
1+
package http
22

33
import (
44
"bufio"
@@ -13,42 +13,45 @@ import (
1313

1414
"github.com/xjasonlyu/tun2socks/v2/dialer"
1515
M "github.com/xjasonlyu/tun2socks/v2/metadata"
16-
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
16+
"github.com/xjasonlyu/tun2socks/v2/proxy"
17+
"github.com/xjasonlyu/tun2socks/v2/proxy/internal/utils"
1718
)
1819

19-
type HTTP struct {
20-
*Base
20+
var _ proxy.Proxy = (*HTTP)(nil)
2121

22+
type HTTP struct {
23+
addr string
2224
user string
2325
pass string
2426
}
2527

26-
func NewHTTP(addr, user, pass string) (*HTTP, error) {
28+
func New(addr, user, pass string) (*HTTP, error) {
2729
return &HTTP{
28-
Base: &Base{
29-
addr: addr,
30-
proto: proto.HTTP,
31-
},
30+
addr: addr,
3231
user: user,
3332
pass: pass,
3433
}, nil
3534
}
3635

3736
func (h *HTTP) DialContext(ctx context.Context, metadata *M.Metadata) (c net.Conn, err error) {
38-
c, err = dialer.DialContext(ctx, "tcp", h.Addr())
37+
c, err = dialer.DialContext(ctx, "tcp", h.addr)
3938
if err != nil {
40-
return nil, fmt.Errorf("connect to %s: %w", h.Addr(), err)
39+
return nil, fmt.Errorf("connect to %s: %w", h.addr, err)
4140
}
42-
setKeepAlive(c)
41+
utils.SetKeepAlive(c)
4342

4443
defer func(c net.Conn) {
45-
safeConnClose(c, err)
44+
utils.SafeConnClose(c, err)
4645
}(c)
4746

4847
err = h.shakeHand(metadata, c)
4948
return c, err
5049
}
5150

51+
func (h *HTTP) DialUDP(*M.Metadata) (net.PacketConn, error) {
52+
return nil, errors.ErrUnsupported
53+
}
54+
5255
func (h *HTTP) shakeHand(metadata *M.Metadata, rw io.ReadWriter) error {
5356
addr := metadata.DestinationAddress()
5457
req := &http.Request{
@@ -98,3 +101,13 @@ func basicAuth(username, password string) string {
98101
auth := username + ":" + password
99102
return base64.StdEncoding.EncodeToString([]byte(auth))
100103
}
104+
105+
func Parse(u *url.URL) (proxy.Proxy, error) {
106+
address, username := u.Host, u.User.Username()
107+
password, _ := u.User.Password()
108+
return New(address, username, password)
109+
}
110+
111+
func init() {
112+
proxy.RegisterProtocol("http", Parse)
113+
}

proxy/internal/utils/utils.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package utils
2+
3+
import (
4+
"context"
5+
"net"
6+
"time"
7+
8+
M "github.com/xjasonlyu/tun2socks/v2/metadata"
9+
"github.com/xjasonlyu/tun2socks/v2/transport/socks5"
10+
)
11+
12+
const (
13+
tcpConnectTimeout = 5 * time.Second
14+
tcpKeepAlivePeriod = 30 * time.Second
15+
)
16+
17+
// SetKeepAlive sets tcp keepalive option for tcp connection.
18+
func SetKeepAlive(c net.Conn) {
19+
if tcp, ok := c.(*net.TCPConn); ok {
20+
tcp.SetKeepAlive(true)
21+
tcp.SetKeepAlivePeriod(tcpKeepAlivePeriod)
22+
}
23+
}
24+
25+
// SafeConnClose closes tcp connection safely.
26+
func SafeConnClose(c net.Conn, err error) {
27+
if c != nil && err != nil {
28+
c.Close()
29+
}
30+
}
31+
32+
// SerializeSocksAddr serializes metadata to SOCKSv5 address.
33+
func SerializeSocksAddr(m *M.Metadata) socks5.Addr {
34+
return socks5.SerializeAddr("", m.DstIP, m.DstPort)
35+
}
36+
37+
// WithTCPConnectTimeout returns a derived context with the default TCP connect timeout.
38+
func WithTCPConnectTimeout(ctx context.Context) (context.Context, context.CancelFunc) {
39+
return context.WithTimeout(ctx, tcpConnectTimeout)
40+
}

0 commit comments

Comments
 (0)