github.com/chwjbn/xclash@v0.2.0/adapter/outbound/shadowsocks.go (about)

     1  package outbound
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net"
     8  	"strconv"
     9  
    10  	"github.com/chwjbn/xclash/common/structure"
    11  	"github.com/chwjbn/xclash/component/dialer"
    12  	C "github.com/chwjbn/xclash/constant"
    13  	obfs "github.com/chwjbn/xclash/transport/simple-obfs"
    14  	"github.com/chwjbn/xclash/transport/socks5"
    15  	v2rayObfs "github.com/chwjbn/xclash/transport/v2ray-plugin"
    16  
    17  	"github.com/Dreamacro/go-shadowsocks2/core"
    18  )
    19  
    20  type ShadowSocks struct {
    21  	*Base
    22  	cipher core.Cipher
    23  
    24  	// obfs
    25  	obfsMode    string
    26  	obfsOption  *simpleObfsOption
    27  	v2rayOption *v2rayObfs.Option
    28  }
    29  
    30  type ShadowSocksOption struct {
    31  	BasicOption
    32  	Name       string                 `proxy:"name"`
    33  	Server     string                 `proxy:"server"`
    34  	Port       int                    `proxy:"port"`
    35  	Password   string                 `proxy:"password"`
    36  	Cipher     string                 `proxy:"cipher"`
    37  	UDP        bool                   `proxy:"udp,omitempty"`
    38  	Plugin     string                 `proxy:"plugin,omitempty"`
    39  	PluginOpts map[string]interface{} `proxy:"plugin-opts,omitempty"`
    40  }
    41  
    42  type simpleObfsOption struct {
    43  	Mode string `obfs:"mode,omitempty"`
    44  	Host string `obfs:"host,omitempty"`
    45  }
    46  
    47  type v2rayObfsOption struct {
    48  	Mode           string            `obfs:"mode"`
    49  	Host           string            `obfs:"host,omitempty"`
    50  	Path           string            `obfs:"path,omitempty"`
    51  	TLS            bool              `obfs:"tls,omitempty"`
    52  	Headers        map[string]string `obfs:"headers,omitempty"`
    53  	SkipCertVerify bool              `obfs:"skip-cert-verify,omitempty"`
    54  	Mux            bool              `obfs:"mux,omitempty"`
    55  }
    56  
    57  // StreamConn implements C.ProxyAdapter
    58  func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
    59  	switch ss.obfsMode {
    60  	case "tls":
    61  		c = obfs.NewTLSObfs(c, ss.obfsOption.Host)
    62  	case "http":
    63  		_, port, _ := net.SplitHostPort(ss.addr)
    64  		c = obfs.NewHTTPObfs(c, ss.obfsOption.Host, port)
    65  	case "websocket":
    66  		var err error
    67  		c, err = v2rayObfs.NewV2rayObfs(c, ss.v2rayOption)
    68  		if err != nil {
    69  			return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
    70  		}
    71  	}
    72  	c = ss.cipher.StreamConn(c)
    73  	_, err := c.Write(serializesSocksAddr(metadata))
    74  	return c, err
    75  }
    76  
    77  // DialContext implements C.ProxyAdapter
    78  func (ss *ShadowSocks) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
    79  	c, err := dialer.DialContext(ctx, "tcp", ss.addr, ss.Base.DialOptions(opts...)...)
    80  	if err != nil {
    81  		return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
    82  	}
    83  	tcpKeepAlive(c)
    84  
    85  	defer safeConnClose(c, err)
    86  
    87  	c, err = ss.StreamConn(c, metadata)
    88  	return NewConn(c, ss), err
    89  }
    90  
    91  // ListenPacketContext implements C.ProxyAdapter
    92  func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
    93  	pc, err := dialer.ListenPacket(ctx, "udp", "", ss.Base.DialOptions(opts...)...)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	addr, err := resolveUDPAddr("udp", ss.addr)
    99  	if err != nil {
   100  		pc.Close()
   101  		return nil, err
   102  	}
   103  
   104  	pc = ss.cipher.PacketConn(pc)
   105  	return newPacketConn(&ssPacketConn{PacketConn: pc, rAddr: addr}, ss), nil
   106  }
   107  
   108  func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
   109  	addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
   110  	cipher := option.Cipher
   111  	password := option.Password
   112  	ciph, err := core.PickCipher(cipher, nil, password)
   113  	if err != nil {
   114  		return nil, fmt.Errorf("ss %s initialize error: %w", addr, err)
   115  	}
   116  
   117  	var v2rayOption *v2rayObfs.Option
   118  	var obfsOption *simpleObfsOption
   119  	obfsMode := ""
   120  
   121  	decoder := structure.NewDecoder(structure.Option{TagName: "obfs", WeaklyTypedInput: true})
   122  	if option.Plugin == "obfs" {
   123  		opts := simpleObfsOption{Host: "bing.com"}
   124  		if err := decoder.Decode(option.PluginOpts, &opts); err != nil {
   125  			return nil, fmt.Errorf("ss %s initialize obfs error: %w", addr, err)
   126  		}
   127  
   128  		if opts.Mode != "tls" && opts.Mode != "http" {
   129  			return nil, fmt.Errorf("ss %s obfs mode error: %s", addr, opts.Mode)
   130  		}
   131  		obfsMode = opts.Mode
   132  		obfsOption = &opts
   133  	} else if option.Plugin == "v2ray-plugin" {
   134  		opts := v2rayObfsOption{Host: "bing.com", Mux: true}
   135  		if err := decoder.Decode(option.PluginOpts, &opts); err != nil {
   136  			return nil, fmt.Errorf("ss %s initialize v2ray-plugin error: %w", addr, err)
   137  		}
   138  
   139  		if opts.Mode != "websocket" {
   140  			return nil, fmt.Errorf("ss %s obfs mode error: %s", addr, opts.Mode)
   141  		}
   142  		obfsMode = opts.Mode
   143  		v2rayOption = &v2rayObfs.Option{
   144  			Host:    opts.Host,
   145  			Path:    opts.Path,
   146  			Headers: opts.Headers,
   147  			Mux:     opts.Mux,
   148  		}
   149  
   150  		if opts.TLS {
   151  			v2rayOption.TLS = true
   152  			v2rayOption.SkipCertVerify = opts.SkipCertVerify
   153  		}
   154  	}
   155  
   156  	return &ShadowSocks{
   157  		Base: &Base{
   158  			name:  option.Name,
   159  			addr:  addr,
   160  			tp:    C.Shadowsocks,
   161  			udp:   option.UDP,
   162  			iface: option.Interface,
   163  		},
   164  		cipher: ciph,
   165  
   166  		obfsMode:    obfsMode,
   167  		v2rayOption: v2rayOption,
   168  		obfsOption:  obfsOption,
   169  	}, nil
   170  }
   171  
   172  type ssPacketConn struct {
   173  	net.PacketConn
   174  	rAddr net.Addr
   175  }
   176  
   177  func (spc *ssPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
   178  	packet, err := socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b)
   179  	if err != nil {
   180  		return
   181  	}
   182  	return spc.PacketConn.WriteTo(packet[3:], spc.rAddr)
   183  }
   184  
   185  func (spc *ssPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
   186  	n, _, e := spc.PacketConn.ReadFrom(b)
   187  	if e != nil {
   188  		return 0, nil, e
   189  	}
   190  
   191  	addr := socks5.SplitAddr(b[:n])
   192  	if addr == nil {
   193  		return 0, nil, errors.New("parse addr error")
   194  	}
   195  
   196  	udpAddr := addr.UDPAddr()
   197  	if udpAddr == nil {
   198  		return 0, nil, errors.New("parse addr error")
   199  	}
   200  
   201  	copy(b, b[len(addr):])
   202  	return n - len(addr), udpAddr, e
   203  }