github.com/AntonOrnatskyi/goproxy@v0.0.0-20190205095733-4526a9fa18b4/services/http/http.go (about) 1 package http 2 3 import ( 4 "crypto/tls" 5 "fmt" 6 "io" 7 "io/ioutil" 8 logger "log" 9 "net" 10 "runtime/debug" 11 "strconv" 12 "strings" 13 "time" 14 15 server "github.com/AntonOrnatskyi/goproxy/core/cs/server" 16 "github.com/AntonOrnatskyi/goproxy/core/lib/kcpcfg" 17 "github.com/AntonOrnatskyi/goproxy/services" 18 "github.com/AntonOrnatskyi/goproxy/utils/datasize" 19 "github.com/AntonOrnatskyi/goproxy/utils/dnsx" 20 "github.com/AntonOrnatskyi/goproxy/utils/iolimiter" 21 "github.com/AntonOrnatskyi/goproxy/utils/jumper" 22 "github.com/AntonOrnatskyi/goproxy/utils/lb" 23 "github.com/AntonOrnatskyi/goproxy/utils/mapx" 24 25 "github.com/AntonOrnatskyi/goproxy/utils" 26 "github.com/AntonOrnatskyi/goproxy/utils/conncrypt" 27 28 "golang.org/x/crypto/ssh" 29 ) 30 31 type HTTPArgs struct { 32 Parent *[]string 33 CertFile *string 34 KeyFile *string 35 CaCertFile *string 36 CaCertBytes []byte 37 CertBytes []byte 38 KeyBytes []byte 39 Local *string 40 Always *bool 41 HTTPTimeout *int 42 Interval *int 43 Blocked *string 44 Direct *string 45 AuthFile *string 46 Auth *[]string 47 AuthURL *string 48 AuthURLOkCode *int 49 AuthURLTimeout *int 50 AuthURLRetry *int 51 ParentType *string 52 LocalType *string 53 Timeout *int 54 CheckParentInterval *int 55 SSHKeyFile *string 56 SSHKeyFileSalt *string 57 SSHPassword *string 58 SSHUser *string 59 SSHKeyBytes []byte 60 SSHAuthMethod ssh.AuthMethod 61 KCP kcpcfg.KCPConfigArgs 62 LocalIPS *[]string 63 DNSAddress *string 64 DNSTTL *int 65 LocalKey *string 66 ParentKey *string 67 LocalCompress *bool 68 ParentCompress *bool 69 Intelligent *string 70 LoadBalanceMethod *string 71 LoadBalanceTimeout *int 72 LoadBalanceRetryTime *int 73 LoadBalanceHashTarget *bool 74 LoadBalanceOnlyHA *bool 75 76 RateLimit *string 77 RateLimitBytes float64 78 BindListen *bool 79 Debug *bool 80 Jumper *string 81 } 82 type HTTP struct { 83 cfg HTTPArgs 84 checker utils.Checker 85 basicAuth utils.BasicAuth 86 sshClient *ssh.Client 87 lockChn chan bool 88 domainResolver dnsx.DomainResolver 89 isStop bool 90 serverChannels []*server.ServerChannel 91 userConns mapx.ConcurrentMap 92 log *logger.Logger 93 lb *lb.Group 94 jumper *jumper.Jumper 95 } 96 97 func NewHTTP() services.Service { 98 return &HTTP{ 99 cfg: HTTPArgs{}, 100 checker: utils.Checker{}, 101 basicAuth: utils.BasicAuth{}, 102 lockChn: make(chan bool, 1), 103 isStop: false, 104 serverChannels: []*server.ServerChannel{}, 105 userConns: mapx.NewConcurrentMap(), 106 } 107 } 108 func (s *HTTP) CheckArgs() (err error) { 109 110 if len(*s.cfg.Parent) == 1 && (*s.cfg.Parent)[0] == "" { 111 (*s.cfg.Parent) = []string{} 112 } 113 if len(*s.cfg.Parent) > 0 && *s.cfg.ParentType == "" { 114 err = fmt.Errorf("parent type unkown,use -T <tls|tcp|ssh|kcp>") 115 return 116 } 117 if *s.cfg.ParentType == "tls" || *s.cfg.LocalType == "tls" { 118 s.cfg.CertBytes, s.cfg.KeyBytes, err = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) 119 if err != nil { 120 return 121 } 122 if *s.cfg.CaCertFile != "" { 123 s.cfg.CaCertBytes, err = ioutil.ReadFile(*s.cfg.CaCertFile) 124 if err != nil { 125 err = fmt.Errorf("read ca file error,ERR:%s", err) 126 return 127 } 128 } 129 } 130 if *s.cfg.ParentType == "ssh" { 131 if *s.cfg.SSHUser == "" { 132 err = fmt.Errorf("ssh user required") 133 return 134 } 135 if *s.cfg.SSHKeyFile == "" && *s.cfg.SSHPassword == "" { 136 err = fmt.Errorf("ssh password or key required") 137 return 138 } 139 140 if *s.cfg.SSHPassword != "" { 141 s.cfg.SSHAuthMethod = ssh.Password(*s.cfg.SSHPassword) 142 } else { 143 var SSHSigner ssh.Signer 144 s.cfg.SSHKeyBytes, err = ioutil.ReadFile(*s.cfg.SSHKeyFile) 145 if err != nil { 146 err = fmt.Errorf("read key file ERR: %s", err) 147 return 148 } 149 if *s.cfg.SSHKeyFileSalt != "" { 150 SSHSigner, err = ssh.ParsePrivateKeyWithPassphrase(s.cfg.SSHKeyBytes, []byte(*s.cfg.SSHKeyFileSalt)) 151 } else { 152 SSHSigner, err = ssh.ParsePrivateKey(s.cfg.SSHKeyBytes) 153 } 154 if err != nil { 155 err = fmt.Errorf("parse ssh private key fail,ERR: %s", err) 156 return 157 } 158 s.cfg.SSHAuthMethod = ssh.PublicKeys(SSHSigner) 159 } 160 } 161 if *s.cfg.RateLimit != "0" && *s.cfg.RateLimit != "" { 162 var size uint64 163 size, err = datasize.Parse(*s.cfg.RateLimit) 164 if err != nil { 165 err = fmt.Errorf("parse rate limit size error,ERR:%s", err) 166 return 167 } 168 s.cfg.RateLimitBytes = float64(size) 169 } 170 if *s.cfg.Jumper != "" { 171 if *s.cfg.ParentType != "tls" && *s.cfg.ParentType != "tcp" { 172 err = fmt.Errorf("jumper only worked of -T is tls or tcp") 173 return 174 } 175 var j jumper.Jumper 176 j, err = jumper.New(*s.cfg.Jumper, time.Millisecond*time.Duration(*s.cfg.Timeout)) 177 if err != nil { 178 err = fmt.Errorf("parse jumper fail, err %s", err) 179 return 180 } 181 s.jumper = &j 182 } 183 return 184 } 185 func (s *HTTP) InitService() (err error) { 186 s.InitBasicAuth() 187 //init lb 188 if len(*s.cfg.Parent) > 0 { 189 s.checker = utils.NewChecker(*s.cfg.HTTPTimeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct, s.log, *s.cfg.Intelligent) 190 s.InitLB() 191 } 192 if *s.cfg.DNSAddress != "" { 193 s.domainResolver = dnsx.NewDomainResolver(*s.cfg.DNSAddress, *s.cfg.DNSTTL, s.log) 194 } 195 if *s.cfg.ParentType == "ssh" { 196 err = s.ConnectSSH() 197 if err != nil { 198 err = fmt.Errorf("init service fail, ERR: %s", err) 199 return 200 } 201 go func() { 202 defer func() { 203 if e := recover(); e != nil { 204 fmt.Printf("crashed, err: %s\nstack:%s", e, string(debug.Stack())) 205 } 206 }() 207 //循环检查ssh网络连通性 208 for { 209 if s.isStop { 210 return 211 } 212 conn, err := utils.ConnectHost(s.Resolve(s.lb.Select("", *s.cfg.LoadBalanceOnlyHA)), *s.cfg.Timeout*2) 213 if err == nil && conn != nil { 214 conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) 215 _, err = conn.Write([]byte{0}) 216 conn.SetDeadline(time.Time{}) 217 } 218 if err != nil { 219 if s.sshClient != nil { 220 s.sshClient.Close() 221 if s.sshClient.Conn != nil { 222 s.sshClient.Conn.Close() 223 } 224 } 225 s.log.Printf("ssh offline, retrying...") 226 s.ConnectSSH() 227 } else { 228 conn.Close() 229 } 230 time.Sleep(time.Second * 3) 231 } 232 }() 233 } 234 return 235 } 236 func (s *HTTP) StopService() { 237 defer func() { 238 e := recover() 239 if e != nil { 240 s.log.Printf("stop http(s) service crashed,%s", e) 241 } else { 242 s.log.Printf("service http(s) stopped") 243 } 244 s.basicAuth = utils.BasicAuth{} 245 s.cfg = HTTPArgs{} 246 s.checker = utils.Checker{} 247 s.domainResolver = dnsx.DomainResolver{} 248 s.lb = nil 249 s.lockChn = nil 250 s.log = nil 251 s.jumper = nil 252 s.serverChannels = nil 253 s.sshClient = nil 254 s.userConns = nil 255 s = nil 256 }() 257 s.isStop = true 258 if len(*s.cfg.Parent) > 0 { 259 s.checker.Stop() 260 } 261 if s.sshClient != nil { 262 s.sshClient.Close() 263 } 264 for _, sc := range s.serverChannels { 265 if sc.Listener != nil && *sc.Listener != nil { 266 (*sc.Listener).Close() 267 } 268 if sc.UDPListener != nil { 269 (*sc.UDPListener).Close() 270 } 271 } 272 if s.lb != nil { 273 s.lb.Stop() 274 } 275 } 276 func (s *HTTP) Start(args interface{}, log *logger.Logger) (err error) { 277 s.log = log 278 s.cfg = args.(HTTPArgs) 279 if err = s.CheckArgs(); err != nil { 280 return 281 } 282 283 if err = s.InitService(); err != nil { 284 return 285 } 286 287 if len(*s.cfg.Parent) > 0 { 288 s.log.Printf("use %s parent %v [ %s ]", *s.cfg.ParentType, *s.cfg.Parent, strings.ToUpper(*s.cfg.LoadBalanceMethod)) 289 } 290 291 for _, addr := range strings.Split(*s.cfg.Local, ",") { 292 if addr != "" { 293 host, port, _ := net.SplitHostPort(addr) 294 p, _ := strconv.Atoi(port) 295 sc := server.NewServerChannel(host, p, s.log) 296 if *s.cfg.LocalType == "tcp" { 297 err = sc.ListenTCP(s.callback) 298 } else if *s.cfg.LocalType == "tls" { 299 err = sc.ListenTLS(s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes, s.callback) 300 } else if *s.cfg.LocalType == "kcp" { 301 err = sc.ListenKCP(s.cfg.KCP, s.callback, s.log) 302 } 303 if err != nil { 304 return 305 } 306 s.log.Printf("%s http(s) proxy on %s", *s.cfg.LocalType, (*sc.Listener).Addr()) 307 s.serverChannels = append(s.serverChannels, &sc) 308 } 309 } 310 return 311 } 312 313 func (s *HTTP) Clean() { 314 s.StopService() 315 } 316 func (s *HTTP) callback(inConn net.Conn) { 317 defer func() { 318 if err := recover(); err != nil { 319 s.log.Printf("http(s) conn handler crashed with err : %s \nstack: %s", err, string(debug.Stack())) 320 } 321 }() 322 if *s.cfg.LocalCompress { 323 inConn = utils.NewCompConn(inConn) 324 } 325 if *s.cfg.LocalKey != "" { 326 inConn = conncrypt.New(inConn, &conncrypt.Config{ 327 Password: *s.cfg.LocalKey, 328 }) 329 } 330 var err interface{} 331 var req utils.HTTPRequest 332 req, err = utils.NewHTTPRequest(&inConn, 4096, s.IsBasicAuth(), &s.basicAuth, s.log) 333 if err != nil { 334 if err != io.EOF { 335 s.log.Printf("decoder error , from %s, ERR:%s", inConn.RemoteAddr(), err) 336 } 337 utils.CloseConn(&inConn) 338 return 339 } 340 address := req.Host 341 host, _, _ := net.SplitHostPort(address) 342 useProxy := false 343 if !utils.IsInternalIP(host, *s.cfg.Always) { 344 useProxy = true 345 if len(*s.cfg.Parent) == 0 { 346 useProxy = false 347 } else if *s.cfg.Always { 348 useProxy = true 349 } else { 350 var isInMap bool 351 useProxy, isInMap, _, _ = s.checker.IsBlocked(address) 352 if !isInMap { 353 s.checker.Add(address, s.Resolve(address)) 354 } 355 //s.log.Printf("blocked ? : %v, %s , fail:%d ,success:%d", useProxy, address, n, m) 356 } 357 } 358 359 s.log.Printf("use proxy : %v, %s", useProxy, address) 360 361 lbAddr, err := s.OutToTCP(useProxy, address, &inConn, &req) 362 if err != nil { 363 if len(*s.cfg.Parent) == 0 { 364 s.log.Printf("connect to %s fail, ERR:%s", address, err) 365 } else { 366 s.log.Printf("connect to %s parent %v fail", *s.cfg.ParentType, lbAddr) 367 } 368 utils.CloseConn(&inConn) 369 } 370 } 371 func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *utils.HTTPRequest) (lbAddr string, err interface{}) { 372 inAddr := (*inConn).RemoteAddr().String() 373 inLocalAddr := (*inConn).LocalAddr().String() 374 //防止死循环 375 if s.IsDeadLoop(inLocalAddr, req.Host) { 376 utils.CloseConn(inConn) 377 err = fmt.Errorf("dead loop detected , %s", req.Host) 378 return 379 } 380 var outConn net.Conn 381 tryCount := 0 382 maxTryCount := 5 383 for { 384 if s.isStop { 385 return 386 } 387 if useProxy { 388 // s.log.Printf("%v", s.outPool) 389 if *s.cfg.ParentType == "ssh" { 390 outConn, err = s.getSSHConn(address) 391 } else { 392 selectAddr := (*inConn).RemoteAddr().String() 393 if utils.LBMethod(*s.cfg.LoadBalanceMethod) == lb.SELECT_HASH && *s.cfg.LoadBalanceHashTarget { 394 selectAddr = address 395 } 396 lbAddr = s.lb.Select(selectAddr, *s.cfg.LoadBalanceOnlyHA) 397 outConn, err = s.GetParentConn(lbAddr) 398 } 399 400 } else { 401 outConn, err = s.GetDirectConn(s.Resolve(address), inLocalAddr) 402 } 403 tryCount++ 404 if err == nil || tryCount > maxTryCount { 405 break 406 } else { 407 s.log.Printf("connect to %s , err:%s,retrying...", lbAddr, err) 408 time.Sleep(time.Second * 2) 409 } 410 } 411 if err != nil { 412 s.log.Printf("connect to %s , err:%s", lbAddr, err) 413 utils.CloseConn(inConn) 414 return 415 } 416 if *s.cfg.ParentCompress { 417 outConn = utils.NewCompConn(outConn) 418 } 419 if useProxy && *s.cfg.ParentKey != "" { 420 outConn = conncrypt.New(outConn, &conncrypt.Config{ 421 Password: *s.cfg.ParentKey, 422 }) 423 } 424 425 outAddr := outConn.RemoteAddr().String() 426 //outLocalAddr := outConn.LocalAddr().String() 427 if req.IsHTTPS() && (!useProxy || *s.cfg.ParentType == "ssh") { 428 //https无上级或者上级非代理,proxy需要响应connect请求,并直连目标 429 err = req.HTTPSReply() 430 } else { 431 //https或者http,上级是代理,proxy需要转发 432 outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) 433 //直连目标或上级非代理或非SNI,,清理HTTP头部的代理头信息 434 if (!useProxy || *s.cfg.ParentType == "ssh") && !req.IsSNI { 435 _, err = outConn.Write(utils.RemoveProxyHeaders(req.HeadBuf)) 436 } else { 437 _, err = outConn.Write(req.HeadBuf) 438 } 439 outConn.SetDeadline(time.Time{}) 440 if err != nil { 441 s.log.Printf("write to %s , err:%s", lbAddr, err) 442 utils.CloseConn(inConn) 443 return 444 } 445 } 446 447 if s.cfg.RateLimitBytes > 0 { 448 outConn = iolimiter.NewReaderConn(outConn, s.cfg.RateLimitBytes) 449 } 450 451 utils.IoBind((*inConn), outConn, func(err interface{}) { 452 s.log.Printf("conn %s - %s released [%s]", inAddr, outAddr, req.Host) 453 s.userConns.Remove(inAddr) 454 if len(*s.cfg.Parent) > 0 { 455 s.lb.DecreaseConns(lbAddr) 456 } 457 }, s.log) 458 s.log.Printf("conn %s - %s connected [%s]", inAddr, outAddr, req.Host) 459 if c, ok := s.userConns.Get(inAddr); ok { 460 (*c.(*net.Conn)).Close() 461 } 462 s.userConns.Set(inAddr, inConn) 463 if len(*s.cfg.Parent) > 0 { 464 s.lb.IncreasConns(lbAddr) 465 } 466 return 467 } 468 469 func (s *HTTP) getSSHConn(host string) (outConn net.Conn, err interface{}) { 470 maxTryCount := 1 471 tryCount := 0 472 RETRY: 473 if tryCount >= maxTryCount || s.isStop { 474 return 475 } 476 wait := make(chan bool, 1) 477 go func() { 478 defer func() { 479 if err == nil { 480 err = recover() 481 } 482 wait <- true 483 }() 484 outConn, err = s.sshClient.Dial("tcp", host) 485 }() 486 select { 487 case <-wait: 488 case <-time.After(time.Second * 5): 489 err = fmt.Errorf("ssh dial %s timeout", host) 490 } 491 if err != nil { 492 s.log.Printf("connect ssh fail, ERR: %s, retrying...", err) 493 e := s.ConnectSSH() 494 if e == nil { 495 tryCount++ 496 time.Sleep(time.Second * 3) 497 goto RETRY 498 } else { 499 err = e 500 } 501 } 502 return 503 } 504 func (s *HTTP) ConnectSSH() (err error) { 505 select { 506 case s.lockChn <- true: 507 default: 508 err = fmt.Errorf("can not connect at same time") 509 return 510 } 511 config := ssh.ClientConfig{ 512 Timeout: time.Duration(*s.cfg.Timeout) * time.Millisecond, 513 User: *s.cfg.SSHUser, 514 Auth: []ssh.AuthMethod{s.cfg.SSHAuthMethod}, 515 HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { 516 return nil 517 }, 518 } 519 if s.sshClient != nil { 520 s.sshClient.Close() 521 } 522 s.sshClient, err = ssh.Dial("tcp", s.Resolve(s.lb.Select("", *s.cfg.LoadBalanceOnlyHA)), &config) 523 if err != nil { 524 s.log.Printf("connect to ssh %s fail", s.cfg.Parent) 525 } 526 <-s.lockChn 527 return 528 } 529 530 func (s *HTTP) InitBasicAuth() (err error) { 531 if *s.cfg.DNSAddress != "" { 532 s.basicAuth = utils.NewBasicAuth(&(*s).domainResolver, s.log) 533 } else { 534 s.basicAuth = utils.NewBasicAuth(nil, s.log) 535 } 536 if *s.cfg.AuthURL != "" { 537 s.basicAuth.SetAuthURL(*s.cfg.AuthURL, *s.cfg.AuthURLOkCode, *s.cfg.AuthURLTimeout, *s.cfg.AuthURLRetry) 538 s.log.Printf("auth from %s", *s.cfg.AuthURL) 539 } 540 if *s.cfg.AuthFile != "" { 541 var n = 0 542 n, err = s.basicAuth.AddFromFile(*s.cfg.AuthFile) 543 if err != nil { 544 err = fmt.Errorf("auth-file ERR:%s", err) 545 return 546 } 547 s.log.Printf("auth data added from file %d , total:%d", n, s.basicAuth.Total()) 548 } 549 if len(*s.cfg.Auth) > 0 { 550 n := s.basicAuth.Add(*s.cfg.Auth) 551 s.log.Printf("auth data added %d, total:%d", n, s.basicAuth.Total()) 552 } 553 return 554 } 555 func (s *HTTP) InitLB() { 556 configs := lb.BackendsConfig{} 557 for _, addr := range *s.cfg.Parent { 558 _addrInfo := strings.Split(addr, "@") 559 _addr := _addrInfo[0] 560 weight := 1 561 if len(_addrInfo) == 2 { 562 weight, _ = strconv.Atoi(_addrInfo[1]) 563 } 564 configs = append(configs, &lb.BackendConfig{ 565 Address: _addr, 566 Weight: weight, 567 ActiveAfter: 1, 568 InactiveAfter: 2, 569 Timeout: time.Duration(*s.cfg.LoadBalanceTimeout) * time.Millisecond, 570 RetryTime: time.Duration(*s.cfg.LoadBalanceRetryTime) * time.Millisecond, 571 }) 572 } 573 LB := lb.NewGroup(utils.LBMethod(*s.cfg.LoadBalanceMethod), configs, &s.domainResolver, s.log, *s.cfg.Debug) 574 s.lb = &LB 575 } 576 func (s *HTTP) IsBasicAuth() bool { 577 return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0 || *s.cfg.AuthURL != "" 578 } 579 func (s *HTTP) IsDeadLoop(inLocalAddr string, host string) bool { 580 inIP, inPort, err := net.SplitHostPort(inLocalAddr) 581 if err != nil { 582 return false 583 } 584 outDomain, outPort, err := net.SplitHostPort(host) 585 if err != nil { 586 return false 587 } 588 if inPort == outPort { 589 var outIPs []net.IP 590 if *s.cfg.DNSAddress != "" { 591 outIPs = []net.IP{net.ParseIP(s.Resolve(outDomain))} 592 } else { 593 outIPs, err = utils.LookupIP(outDomain) 594 } 595 if err == nil { 596 for _, ip := range outIPs { 597 if ip.String() == inIP { 598 return true 599 } 600 } 601 } 602 interfaceIPs, err := utils.GetAllInterfaceAddr() 603 for _, ip := range *s.cfg.LocalIPS { 604 interfaceIPs = append(interfaceIPs, net.ParseIP(ip).To4()) 605 } 606 if err == nil { 607 for _, localIP := range interfaceIPs { 608 for _, outIP := range outIPs { 609 if localIP.Equal(outIP) { 610 return true 611 } 612 } 613 } 614 } 615 } 616 return false 617 } 618 func (s *HTTP) Resolve(address string) string { 619 if *s.cfg.DNSAddress == "" { 620 return address 621 } 622 ip, err := s.domainResolver.Resolve(address) 623 if err != nil { 624 s.log.Printf("dns error %s , ERR:%s", address, err) 625 return address 626 } 627 return ip 628 } 629 func (s *HTTP) GetParentConn(address string) (conn net.Conn, err error) { 630 if *s.cfg.ParentType == "tls" { 631 if s.jumper == nil { 632 var _conn tls.Conn 633 _conn, err = utils.TlsConnectHost(address, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes) 634 if err == nil { 635 conn = net.Conn(&_conn) 636 } 637 } else { 638 conf, err := utils.TlsConfig(s.cfg.CertBytes, s.cfg.KeyBytes, s.cfg.CaCertBytes) 639 if err != nil { 640 return nil, err 641 } 642 var _c net.Conn 643 _c, err = s.jumper.Dial(address, time.Millisecond*time.Duration(*s.cfg.Timeout)) 644 if err == nil { 645 conn = net.Conn(tls.Client(_c, conf)) 646 } 647 } 648 649 } else if *s.cfg.ParentType == "kcp" { 650 conn, err = utils.ConnectKCPHost(address, s.cfg.KCP) 651 } else if *s.cfg.ParentType == "ssh" { 652 var e interface{} 653 conn, e = s.getSSHConn(address) 654 if e != nil { 655 err = fmt.Errorf("%s", e) 656 } 657 } else { 658 if s.jumper == nil { 659 conn, err = utils.ConnectHost(address, *s.cfg.Timeout) 660 } else { 661 conn, err = s.jumper.Dial(address, time.Millisecond*time.Duration(*s.cfg.Timeout)) 662 } 663 } 664 return 665 } 666 func (s *HTTP) GetDirectConn(address string, localAddr string) (conn net.Conn, err error) { 667 if !*s.cfg.BindListen { 668 return utils.ConnectHost(address, *s.cfg.Timeout) 669 } 670 ip, _, _ := net.SplitHostPort(localAddr) 671 if utils.IsInternalIP(ip, false) { 672 return utils.ConnectHost(address, *s.cfg.Timeout) 673 } 674 local, _ := net.ResolveTCPAddr("tcp", ip+":0") 675 d := net.Dialer{ 676 Timeout: time.Millisecond * time.Duration(*s.cfg.Timeout), 677 LocalAddr: local, 678 } 679 conn, err = d.Dial("tcp", address) 680 return 681 }