github.com/database64128/shadowsocks-go@v1.10.2-0.20240315062903-143a773533f1/service/client.go (about)

     1  package service
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/database64128/shadowsocks-go/conn"
     8  	"github.com/database64128/shadowsocks-go/direct"
     9  	"github.com/database64128/shadowsocks-go/http"
    10  	"github.com/database64128/shadowsocks-go/ss2022"
    11  	"github.com/database64128/shadowsocks-go/zerocopy"
    12  	"go.uber.org/zap"
    13  )
    14  
    15  // ClientConfig stores a client configuration.
    16  // It may be marshaled as or unmarshaled from JSON.
    17  type ClientConfig struct {
    18  	// Name is the name of the client.
    19  	Name string `json:"name"`
    20  
    21  	// Protocol is the protocol used by the client.
    22  	// Valid values include "direct", "socks5", "http", "none", "plain", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm".
    23  	Protocol string `json:"protocol"`
    24  
    25  	// Network controls the address family of the resolved IP address
    26  	// when the address is a domain name. It is ignored if the address
    27  	// is an IP address.
    28  	//
    29  	// - "ip": Follow the system default.
    30  	// - "ip4": Resolve to an IPv4 address.
    31  	// - "ip6": Resolve to an IPv6 address.
    32  	//
    33  	// If unspecified, "ip" is used.
    34  	Network string `json:"network"`
    35  
    36  	// Endpoint is the address of the remote proxy server, if applicable.
    37  	//
    38  	// Do not use if either TCPAddress or UDPAddress is specified.
    39  	Endpoint conn.Addr `json:"endpoint"`
    40  
    41  	// TCPAddress is the TCP address of the remote proxy server, if applicable.
    42  	//
    43  	// Do not use if Endpoint is specified.
    44  	TCPAddress conn.Addr `json:"tcpAddress"`
    45  
    46  	// UDPAddress is the UDP address of the remote proxy server, if applicable.
    47  	//
    48  	// Do not use if Endpoint is specified.
    49  	UDPAddress conn.Addr `json:"udpAddress"`
    50  
    51  	DialerFwmark       int `json:"dialerFwmark"`
    52  	DialerTrafficClass int `json:"dialerTrafficClass"`
    53  
    54  	// TCP
    55  
    56  	EnableTCP bool `json:"enableTCP"`
    57  	DialerTFO bool `json:"dialerTFO"`
    58  
    59  	// TCPFastOpenFallback enables runtime detection of TCP Fast Open support on the dialer.
    60  	//
    61  	// When enabled, the dialer will connect without TFO if TFO is not available on the system.
    62  	// When disabled, the dialer will abort if TFO cannot be enabled on the socket.
    63  	//
    64  	// Available on all platforms.
    65  	TCPFastOpenFallback bool `json:"tcpFastOpenFallback"`
    66  
    67  	// MultipathTCP enables multipath TCP on the client.
    68  	//
    69  	// Unlike Go std, we make MPTCP strictly opt-in.
    70  	// That is, if this field is false, MPTCP will be explicitly disabled.
    71  	// This ensures that if Go std suddenly decides to enable MPTCP by default,
    72  	// existing configurations won't encounter issues due to missing features in the kernel MPTCP stack,
    73  	// such as TCP keepalive (as of Linux 6.5), and failed connect attempts won't always be retried once.
    74  	//
    75  	// Available on platforms supported by Go std's MPTCP implementation.
    76  	MultipathTCP bool `json:"multipathTCP"`
    77  
    78  	// AllowSegmentedFixedLengthHeader disables the requirement that
    79  	// the fixed-length header must be read in a single read call.
    80  	//
    81  	// This option is useful when the underlying stream transport
    82  	// does not exhibit typical TCP behavior.
    83  	//
    84  	// Only applicable to Shadowsocks 2022 TCP.
    85  	AllowSegmentedFixedLengthHeader bool `json:"allowSegmentedFixedLengthHeader"`
    86  
    87  	// UDP
    88  
    89  	EnableUDP bool `json:"enableUDP"`
    90  	MTU       int  `json:"mtu"`
    91  
    92  	// Shadowsocks
    93  
    94  	PSK           []byte   `json:"psk"`
    95  	IPSKs         [][]byte `json:"iPSKs"`
    96  	PaddingPolicy string   `json:"paddingPolicy"`
    97  
    98  	// SlidingWindowFilterSize is the size of the sliding window filter.
    99  	//
   100  	// The default value is 256.
   101  	//
   102  	// Only applicable to Shadowsocks 2022 UDP.
   103  	SlidingWindowFilterSize int `json:"slidingWindowFilterSize"`
   104  
   105  	cipherConfig *ss2022.ClientCipherConfig
   106  
   107  	// Taint
   108  
   109  	UnsafeRequestStreamPrefix  []byte `json:"unsafeRequestStreamPrefix"`
   110  	UnsafeResponseStreamPrefix []byte `json:"unsafeResponseStreamPrefix"`
   111  
   112  	listenConfigCache conn.ListenConfigCache
   113  	dialerCache       conn.DialerCache
   114  	logger            *zap.Logger
   115  }
   116  
   117  func (cc *ClientConfig) checkAddresses() error {
   118  	if cc.Protocol == "direct" {
   119  		return nil
   120  	}
   121  
   122  	ev := cc.Endpoint.IsValid()
   123  	tv := cc.TCPAddress.IsValid()
   124  	uv := cc.UDPAddress.IsValid()
   125  
   126  	if ev == (tv || uv) {
   127  		return errors.New("missing or conflicting proxy server address(es)")
   128  	}
   129  
   130  	if ev {
   131  		cc.TCPAddress = cc.Endpoint
   132  		cc.UDPAddress = cc.Endpoint
   133  		return nil
   134  	}
   135  
   136  	if cc.EnableTCP && !tv {
   137  		return errors.New("missing proxy server TCP address")
   138  	}
   139  
   140  	if cc.EnableUDP && !uv {
   141  		return errors.New("missing proxy server UDP address")
   142  	}
   143  
   144  	return nil
   145  }
   146  
   147  // Initialize initializes the client configuration.
   148  func (cc *ClientConfig) Initialize(listenConfigCache conn.ListenConfigCache, dialerCache conn.DialerCache, logger *zap.Logger) (err error) {
   149  	switch cc.Network {
   150  	case "":
   151  		cc.Network = "ip"
   152  	case "ip", "ip4", "ip6":
   153  	default:
   154  		return fmt.Errorf("unknown network: %q", cc.Network)
   155  	}
   156  
   157  	if err = cc.checkAddresses(); err != nil {
   158  		return
   159  	}
   160  
   161  	switch cc.Protocol {
   162  	case "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm":
   163  		if err = ss2022.CheckPSKLength(cc.Protocol, cc.PSK, cc.IPSKs); err != nil {
   164  			return
   165  		}
   166  		cc.cipherConfig, err = ss2022.NewClientCipherConfig(cc.PSK, cc.IPSKs, cc.EnableUDP)
   167  		if err != nil {
   168  			return
   169  		}
   170  	}
   171  
   172  	cc.listenConfigCache = listenConfigCache
   173  	cc.dialerCache = dialerCache
   174  	cc.logger = logger
   175  	return
   176  }
   177  
   178  func (cc *ClientConfig) tcpNetwork() string {
   179  	switch cc.Network {
   180  	case "ip":
   181  		return "tcp"
   182  	case "ip4":
   183  		return "tcp4"
   184  	case "ip6":
   185  		return "tcp6"
   186  	default:
   187  		panic("unreachable")
   188  	}
   189  }
   190  
   191  func (cc *ClientConfig) dialer() conn.Dialer {
   192  	return cc.dialerCache.Get(conn.DialerSocketOptions{
   193  		Fwmark:              cc.DialerFwmark,
   194  		TrafficClass:        cc.DialerTrafficClass,
   195  		TCPFastOpen:         cc.DialerTFO,
   196  		TCPFastOpenFallback: cc.TCPFastOpenFallback,
   197  		MultipathTCP:        cc.MultipathTCP,
   198  	})
   199  }
   200  
   201  // TCPClient creates a zerocopy.TCPClient from the ClientConfig.
   202  func (cc *ClientConfig) TCPClient() (zerocopy.TCPClient, error) {
   203  	if !cc.EnableTCP {
   204  		return nil, errNetworkDisabled
   205  	}
   206  
   207  	network := cc.tcpNetwork()
   208  	dialer := cc.dialer()
   209  
   210  	switch cc.Protocol {
   211  	case "direct":
   212  		return direct.NewTCPClient(cc.Name, network, dialer), nil
   213  	case "none", "plain":
   214  		return direct.NewShadowsocksNoneTCPClient(cc.Name, network, cc.TCPAddress.String(), dialer), nil
   215  	case "socks5":
   216  		return direct.NewSocks5TCPClient(cc.Name, network, cc.TCPAddress.String(), dialer), nil
   217  	case "http":
   218  		return http.NewProxyClient(cc.Name, network, cc.TCPAddress.String(), dialer), nil
   219  	case "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm":
   220  		if len(cc.UnsafeRequestStreamPrefix) != 0 || len(cc.UnsafeResponseStreamPrefix) != 0 {
   221  			cc.logger.Warn("Unsafe stream prefix taints the client", zap.String("client", cc.Name))
   222  		}
   223  		return ss2022.NewTCPClient(cc.Name, network, cc.TCPAddress.String(), dialer, cc.AllowSegmentedFixedLengthHeader, cc.cipherConfig, cc.UnsafeRequestStreamPrefix, cc.UnsafeResponseStreamPrefix), nil
   224  	default:
   225  		return nil, fmt.Errorf("unknown protocol: %s", cc.Protocol)
   226  	}
   227  }
   228  
   229  func (cc *ClientConfig) UDPClient() (zerocopy.UDPClient, error) {
   230  	if !cc.EnableUDP {
   231  		return nil, errNetworkDisabled
   232  	}
   233  
   234  	if cc.MTU < minimumMTU {
   235  		return nil, ErrMTUTooSmall
   236  	}
   237  
   238  	listenConfig := cc.listenConfigCache.Get(conn.ListenerSocketOptions{
   239  		Fwmark:           cc.DialerFwmark,
   240  		TrafficClass:     cc.DialerTrafficClass,
   241  		PathMTUDiscovery: true,
   242  	})
   243  
   244  	switch cc.Protocol {
   245  	case "direct":
   246  		return direct.NewDirectUDPClient(cc.Name, cc.Network, cc.MTU, listenConfig), nil
   247  	case "none", "plain":
   248  		return direct.NewShadowsocksNoneUDPClient(cc.Name, cc.Network, cc.UDPAddress, cc.MTU, listenConfig), nil
   249  	case "socks5":
   250  		dialer := cc.dialer()
   251  		networkTCP := cc.tcpNetwork()
   252  		return direct.NewSocks5UDPClient(cc.logger, cc.Name, networkTCP, cc.Network, cc.UDPAddress.String(), dialer, cc.MTU, listenConfig), nil
   253  	case "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm":
   254  		shouldPad, err := ss2022.ParsePaddingPolicy(cc.PaddingPolicy)
   255  		if err != nil {
   256  			return nil, err
   257  		}
   258  
   259  		switch {
   260  		case cc.SlidingWindowFilterSize == 0:
   261  			cc.SlidingWindowFilterSize = ss2022.DefaultSlidingWindowFilterSize
   262  		case cc.SlidingWindowFilterSize < 0:
   263  			return nil, fmt.Errorf("negative sliding window filter size: %d", cc.SlidingWindowFilterSize)
   264  		}
   265  
   266  		return ss2022.NewUDPClient(cc.Name, cc.Network, cc.UDPAddress, cc.MTU, listenConfig, uint64(cc.SlidingWindowFilterSize), cc.cipherConfig, shouldPad), nil
   267  	default:
   268  		return nil, fmt.Errorf("unknown protocol: %s", cc.Protocol)
   269  	}
   270  }