github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/netx/proxy.go (about) 1 package netx 2 3 import ( 4 "bufio" 5 "net" 6 "net/http" 7 "net/url" 8 9 "github.com/juju/errors" 10 "golang.org/x/net/proxy" 11 ) 12 13 // NewProxyDialer based on proxy url. 14 func NewProxyDialer(proxyUrl string) (proxy.Dialer, error) { 15 u, err := url.Parse(proxyUrl) 16 if err != nil { 17 return nil, err 18 } 19 20 switch u.Scheme { 21 case "socks5": 22 return &socks5ProxyClient{proxyUrl: u}, nil 23 case "http": 24 return &httpProxyClient{proxyUrl: u}, nil 25 default: 26 return &defaultProxyClient{}, nil 27 } 28 } 29 30 // defaultProxyClient is used to implement a proxy in default. 31 type defaultProxyClient struct { 32 rAddr *net.TCPAddr 33 } 34 35 // Dial implementation of ProxyConn. 36 // Set KeepAlive=-1 to reduce the call of syscall. 37 func (dc *defaultProxyClient) Dial(network string, address string) (conn net.Conn, err error) { 38 if dc.rAddr == nil { 39 if dc.rAddr, err = net.ResolveTCPAddr("tcp", address); err != nil { 40 return nil, err 41 } 42 } 43 return net.DialTCP(network, nil, dc.rAddr) 44 } 45 46 // socks5ProxyClient is used to implement a proxy in socks5 47 type socks5ProxyClient struct { 48 proxyUrl *url.URL 49 } 50 51 // Dial implementation of ProxyConn. 52 func (s5 *socks5ProxyClient) Dial(network string, address string) (net.Conn, error) { 53 d, err := proxy.FromURL(s5.proxyUrl, nil) 54 if err != nil { 55 return nil, err 56 } 57 58 return d.Dial(network, address) 59 } 60 61 // httpProxyClient is used to implement a proxy in http. 62 type httpProxyClient struct { 63 proxyUrl *url.URL 64 } 65 66 // Dial implementation of ProxyConn 67 func (hc *httpProxyClient) Dial(_ string, address string) (net.Conn, error) { 68 req, err := http.NewRequest("CONNECT", "http://"+address, nil) 69 if err != nil { 70 return nil, err 71 } 72 password, _ := hc.proxyUrl.User.Password() 73 req.SetBasicAuth(hc.proxyUrl.User.Username(), password) 74 proxyConn, err := net.Dial("tcp", hc.proxyUrl.Host) 75 if err != nil { 76 return nil, err 77 } 78 if err := req.Write(proxyConn); err != nil { 79 return nil, err 80 } 81 res, err := http.ReadResponse(bufio.NewReader(proxyConn), req) 82 if err != nil { 83 return nil, err 84 } 85 _ = res.Body.Close() 86 if res.StatusCode != 200 { 87 return nil, errors.New("Proxy error " + res.Status) 88 } 89 return proxyConn, nil 90 }