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 }