github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/net/proxy/socks5.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package proxy 6 7 import ( 8 "errors" 9 "io" 10 "net" 11 "strconv" 12 ) 13 14 // SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address 15 // with an optional username and password. See RFC 1928. 16 func SOCKS5(network, addr string, auth *Auth, forward Dialer) (Dialer, error) { 17 s := &socks5{ 18 network: network, 19 addr: addr, 20 forward: forward, 21 } 22 if auth != nil { 23 s.user = auth.User 24 s.password = auth.Password 25 } 26 27 return s, nil 28 } 29 30 type socks5 struct { 31 user, password string 32 network, addr string 33 forward Dialer 34 } 35 36 const socks5Version = 5 37 38 const ( 39 socks5AuthNone = 0 40 socks5AuthPassword = 2 41 ) 42 43 const socks5Connect = 1 44 45 const ( 46 socks5IP4 = 1 47 socks5Domain = 3 48 socks5IP6 = 4 49 ) 50 51 var socks5Errors = []string{ 52 "", 53 "general failure", 54 "connection forbidden", 55 "network unreachable", 56 "host unreachable", 57 "connection refused", 58 "TTL expired", 59 "command not supported", 60 "address type not supported", 61 } 62 63 // Dial connects to the address addr on the network net via the SOCKS5 proxy. 64 func (s *socks5) Dial(network, addr string) (net.Conn, error) { 65 switch network { 66 case "tcp", "tcp6", "tcp4": 67 default: 68 return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network) 69 } 70 71 conn, err := s.forward.Dial(s.network, s.addr) 72 if err != nil { 73 return nil, err 74 } 75 closeConn := &conn 76 defer func() { 77 if closeConn != nil { 78 (*closeConn).Close() 79 } 80 }() 81 82 host, portStr, err := net.SplitHostPort(addr) 83 if err != nil { 84 return nil, err 85 } 86 87 port, err := strconv.Atoi(portStr) 88 if err != nil { 89 return nil, errors.New("proxy: failed to parse port number: " + portStr) 90 } 91 if port < 1 || port > 0xffff { 92 return nil, errors.New("proxy: port number out of range: " + portStr) 93 } 94 95 // the size here is just an estimate 96 buf := make([]byte, 0, 6+len(host)) 97 98 buf = append(buf, socks5Version) 99 if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 { 100 buf = append(buf, 2 /* num auth methods */, socks5AuthNone, socks5AuthPassword) 101 } else { 102 buf = append(buf, 1 /* num auth methods */, socks5AuthNone) 103 } 104 105 if _, err := conn.Write(buf); err != nil { 106 return nil, errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error()) 107 } 108 109 if _, err := io.ReadFull(conn, buf[:2]); err != nil { 110 return nil, errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error()) 111 } 112 if buf[0] != 5 { 113 return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0]))) 114 } 115 if buf[1] == 0xff { 116 return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication") 117 } 118 119 if buf[1] == socks5AuthPassword { 120 buf = buf[:0] 121 buf = append(buf, 1 /* password protocol version */) 122 buf = append(buf, uint8(len(s.user))) 123 buf = append(buf, s.user...) 124 buf = append(buf, uint8(len(s.password))) 125 buf = append(buf, s.password...) 126 127 if _, err := conn.Write(buf); err != nil { 128 return nil, errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) 129 } 130 131 if _, err := io.ReadFull(conn, buf[:2]); err != nil { 132 return nil, errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) 133 } 134 135 if buf[1] != 0 { 136 return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password") 137 } 138 } 139 140 buf = buf[:0] 141 buf = append(buf, socks5Version, socks5Connect, 0 /* reserved */) 142 143 if ip := net.ParseIP(host); ip != nil { 144 if ip4 := ip.To4(); ip4 != nil { 145 buf = append(buf, socks5IP4) 146 ip = ip4 147 } else { 148 buf = append(buf, socks5IP6) 149 } 150 buf = append(buf, ip...) 151 } else { 152 if len(host) > 255 { 153 return nil, errors.New("proxy: destination hostname too long: " + host) 154 } 155 buf = append(buf, socks5Domain) 156 buf = append(buf, byte(len(host))) 157 buf = append(buf, host...) 158 } 159 buf = append(buf, byte(port>>8), byte(port)) 160 161 if _, err := conn.Write(buf); err != nil { 162 return nil, errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) 163 } 164 165 if _, err := io.ReadFull(conn, buf[:4]); err != nil { 166 return nil, errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) 167 } 168 169 failure := "unknown error" 170 if int(buf[1]) < len(socks5Errors) { 171 failure = socks5Errors[buf[1]] 172 } 173 174 if len(failure) > 0 { 175 return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure) 176 } 177 178 bytesToDiscard := 0 179 switch buf[3] { 180 case socks5IP4: 181 bytesToDiscard = net.IPv4len 182 case socks5IP6: 183 bytesToDiscard = net.IPv6len 184 case socks5Domain: 185 _, err := io.ReadFull(conn, buf[:1]) 186 if err != nil { 187 return nil, errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error()) 188 } 189 bytesToDiscard = int(buf[0]) 190 default: 191 return nil, errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr) 192 } 193 194 if cap(buf) < bytesToDiscard { 195 buf = make([]byte, bytesToDiscard) 196 } else { 197 buf = buf[:bytesToDiscard] 198 } 199 if _, err := io.ReadFull(conn, buf); err != nil { 200 return nil, errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error()) 201 } 202 203 // Also need to discard the port number 204 if _, err := io.ReadFull(conn, buf[:2]); err != nil { 205 return nil, errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error()) 206 } 207 208 closeConn = nil 209 return conn, nil 210 }