Skip to content

Commit

Permalink
Refactor(proxy): make proxies registerable
Browse files Browse the repository at this point in the history
  • Loading branch information
xjasonlyu committed Aug 28, 2024
1 parent 601601a commit 8ef439b
Show file tree
Hide file tree
Showing 18 changed files with 423 additions and 373 deletions.
4 changes: 2 additions & 2 deletions engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ func netstack(k *Key) (err error) {
if _defaultProxy, err = parseProxy(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
}
111 changes: 1 addition & 110 deletions engine/parse.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
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 @@ -70,112 +66,7 @@ func parseFD(u *url.URL, mtu uint32) (device.Device, error) {
}

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)
return proxy.ParseFromURL(s)
}

func parseMulticastGroups(s string) (multicastGroups []net.IP, _ error) {
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.

30 changes: 16 additions & 14 deletions proxy/direct.go → proxy/direct/direct.go
Original file line number Diff line number Diff line change
@@ -1,34 +1,32 @@
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"
)

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{ *internal.Base }

func New() *Direct { return &Direct{internal.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 +53,7 @@ func (pc *directPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
}
return pc.PacketConn.WriteTo(b, udpAddr)
}

func init() {
proxy.RegisterProtocol(Protocol, Parse)
}
36 changes: 24 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,44 @@ 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"
)

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

const Protocol = "http"

type HTTP struct {
*Base
*internal.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: internal.New(Protocol, addr),
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 +106,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.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package internal

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 {
protocol, address string
}

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

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

0 comments on commit 8ef439b

Please sign in to comment.