github.com/gospider007/requests@v0.0.0-20240506025355-c73d46169a23/dial.go (about) 1 package requests 2 3 import ( 4 "bufio" 5 "context" 6 "crypto/tls" 7 "errors" 8 "io" 9 "net" 10 "net/textproto" 11 "net/url" 12 "sync" 13 "time" 14 15 "net/http" 16 17 "github.com/gospider007/gtls" 18 "github.com/gospider007/ja3" 19 "github.com/gospider007/tools" 20 utls "github.com/refraction-networking/utls" 21 ) 22 23 type DialClient struct { 24 dialer *net.Dialer 25 dnsIpData sync.Map 26 dns *net.UDPAddr 27 getAddrType func(host string) gtls.AddrType 28 } 29 type msgClient struct { 30 time time.Time 31 host string 32 } 33 34 type DialOption struct { 35 DialTimeout time.Duration 36 KeepAlive time.Duration 37 LocalAddr *net.TCPAddr //network card ip 38 AddrType gtls.AddrType //first ip type 39 Dns *net.UDPAddr 40 GetAddrType func(host string) gtls.AddrType 41 } 42 43 func NewDialer(option DialOption) *net.Dialer { 44 if option.KeepAlive == 0 { 45 option.KeepAlive = time.Second * 30 46 } 47 if option.DialTimeout == 0 { 48 option.DialTimeout = time.Second * 15 49 } 50 dialer := &net.Dialer{ 51 Timeout: option.DialTimeout, 52 KeepAlive: option.KeepAlive, 53 LocalAddr: option.LocalAddr, 54 } 55 if option.LocalAddr != nil { 56 dialer.LocalAddr = option.LocalAddr 57 } 58 if option.Dns != nil { 59 dialer.Resolver = &net.Resolver{ 60 PreferGo: true, 61 Dial: func(ctx context.Context, network, address string) (net.Conn, error) { 62 return (&net.Dialer{ 63 Timeout: option.DialTimeout, 64 KeepAlive: option.KeepAlive, 65 }).DialContext(ctx, network, option.Dns.String()) 66 }, 67 } 68 } 69 dialer.SetMultipathTCP(true) 70 return dialer 71 } 72 func NewDail(option DialOption) *DialClient { 73 return &DialClient{ 74 dialer: NewDialer(option), 75 dns: option.Dns, 76 getAddrType: option.GetAddrType, 77 } 78 } 79 func (obj *DialClient) DialContext(ctx context.Context, ctxData *reqCtxData, network string, addr string) (net.Conn, error) { 80 if ctxData == nil { 81 ctxData = &reqCtxData{} 82 } 83 host, port, err := net.SplitHostPort(addr) 84 if err != nil { 85 return nil, tools.WrapError(err, "addrToIp error,SplitHostPort") 86 } 87 var dialer *net.Dialer 88 if _, ipInt := gtls.ParseHost(host); ipInt == 0 { //domain 89 host, ok := obj.loadHost(host) 90 if !ok { //dns parse 91 dialer = obj.getDialer(ctxData, true) 92 var addrType gtls.AddrType 93 if ctxData.addrType != 0 { 94 addrType = ctxData.addrType 95 } else if obj.getAddrType != nil { 96 addrType = obj.getAddrType(host) 97 } 98 ips, err := dialer.Resolver.LookupIPAddr(ctx, host) 99 if err != nil { 100 return nil, err 101 } 102 if host, err = obj.addrToIp(host, ips, addrType); err != nil { 103 return nil, err 104 } 105 addr = net.JoinHostPort(host, port) 106 } 107 } 108 if dialer == nil { 109 dialer = obj.getDialer(ctxData, false) 110 } 111 return dialer.DialContext(ctx, network, addr) 112 } 113 func (obj *DialClient) DialContextWithProxy(ctx context.Context, ctxData *reqCtxData, network string, scheme string, addr string, host string, proxyUrl *url.URL, tlsConfig *tls.Config) (net.Conn, error) { 114 if ctxData == nil { 115 ctxData = &reqCtxData{} 116 } 117 if proxyUrl == nil { 118 return obj.DialContext(ctx, ctxData, network, addr) 119 } 120 if proxyUrl.Port() == "" { 121 if proxyUrl.Scheme == "http" { 122 proxyUrl.Host = net.JoinHostPort(proxyUrl.Hostname(), "80") 123 } else if proxyUrl.Scheme == "https" { 124 proxyUrl.Host = net.JoinHostPort(proxyUrl.Hostname(), "443") 125 } 126 } 127 switch proxyUrl.Scheme { 128 case "http", "https": 129 conn, err := obj.DialContext(ctx, ctxData, network, net.JoinHostPort(proxyUrl.Hostname(), proxyUrl.Port())) 130 if err != nil { 131 return conn, err 132 } else if proxyUrl.Scheme == "https" { 133 if conn, err = obj.addTls(ctx, conn, proxyUrl.Host, true, tlsConfig); err != nil { 134 return conn, err 135 } 136 } 137 return conn, obj.clientVerifyHttps(ctx, scheme, proxyUrl, addr, host, conn) 138 case "socks5": 139 return obj.Socks5Proxy(ctx, ctxData, network, addr, proxyUrl) 140 default: 141 return nil, errors.New("proxyUrl Scheme error") 142 } 143 } 144 func (obj *DialClient) loadHost(host string) (string, bool) { 145 msgDataAny, ok := obj.dnsIpData.Load(host) 146 if ok { 147 msgdata := msgDataAny.(msgClient) 148 if time.Since(msgdata.time) < time.Second*60*5 { 149 return msgdata.host, true 150 } 151 } 152 return host, false 153 } 154 func (obj *DialClient) addrToIp(host string, ips []net.IPAddr, addrType gtls.AddrType) (string, error) { 155 ip, err := obj.lookupIPAddr(ips, addrType) 156 if err != nil { 157 return host, tools.WrapError(err, "addrToIp error,lookupIPAddr") 158 } 159 obj.dnsIpData.Store(host, msgClient{time: time.Now(), host: ip.String()}) 160 return ip.String(), nil 161 } 162 func (obj *DialClient) clientVerifySocks5(proxyUrl *url.URL, addr string, conn net.Conn) (err error) { 163 if _, err = conn.Write([]byte{5, 2, 0, 2}); err != nil { 164 return 165 } 166 readCon := make([]byte, 4) 167 if _, err = io.ReadFull(conn, readCon[:2]); err != nil { 168 return 169 } 170 switch readCon[1] { 171 case 2: 172 if proxyUrl.User == nil { 173 err = errors.New("socks5 need auth") 174 return 175 } 176 pwd, pwdOk := proxyUrl.User.Password() 177 if !pwdOk { 178 err = errors.New("socks5 auth error") 179 return 180 } 181 usr := proxyUrl.User.Username() 182 183 if usr == "" { 184 err = errors.New("socks5 auth user format error") 185 return 186 } 187 if _, err = conn.Write(append( 188 append( 189 []byte{1, byte(len(usr))}, 190 tools.StringToBytes(usr)..., 191 ), 192 append( 193 []byte{byte(len(pwd))}, 194 tools.StringToBytes(pwd)..., 195 )..., 196 )); err != nil { 197 return 198 } 199 if _, err = io.ReadFull(conn, readCon[:2]); err != nil { 200 return 201 } 202 switch readCon[1] { 203 case 0: 204 default: 205 err = errors.New("socks5 auth error") 206 return 207 } 208 case 0: 209 default: 210 err = errors.New("not support auth format") 211 return 212 } 213 var host string 214 var port int 215 if host, port, err = gtls.SplitHostPort(addr); err != nil { 216 return 217 } 218 writeCon := []byte{5, 1, 0} 219 ip, ipInt := gtls.ParseHost(host) 220 switch ipInt { 221 case 4: 222 writeCon = append(writeCon, 1) 223 writeCon = append(writeCon, ip...) 224 case 6: 225 writeCon = append(writeCon, 4) 226 writeCon = append(writeCon, ip...) 227 case 0: 228 if len(host) > 255 { 229 err = errors.New("FQDN too long") 230 return 231 } 232 writeCon = append(writeCon, 3) 233 writeCon = append(writeCon, byte(len(host))) 234 writeCon = append(writeCon, host...) 235 } 236 writeCon = append(writeCon, byte(port>>8), byte(port)) 237 if _, err = conn.Write(writeCon); err != nil { 238 return 239 } 240 if _, err = io.ReadFull(conn, readCon); err != nil { 241 return 242 } 243 if readCon[0] != 5 { 244 err = errors.New("socks version error") 245 return 246 } 247 if readCon[1] != 0 { 248 err = errors.New("socks conn error") 249 return 250 } 251 if readCon[3] != 1 { 252 err = errors.New("socks conn type error") 253 return 254 } 255 256 switch readCon[3] { 257 case 1: //ipv4 258 if _, err = io.ReadFull(conn, readCon); err != nil { 259 return 260 } 261 case 3: //domain 262 if _, err = io.ReadFull(conn, readCon[:1]); err != nil { 263 return 264 } 265 if _, err = io.ReadFull(conn, make([]byte, readCon[0])); err != nil { 266 return 267 } 268 case 4: //IPv6 269 if _, err = io.ReadFull(conn, make([]byte, 16)); err != nil { 270 return 271 } 272 default: 273 err = errors.New("invalid atyp") 274 return 275 } 276 _, err = io.ReadFull(conn, readCon[:2]) 277 return 278 } 279 func (obj *DialClient) lookupIPAddr(ips []net.IPAddr, addrType gtls.AddrType) (net.IP, error) { 280 for _, ipAddr := range ips { 281 ip := ipAddr.IP 282 if ipType := gtls.ParseIp(ip); ipType == 4 || ipType == 6 { 283 if addrType == 0 || addrType == ipType { 284 return ip, nil 285 } 286 } 287 } 288 for _, ipAddr := range ips { 289 ip := ipAddr.IP 290 if ipType := gtls.ParseIp(ip); ipType == 4 || ipType == 6 { 291 return ip, nil 292 } 293 } 294 return nil, errors.New("dns parse host error") 295 } 296 func (obj *DialClient) getDialer(ctxData *reqCtxData, parseDns bool) *net.Dialer { 297 var dialOption DialOption 298 var isNew bool 299 if ctxData.dialTimeout == 0 { 300 dialOption.DialTimeout = obj.dialer.Timeout 301 } else { 302 dialOption.DialTimeout = ctxData.dialTimeout 303 if ctxData.dialTimeout != obj.dialer.Timeout { 304 isNew = true 305 } 306 } 307 308 if ctxData.keepAlive == 0 { 309 dialOption.KeepAlive = obj.dialer.KeepAlive 310 } else { 311 dialOption.KeepAlive = ctxData.keepAlive 312 if ctxData.keepAlive != obj.dialer.KeepAlive { 313 isNew = true 314 } 315 } 316 317 if ctxData.localAddr == nil { 318 if obj.dialer.LocalAddr != nil { 319 dialOption.LocalAddr = obj.dialer.LocalAddr.(*net.TCPAddr) 320 } 321 } else { 322 dialOption.LocalAddr = ctxData.localAddr 323 if ctxData.localAddr.String() != obj.dialer.LocalAddr.String() { 324 isNew = true 325 } 326 } 327 if ctxData.dns == nil { 328 dialOption.Dns = obj.dns 329 } else { 330 dialOption.Dns = ctxData.dns 331 if parseDns && ctxData.dns.String() != obj.dns.String() { 332 isNew = true 333 } 334 } 335 if isNew { 336 return NewDialer(dialOption) 337 } else { 338 return obj.dialer 339 } 340 } 341 func (obj *DialClient) addTls(ctx context.Context, conn net.Conn, host string, disHttp2 bool, tlsConfig *tls.Config) (*tls.Conn, error) { 342 var tlsConn *tls.Conn 343 tlsConfig.ServerName = gtls.GetServerName(host) 344 if disHttp2 { 345 tlsConfig.NextProtos = []string{"http/1.1"} 346 } else { 347 tlsConfig.NextProtos = []string{"h2", "http/1.1"} 348 } 349 tlsConn = tls.Client(conn, tlsConfig) 350 return tlsConn, tlsConn.HandshakeContext(ctx) 351 } 352 func (obj *DialClient) addJa3Tls(ctx context.Context, conn net.Conn, host string, disHttp2 bool, ja3Spec ja3.Ja3Spec, tlsConfig *utls.Config) (*utls.UConn, error) { 353 tlsConfig.ServerName = gtls.GetServerName(host) 354 if disHttp2 { 355 tlsConfig.NextProtos = []string{"http/1.1"} 356 } else { 357 tlsConfig.NextProtos = []string{"h2", "http/1.1"} 358 } 359 return ja3.NewClient(ctx, conn, ja3Spec, disHttp2, tlsConfig) 360 } 361 func (obj *DialClient) Socks5Proxy(ctx context.Context, ctxData *reqCtxData, network string, addr string, proxyUrl *url.URL) (conn net.Conn, err error) { 362 if conn, err = obj.DialContext(ctx, ctxData, network, net.JoinHostPort(proxyUrl.Hostname(), proxyUrl.Port())); err != nil { 363 return 364 } 365 didVerify := make(chan struct{}) 366 go func() { 367 if err = obj.clientVerifySocks5(proxyUrl, addr, conn); err != nil { 368 conn.Close() 369 } 370 close(didVerify) 371 }() 372 select { 373 case <-ctx.Done(): 374 return conn, ctx.Err() 375 case <-didVerify: 376 return 377 } 378 } 379 func (obj *DialClient) clientVerifyHttps(ctx context.Context, scheme string, proxyUrl *url.URL, addr string, host string, conn net.Conn) (err error) { 380 hdr := make(http.Header) 381 hdr.Set("User-Agent", UserAgent) 382 if proxyUrl.User != nil { 383 if password, ok := proxyUrl.User.Password(); ok { 384 hdr.Set("Proxy-Authorization", "Basic "+tools.Base64Encode(proxyUrl.User.Username()+":"+password)) 385 } 386 } 387 cHost := host 388 _, hport, _ := net.SplitHostPort(host) 389 if hport == "" { 390 _, aport, _ := net.SplitHostPort(addr) 391 if aport != "" { 392 cHost = net.JoinHostPort(cHost, aport) 393 } else if scheme == "http" { 394 cHost = net.JoinHostPort(cHost, "80") 395 } else if scheme == "https" { 396 cHost = net.JoinHostPort(cHost, "443") 397 } else { 398 return errors.New("clientVerifyHttps not found port") 399 } 400 } 401 connectReq, err := NewRequestWithContext(ctx, http.MethodConnect, &url.URL{Opaque: addr}, nil) 402 if err != nil { 403 return err 404 } 405 connectReq.Header = hdr 406 connectReq.Host = cHost 407 if err = connectReq.Write(conn); err != nil { 408 return err 409 } 410 resp, err := readResponse(textproto.NewReader(bufio.NewReader(conn)), connectReq) 411 if err != nil { 412 return err 413 } 414 if resp.StatusCode != 200 { 415 return errors.New(resp.Status) 416 } 417 return 418 }