github.com/AntonOrnatskyi/goproxy@v0.0.0-20190205095733-4526a9fa18b4/utils/jumper/jumper.go (about) 1 package jumper 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "fmt" 7 "net" 8 "net/url" 9 "time" 10 11 "golang.org/x/net/proxy" 12 ) 13 14 type Jumper struct { 15 proxyURL *url.URL 16 timeout time.Duration 17 } 18 type socks5Dialer struct { 19 timeout time.Duration 20 } 21 22 func (s socks5Dialer) Dial(network, addr string) (net.Conn, error) { 23 return net.DialTimeout(network, addr, s.timeout) 24 } 25 26 func New(proxyURL string, timeout time.Duration) (j Jumper, err error) { 27 u, e := url.Parse(proxyURL) 28 if e != nil { 29 err = e 30 return 31 } 32 j = Jumper{ 33 proxyURL: u, 34 timeout: timeout, 35 } 36 return 37 } 38 func (j *Jumper) Dial(address string, timeout time.Duration) (net.Conn, error) { 39 switch j.proxyURL.Scheme { 40 case "https": 41 return j.dialHTTPS(address, timeout) 42 case "socks5": 43 return j.dialSOCKS5(address, timeout) 44 default: 45 return nil, fmt.Errorf("unkown scheme of %s", j.proxyURL.String()) 46 } 47 } 48 func (j *Jumper) dialHTTPS(address string, timeout time.Duration) (conn net.Conn, err error) { 49 conn, err = net.DialTimeout("tcp", j.proxyURL.Host, timeout) 50 if err != nil { 51 return 52 } 53 pb := new(bytes.Buffer) 54 pb.Write([]byte(fmt.Sprintf("CONNECT %s HTTP/1.1\r\n", address))) 55 pb.WriteString(fmt.Sprintf("Host: %s\r\n", address)) 56 pb.WriteString(fmt.Sprintf("Proxy-Host: %s\r\n", address)) 57 pb.WriteString("Proxy-Connection: Keep-Alive\r\n") 58 pb.WriteString("Connection: Keep-Alive\r\n") 59 if j.proxyURL.User != nil { 60 p, _ := j.proxyURL.User.Password() 61 u := fmt.Sprintf("%s:%s", j.proxyURL.User.Username(), p) 62 pb.Write([]byte(fmt.Sprintf("Proxy-Authorization: Basic %s\r\n", base64.StdEncoding.EncodeToString([]byte(u))))) 63 } 64 pb.Write([]byte("\r\n")) 65 _, err = conn.Write(pb.Bytes()) 66 if err != nil { 67 conn.Close() 68 conn = nil 69 err = fmt.Errorf("error connecting to proxy: %s", err) 70 return 71 } 72 reply := make([]byte, 1024) 73 conn.SetDeadline(time.Now().Add(timeout)) 74 n, e := conn.Read(reply) 75 conn.SetDeadline(time.Time{}) 76 if e != nil { 77 err = fmt.Errorf("error read reply from proxy: %s", e) 78 conn.Close() 79 conn = nil 80 return 81 } 82 if bytes.Index(reply[:n], []byte("200")) == -1 { 83 err = fmt.Errorf("error greeting to proxy, response: %s", string(reply[:n])) 84 conn.Close() 85 conn = nil 86 return 87 } 88 return 89 } 90 func (j *Jumper) dialSOCKS5(address string, timeout time.Duration) (conn net.Conn, err error) { 91 auth := &proxy.Auth{} 92 if j.proxyURL.User != nil { 93 auth.User = j.proxyURL.User.Username() 94 auth.Password, _ = j.proxyURL.User.Password() 95 } else { 96 auth = nil 97 } 98 dialSocksProxy, e := proxy.SOCKS5("tcp", j.proxyURL.Host, auth, socks5Dialer{timeout: timeout}) 99 if e != nil { 100 err = fmt.Errorf("error connecting to proxy: %s", e) 101 return 102 } 103 return dialSocksProxy.Dial("tcp", address) 104 }