Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor(proxy): make proxies registerable #383

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,10 @@ 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.SetDialer(_defaultProxy)
proxy.DefaultProxy = _defaultProxy

if _defaultDevice, err = parseDevice(k.Device, uint32(k.MTU)); err != nil {
return
Expand Down Expand Up @@ -236,7 +236,7 @@ func netstack(k *Key) (err error) {
log.Infof(
"[STACK] %s://%s <-> %s://%s",
_defaultDevice.Type(), _defaultDevice.Name(),
_defaultProxy.Proto(), _defaultProxy.Addr(),
_defaultProxy.Protocol(), _defaultProxy.Address(),
)
return nil
}
114 changes: 0 additions & 114 deletions engine/parse.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
package engine

import (
"encoding/base64"
"fmt"
"net"
"net/url"
"strings"

"github.com/gorilla/schema"

"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"
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
)

func parseRestAPI(s string) (*url.URL, error) {
Expand Down Expand Up @@ -69,115 +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) {
if !strings.Contains(s, "://") {
s = fmt.Sprintf("%s://%s", proto.Socks5 /* default protocol */, s)
}

u, err := url.Parse(s)
if err != nil {
return nil, err
}

protocol := strings.ToLower(u.Scheme)

switch protocol {
case proto.Direct.String():
return proxy.NewDirect(), nil
case proto.Reject.String():
return proxy.NewReject(), nil
case proto.HTTP.String():
return parseHTTP(u)
case proto.Socks4.String():
return parseSocks4(u)
case proto.Socks5.String():
return parseSocks5(u)
case proto.Shadowsocks.String():
return parseShadowsocks(u)
case proto.Relay.String():
return parseRelay(u)
default:
return nil, fmt.Errorf("unsupported protocol: %s", protocol)
}
}

func parseHTTP(u *url.URL) (proxy.Proxy, error) {
address, username := u.Host, u.User.Username()
password, _ := u.User.Password()
return proxy.NewHTTP(address, username, password)
}

func parseSocks4(u *url.URL) (proxy.Proxy, error) {
address, userID := u.Host, u.User.Username()
return proxy.NewSocks4(address, userID)
}

func parseSocks5(u *url.URL) (proxy.Proxy, error) {
address, username := u.Host, u.User.Username()
password, _ := u.User.Password()

// Socks5 over UDS
if address == "" {
address = u.Path
}
return proxy.NewSocks5(address, username, password)
}

func parseShadowsocks(u *url.URL) (proxy.Proxy, error) {
var (
address = u.Host
method, password string
obfsMode, obfsHost string
)

if ss := u.User.String(); ss == "" {
method = "dummy" // none cipher mode
} else if pass, set := u.User.Password(); set {
method = u.User.Username()
password = pass
} else {
data, _ := base64.RawURLEncoding.DecodeString(ss)
userInfo := strings.SplitN(string(data), ":", 2)
if len(userInfo) == 2 {
method = userInfo[0]
password = userInfo[1]
}
}

rawQuery, _ := url.QueryUnescape(u.RawQuery)
for _, s := range strings.Split(rawQuery, ";") {
data := strings.SplitN(s, "=", 2)
if len(data) != 2 {
continue
}
key := data[0]
value := data[1]

switch key {
case "obfs":
obfsMode = value
case "obfs-host":
obfsHost = value
}
}

return proxy.NewShadowsocks(address, method, password, obfsMode, obfsHost)
}

func parseRelay(u *url.URL) (proxy.Proxy, error) {
address, username := u.Host, u.User.Username()
password, _ := u.User.Password()

opts := struct {
NoDelay bool
}{}
if err := schema.NewDecoder().Decode(&opts, u.Query()); err != nil {
return nil, err
}

return proxy.NewRelay(address, username, password, opts.NoDelay)
}

func parseMulticastGroups(s string) (multicastGroups []net.IP, _ error) {
ipStrings := strings.Split(s, ",")
for _, ipString := range ipStrings {
Expand Down
11 changes: 11 additions & 0 deletions engine/proxy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package engine

import (
_ "github.com/xjasonlyu/tun2socks/v2/proxy/direct"
_ "github.com/xjasonlyu/tun2socks/v2/proxy/http"
_ "github.com/xjasonlyu/tun2socks/v2/proxy/reject"
_ "github.com/xjasonlyu/tun2socks/v2/proxy/relay"
_ "github.com/xjasonlyu/tun2socks/v2/proxy/shadowsocks"
_ "github.com/xjasonlyu/tun2socks/v2/proxy/socks4"
_ "github.com/xjasonlyu/tun2socks/v2/proxy/socks5"
)
33 changes: 0 additions & 33 deletions proxy/base.go

This file was deleted.

31 changes: 17 additions & 14 deletions proxy/direct.go → proxy/direct/direct.go
Original file line number Diff line number Diff line change
@@ -1,34 +1,33 @@
package proxy
package direct

import (
"context"
"net"
"net/url"

"github.com/xjasonlyu/tun2socks/v2/dialer"
M "github.com/xjasonlyu/tun2socks/v2/metadata"
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
"github.com/xjasonlyu/tun2socks/v2/proxy"
"github.com/xjasonlyu/tun2socks/v2/proxy/internal"
"github.com/xjasonlyu/tun2socks/v2/proxy/internal/base"
)

var _ Proxy = (*Direct)(nil)
var _ proxy.Proxy = (*Direct)(nil)

type Direct struct {
*Base
}
const protocol = "direct"

func NewDirect() *Direct {
return &Direct{
Base: &Base{
proto: proto.Direct,
},
}
}
type Direct struct{ *base.Base }

func New() *Direct { return &Direct{base.New("", protocol)} }

func Parse(*url.URL) (proxy.Proxy, error) { return New(), nil }

func (d *Direct) DialContext(ctx context.Context, metadata *M.Metadata) (net.Conn, error) {
c, err := dialer.DialContext(ctx, "tcp", metadata.DestinationAddress())
if err != nil {
return nil, err
}
setKeepAlive(c)
internal.SetKeepAlive(c)
return c, nil
}

Expand All @@ -55,3 +54,7 @@ func (pc *directPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
}
return pc.PacketConn.WriteTo(b, udpAddr)
}

func init() {
proxy.RegisterProtocol(protocol, Parse)
}
37 changes: 25 additions & 12 deletions proxy/http.go → proxy/http/http.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package proxy
package http

import (
"bufio"
Expand All @@ -13,36 +13,45 @@ import (

"github.com/xjasonlyu/tun2socks/v2/dialer"
M "github.com/xjasonlyu/tun2socks/v2/metadata"
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
"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"

type HTTP struct {
*Base
*base.Base

user string
pass string
}

func NewHTTP(addr, user, pass string) (*HTTP, error) {
func New(addr, user, pass string) (*HTTP, error) {
return &HTTP{
Base: &Base{
addr: addr,
proto: proto.HTTP,
},
Base: base.New(addr, protocol),
user: user,
pass: pass,
}, nil
}

func Parse(proxyURL *url.URL) (proxy.Proxy, error) {
address, username := proxyURL.Host, proxyURL.User.Username()
password, _ := proxyURL.User.Password()
return New(address, username, password)
}

func (h *HTTP) DialContext(ctx context.Context, metadata *M.Metadata) (c net.Conn, err error) {
c, err = dialer.DialContext(ctx, "tcp", h.Addr())
c, err = dialer.DialContext(ctx, "tcp", h.Address())
if err != nil {
return nil, fmt.Errorf("connect to %s: %w", h.Addr(), err)
return nil, fmt.Errorf("connect to %s: %w", h.Address(), err)
}
setKeepAlive(c)
internal.SetKeepAlive(c)

defer func(c net.Conn) {
safeConnClose(c, err)
internal.SafeConnClose(c, err)
}(c)

err = h.shakeHand(metadata, c)
Expand Down Expand Up @@ -98,3 +107,7 @@ func basicAuth(username, password string) string {
auth := username + ":" + password
return base64.StdEncoding.EncodeToString([]byte(auth))
}

func init() {
proxy.RegisterProtocol(protocol, Parse)
}
44 changes: 44 additions & 0 deletions proxy/internal/base/base.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package base

import (
"context"
"errors"
"fmt"
"net"

M "github.com/xjasonlyu/tun2socks/v2/metadata"
"github.com/xjasonlyu/tun2socks/v2/proxy"
)

var _ proxy.Proxy = (*Base)(nil)

type Base struct {
address, protocol string
}

func New(address, protocol string) *Base {
return &Base{
address: address,
protocol: protocol,
}
}

func (b *Base) Address() string {
return b.address
}

func (b *Base) Protocol() string {
return b.protocol
}

func (b *Base) String() string {
return fmt.Sprintf("%s://%s", b.protocol, b.address)
}

func (b *Base) DialContext(context.Context, *M.Metadata) (net.Conn, error) {
return nil, errors.ErrUnsupported
}

func (b *Base) DialUDP(*M.Metadata) (net.PacketConn, error) {
return nil, errors.ErrUnsupported
}
Loading
Loading