diff --git a/engine/engine.go b/engine/engine.go index bd752aa9..d74301bc 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -189,7 +189,7 @@ func netstack(k *Key) (err error) { } }() - if _defaultProxy, err = parseProxy(k.Proxy); err != nil { + if _defaultProxy, err = proxy.ParseFromURL(k.Proxy); err != nil { return } proxy.DefaultProxy = _defaultProxy diff --git a/engine/parse.go b/engine/parse.go index cd6cf3d3..32923eae 100644 --- a/engine/parse.go +++ b/engine/parse.go @@ -9,7 +9,6 @@ import ( "github.com/xjasonlyu/tun2socks/v2/core/device" "github.com/xjasonlyu/tun2socks/v2/core/device/fdbased" "github.com/xjasonlyu/tun2socks/v2/core/device/tun" - "github.com/xjasonlyu/tun2socks/v2/proxy" ) func parseRestAPI(s string) (*url.URL, error) { @@ -65,10 +64,6 @@ func parseFD(u *url.URL, mtu uint32) (device.Device, error) { return fdbased.Open(u.Host, mtu, 0) } -func parseProxy(s string) (proxy.Proxy, error) { - return proxy.ParseFromURL(s) -} - func parseMulticastGroups(s string) (multicastGroups []net.IP, _ error) { ipStrings := strings.Split(s, ",") for _, ipString := range ipStrings { diff --git a/proxy/direct/direct.go b/proxy/direct/direct.go index 16768ed8..5dc1e6e2 100644 --- a/proxy/direct/direct.go +++ b/proxy/direct/direct.go @@ -9,15 +9,16 @@ import ( M "github.com/xjasonlyu/tun2socks/v2/metadata" "github.com/xjasonlyu/tun2socks/v2/proxy" "github.com/xjasonlyu/tun2socks/v2/proxy/internal" + "github.com/xjasonlyu/tun2socks/v2/proxy/internal/base" ) var _ proxy.Proxy = (*Direct)(nil) -const Protocol = "direct" +const protocol = "direct" -type Direct struct{ *internal.Base } +type Direct struct{ *base.Base } -func New() *Direct { return &Direct{internal.New(Protocol, "")} } +func New() *Direct { return &Direct{base.New("", protocol)} } func Parse(*url.URL) (proxy.Proxy, error) { return New(), nil } @@ -55,5 +56,5 @@ func (pc *directPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) { } func init() { - proxy.RegisterProtocol(Protocol, Parse) + proxy.RegisterProtocol(protocol, Parse) } diff --git a/proxy/http/http.go b/proxy/http/http.go index 3914494b..a99274b7 100644 --- a/proxy/http/http.go +++ b/proxy/http/http.go @@ -15,14 +15,15 @@ import ( M "github.com/xjasonlyu/tun2socks/v2/metadata" "github.com/xjasonlyu/tun2socks/v2/proxy" "github.com/xjasonlyu/tun2socks/v2/proxy/internal" + "github.com/xjasonlyu/tun2socks/v2/proxy/internal/base" ) var _ proxy.Proxy = (*HTTP)(nil) -const Protocol = "http" +const protocol = "http" type HTTP struct { - *internal.Base + *base.Base user string pass string @@ -30,7 +31,7 @@ type HTTP struct { func New(addr, user, pass string) (*HTTP, error) { return &HTTP{ - Base: internal.New(Protocol, addr), + Base: base.New(addr, protocol), user: user, pass: pass, }, nil @@ -108,5 +109,5 @@ func basicAuth(username, password string) string { } func init() { - proxy.RegisterProtocol(Protocol, Parse) + proxy.RegisterProtocol(protocol, Parse) } diff --git a/proxy/internal/base.go b/proxy/internal/base/base.go similarity index 88% rename from proxy/internal/base.go rename to proxy/internal/base/base.go index 1055d6dd..7bcc9863 100644 --- a/proxy/internal/base.go +++ b/proxy/internal/base/base.go @@ -1,4 +1,4 @@ -package internal +package base import ( "context" @@ -13,13 +13,13 @@ import ( var _ proxy.Proxy = (*Base)(nil) type Base struct { - protocol, address string + address, protocol string } -func New(protocol, address string) *Base { +func New(address, protocol string) *Base { return &Base{ - protocol: protocol, address: address, + protocol: protocol, } } diff --git a/proxy/internal/util.go b/proxy/internal/internal.go similarity index 67% rename from proxy/internal/util.go rename to proxy/internal/internal.go index 7036f519..f000831e 100644 --- a/proxy/internal/util.go +++ b/proxy/internal/internal.go @@ -8,11 +8,9 @@ import ( "github.com/xjasonlyu/tun2socks/v2/transport/socks5" ) -const ( - tcpKeepAlivePeriod = 30 * time.Second -) +const tcpKeepAlivePeriod = 30 * time.Second -// SetKeepAlive sets tcp keepalive option for tcp connection. +// SetKeepAlive sets the tcp keepalive option for the tcp connection. func SetKeepAlive(c net.Conn) { if tcp, ok := c.(*net.TCPConn); ok { tcp.SetKeepAlive(true) @@ -20,13 +18,14 @@ func SetKeepAlive(c net.Conn) { } } -// SafeConnClose closes tcp connection safely. +// SafeConnClose closes the given tcp connection safely. func SafeConnClose(c net.Conn, err error) { if c != nil && err != nil { c.Close() } } +// SerializeSocksAddr serializes *metadata.Metadata to socks5.Addr. func SerializeSocksAddr(m *M.Metadata) socks5.Addr { return socks5.SerializeAddr("", m.DstIP, m.DstPort) } diff --git a/proxy/protocol.go b/proxy/protocol.go index f6edad47..1cf83cae 100644 --- a/proxy/protocol.go +++ b/proxy/protocol.go @@ -2,6 +2,7 @@ package proxy import ( "errors" + "fmt" "net/url" "sync" @@ -11,7 +12,7 @@ import ( // ErrProtocol indicates that parsing encountered an unknown protocol. var ErrProtocol = errors.New("proxy: unknown protocol") -// A proxy holds a proxy's protocol and how to parse it. +// A protocol holds a proxy protocol's name and how to parse it. type protocol struct { name string parse func(*url.URL) (Proxy, error) @@ -44,10 +45,9 @@ func pick(name string) protocol { return protocol{} } -// Parse parses a proxy url that has been encoded in a registered format. -// The string returned is the format name used during format registration. -// Format registration is typically done by an init function in the codec- -// specific package. +// Parse parses proxy *url.URL that holds the proxy info into Proxy. +// Protocol registration is typically done by an init function in the +// proxy-specific package. func Parse(proxyURL *url.URL) (Proxy, error) { if proxyURL == nil { return nil, errors.New("proxy: nil url") @@ -57,12 +57,12 @@ func Parse(proxyURL *url.URL) (Proxy, error) { } p := pick(proxyURL.Scheme) if p.parse == nil { - return nil, ErrProtocol + return nil, fmt.Errorf("%w: %s", ErrProtocol, proxyURL.Scheme) } return p.parse(proxyURL) } -// ParseFromURL parses a +// ParseFromURL parses url string that holds the proxy info into Proxy. func ParseFromURL(proxy string) (Proxy, error) { proxyURL, err := url.Parse(proxy) if err != nil { diff --git a/proxy/proxy.go b/proxy/proxy.go index a114d654..99e24dfb 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -17,10 +17,19 @@ const ( var DefaultProxy Proxy = nil type Proxy interface { + // Address returns the address of the proxy. Address() string + + // Protocol returns the protocol of the proxy. Protocol() string + + // String returns the string representation of the proxy. String() string + + // DialContext is used to dial TCP networks with context. DialContext(context.Context, *M.Metadata) (net.Conn, error) + + // DialUDP is used to to dial/listen UDP networks. DialUDP(*M.Metadata) (net.PacketConn, error) } diff --git a/proxy/reject/reject.go b/proxy/reject/reject.go index 9f1b4ba8..8df278e2 100644 --- a/proxy/reject/reject.go +++ b/proxy/reject/reject.go @@ -9,16 +9,16 @@ import ( M "github.com/xjasonlyu/tun2socks/v2/metadata" "github.com/xjasonlyu/tun2socks/v2/proxy" - "github.com/xjasonlyu/tun2socks/v2/proxy/internal" + "github.com/xjasonlyu/tun2socks/v2/proxy/internal/base" ) var _ proxy.Proxy = (*Reject)(nil) -const Protocol = "reject" +const protocol = "reject" -type Reject struct{ *internal.Base } +type Reject struct{ *base.Base } -func New() *Reject { return &Reject{internal.New(Protocol, "")} } +func New() *Reject { return &Reject{base.New("", protocol)} } func Parse(*url.URL) (proxy.Proxy, error) { return New(), nil } @@ -48,5 +48,5 @@ func (npc *nopPacketConn) SetReadDeadline(time.Time) error { ret func (npc *nopPacketConn) SetWriteDeadline(time.Time) error { return nil } func init() { - proxy.RegisterProtocol(Protocol, Parse) + proxy.RegisterProtocol(protocol, Parse) } diff --git a/proxy/relay/relay.go b/proxy/relay/relay.go index 22a6dea7..335df29d 100644 --- a/proxy/relay/relay.go +++ b/proxy/relay/relay.go @@ -20,14 +20,15 @@ import ( M "github.com/xjasonlyu/tun2socks/v2/metadata" "github.com/xjasonlyu/tun2socks/v2/proxy" "github.com/xjasonlyu/tun2socks/v2/proxy/internal" + "github.com/xjasonlyu/tun2socks/v2/proxy/internal/base" ) var _ proxy.Proxy = (*Relay)(nil) -const Protocol = "relay" +const protocol = "relay" type Relay struct { - *internal.Base + *base.Base user string pass string @@ -37,7 +38,7 @@ type Relay struct { func New(addr, user, pass string, noDelay bool) (*Relay, error) { return &Relay{ - Base: internal.New(Protocol, addr), + Base: base.New(addr, protocol), user: user, pass: pass, noDelay: noDelay, @@ -268,5 +269,5 @@ func serializeRelayAddr(m *M.Metadata) *relay.AddrFeature { } func init() { - proxy.RegisterProtocol(Protocol, Parse) + proxy.RegisterProtocol(protocol, Parse) } diff --git a/proxy/shadowsocks/shadowsocks.go b/proxy/shadowsocks/shadowsocks.go index f0939782..90d5a016 100644 --- a/proxy/shadowsocks/shadowsocks.go +++ b/proxy/shadowsocks/shadowsocks.go @@ -13,6 +13,7 @@ import ( M "github.com/xjasonlyu/tun2socks/v2/metadata" "github.com/xjasonlyu/tun2socks/v2/proxy" "github.com/xjasonlyu/tun2socks/v2/proxy/internal" + "github.com/xjasonlyu/tun2socks/v2/proxy/internal/base" "github.com/xjasonlyu/tun2socks/v2/transport/shadowsocks/core" obfs "github.com/xjasonlyu/tun2socks/v2/transport/simple-obfs" "github.com/xjasonlyu/tun2socks/v2/transport/socks5" @@ -20,10 +21,10 @@ import ( var _ proxy.Proxy = (*Shadowsocks)(nil) -const Protocol = "ss" +const protocol = "ss" type Shadowsocks struct { - *internal.Base + *base.Base cipher core.Cipher @@ -38,7 +39,7 @@ func New(addr, method, password, obfsMode, obfsHost string) (*Shadowsocks, error } return &Shadowsocks{ - Base: internal.New(Protocol, addr), + Base: base.New(addr, protocol), cipher: cipher, obfsMode: obfsMode, obfsHost: obfsHost, @@ -166,5 +167,5 @@ func (pc *ssPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { } func init() { - proxy.RegisterProtocol(Protocol, Parse) + proxy.RegisterProtocol(protocol, Parse) } diff --git a/proxy/socks4/socks4.go b/proxy/socks4/socks4.go index ec43f0a5..a28a5c18 100644 --- a/proxy/socks4/socks4.go +++ b/proxy/socks4/socks4.go @@ -10,22 +10,23 @@ import ( M "github.com/xjasonlyu/tun2socks/v2/metadata" "github.com/xjasonlyu/tun2socks/v2/proxy" "github.com/xjasonlyu/tun2socks/v2/proxy/internal" + "github.com/xjasonlyu/tun2socks/v2/proxy/internal/base" "github.com/xjasonlyu/tun2socks/v2/transport/socks4" ) var _ proxy.Proxy = (*Socks4)(nil) -const Protocol = "socks4" +const protocol = "socks4" type Socks4 struct { - *internal.Base + *base.Base userID string } func New(addr, userID string) (*Socks4, error) { return &Socks4{ - Base: internal.New(Protocol, addr), + Base: base.New(addr, protocol), userID: userID, }, nil } @@ -51,5 +52,5 @@ func (ss *Socks4) DialContext(ctx context.Context, metadata *M.Metadata) (c net. } func init() { - proxy.RegisterProtocol(Protocol, Parse) + proxy.RegisterProtocol(protocol, Parse) } diff --git a/proxy/socks5/socks5.go b/proxy/socks5/socks5.go index 241f8ba2..cf2cfb18 100644 --- a/proxy/socks5/socks5.go +++ b/proxy/socks5/socks5.go @@ -12,15 +12,16 @@ import ( M "github.com/xjasonlyu/tun2socks/v2/metadata" "github.com/xjasonlyu/tun2socks/v2/proxy" "github.com/xjasonlyu/tun2socks/v2/proxy/internal" + "github.com/xjasonlyu/tun2socks/v2/proxy/internal/base" "github.com/xjasonlyu/tun2socks/v2/transport/socks5" ) var _ proxy.Proxy = (*Socks5)(nil) -const Protocol = "socks5" +const protocol = "socks5" type Socks5 struct { - *internal.Base + *base.Base user string pass string @@ -31,7 +32,7 @@ type Socks5 struct { func New(addr, user, pass string) (*Socks5, error) { return &Socks5{ - Base: internal.New(Protocol, addr), + Base: base.New(addr, protocol), user: user, pass: pass, unix: len(addr) > 0 && addr[0] == '/', @@ -198,5 +199,5 @@ func (pc *socksPacketConn) Close() error { } func init() { - proxy.RegisterProtocol(Protocol, Parse) + proxy.RegisterProtocol(protocol, Parse) } diff --git a/proxy/url.go b/proxy/url.go new file mode 100644 index 00000000..0f6bcf31 --- /dev/null +++ b/proxy/url.go @@ -0,0 +1,45 @@ +package proxy + +import ( + "errors" + "net/url" + "strings" +) + +// URL is the universal representation of the proxy configuration. +type URL url.URL + +func (u *URL) Protocol() string { + return u.Scheme +} + +func (u *URL) Address() string { + return u.Host +} + +func (u *URL) String() string { + return (&url.URL{ + Scheme: u.Scheme, + Host: u.Host, + Path: strings.TrimRight(u.Path, "/"), + }).String() +} + +func ParseURL(rawURL string) (*URL, error) { + proxyURL, err := url.Parse(rawURL) + if err != nil { + return nil, err + } + if proxyURL.Scheme == "" { + return nil, errors.New("proxy: protocol not specified") + } + return (*URL)(proxyURL), nil +} + +func MustParseURL(rawURL string) *URL { + u, err := ParseURL(rawURL) + if err != nil { + panic(err) + } + return u +} diff --git a/proxy/url_test.go b/proxy/url_test.go new file mode 100644 index 00000000..16ee472f --- /dev/null +++ b/proxy/url_test.go @@ -0,0 +1,31 @@ +package proxy + +import ( + "testing" + + "github.com/stretchr/testify/suite" +) + +type URLTestSuite struct { + suite.Suite +} + +func (s *URLTestSuite) TestAddress() { + tests := []struct { + u *URL + expected string + }{ + { + MustParseURL("http://example.com/"), + "http://example.com", + }, + } + for _, tt := range tests { + s.Assert().Equal(tt.expected, tt.u.String()) + } + +} + +func TestURLTestSuite(t *testing.T) { + suite.Run(t, new(URLTestSuite)) +}