github.com/AntonOrnatskyi/goproxy@v0.0.0-20190205095733-4526a9fa18b4/utils/socks/client.go (about) 1 package socks 2 3 import ( 4 "encoding/binary" 5 "errors" 6 "fmt" 7 "io" 8 "net" 9 "strconv" 10 "time" 11 ) 12 13 var socks5Errors = []string{ 14 "", 15 "general failure", 16 "connection forbidden", 17 "network unreachable", 18 "host unreachable", 19 "connection refused", 20 "TTL expired", 21 "command not supported", 22 "address type not supported", 23 } 24 25 type Auth struct { 26 User, Password string 27 } 28 type ClientConn struct { 29 user string 30 password string 31 conn *net.Conn 32 header []byte 33 timeout time.Duration 34 addr string 35 network string 36 UDPAddr string 37 } 38 39 // SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address 40 // with an optional username and password. See RFC 1928 and RFC 1929. 41 // target must be a canonical address with a host and port. 42 // network : tcp udp 43 func NewClientConn(conn *net.Conn, network, target string, timeout time.Duration, auth *Auth, header []byte) *ClientConn { 44 s := &ClientConn{ 45 conn: conn, 46 network: network, 47 timeout: timeout, 48 } 49 if auth != nil { 50 s.user = auth.User 51 s.password = auth.Password 52 } 53 if header != nil && len(header) > 0 { 54 s.header = header 55 } 56 if network == "udp" && target == "" { 57 target = "0.0.0.0:0" 58 } 59 s.addr = target 60 return s 61 } 62 63 // connect takes an existing connection to a socks5 proxy server, 64 // and commands the server to extend that connection to target, 65 // which must be a canonical address with a host and port. 66 func (s *ClientConn) Handshake() error { 67 host, portStr, err := net.SplitHostPort(s.addr) 68 if err != nil { 69 return err 70 } 71 port, err := strconv.Atoi(portStr) 72 if err != nil { 73 return errors.New("proxy: failed to parse port number: " + portStr) 74 } 75 if s.network == "tcp" && (port < 1 || port > 0xffff) { 76 return errors.New("proxy: port number out of range: " + portStr) 77 } 78 79 if err := s.handshake(host); err != nil { 80 return err 81 } 82 buf := []byte{} 83 if s.network == "tcp" { 84 buf = append(buf, VERSION_V5, CMD_CONNECT, 0 /* reserved */) 85 86 } else { 87 buf = append(buf, VERSION_V5, CMD_ASSOCIATE, 0 /* reserved */) 88 } 89 if ip := net.ParseIP(host); ip != nil { 90 if ip4 := ip.To4(); ip4 != nil { 91 buf = append(buf, ATYP_IPV4) 92 ip = ip4 93 } else { 94 buf = append(buf, ATYP_IPV6) 95 } 96 buf = append(buf, ip...) 97 } else { 98 if len(host) > 255 { 99 return errors.New("proxy: destination host name too long: " + host) 100 } 101 buf = append(buf, ATYP_DOMAIN) 102 buf = append(buf, byte(len(host))) 103 buf = append(buf, host...) 104 } 105 buf = append(buf, byte(port>>8), byte(port)) 106 (*s.conn).SetDeadline(time.Now().Add(s.timeout)) 107 if _, err := (*s.conn).Write(buf); err != nil { 108 return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) 109 } 110 (*s.conn).SetDeadline(time.Time{}) 111 (*s.conn).SetDeadline(time.Now().Add(s.timeout)) 112 if _, err := io.ReadFull((*s.conn), buf[:4]); err != nil { 113 return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) 114 } 115 (*s.conn).SetDeadline(time.Time{}) 116 failure := "unknown error" 117 if int(buf[1]) < len(socks5Errors) { 118 failure = socks5Errors[buf[1]] 119 } 120 121 if len(failure) > 0 { 122 return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure) 123 } 124 125 bytesToDiscard := 0 126 switch buf[3] { 127 case ATYP_IPV4: 128 bytesToDiscard = net.IPv4len 129 case ATYP_IPV6: 130 bytesToDiscard = net.IPv6len 131 case ATYP_DOMAIN: 132 (*s.conn).SetDeadline(time.Now().Add(s.timeout)) 133 _, err := io.ReadFull((*s.conn), buf[:1]) 134 (*s.conn).SetDeadline(time.Time{}) 135 if err != nil { 136 return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error()) 137 } 138 bytesToDiscard = int(buf[0]) 139 default: 140 return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr) 141 } 142 143 if cap(buf) < bytesToDiscard { 144 buf = make([]byte, bytesToDiscard) 145 } else { 146 buf = buf[:bytesToDiscard] 147 } 148 (*s.conn).SetDeadline(time.Now().Add(s.timeout)) 149 if _, err := io.ReadFull((*s.conn), buf); err != nil { 150 return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error()) 151 } 152 (*s.conn).SetDeadline(time.Time{}) 153 var ip net.IP 154 ip = buf 155 ipStr := "" 156 if bytesToDiscard == net.IPv4len || bytesToDiscard == net.IPv6len { 157 if ipv4 := ip.To4(); ipv4 != nil { 158 ipStr = ipv4.String() 159 } else { 160 ipStr = ip.To16().String() 161 } 162 } 163 //log.Printf("%v", ipStr) 164 // Also need to discard the port number 165 (*s.conn).SetDeadline(time.Now().Add(s.timeout)) 166 if _, err := io.ReadFull((*s.conn), buf[:2]); err != nil { 167 return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error()) 168 } 169 p := binary.BigEndian.Uint16([]byte{buf[0], buf[1]}) 170 //log.Printf("%v", p) 171 s.UDPAddr = net.JoinHostPort(ipStr, fmt.Sprintf("%d", p)) 172 //log.Printf("%v", s.udpAddr) 173 (*s.conn).SetDeadline(time.Time{}) 174 return nil 175 } 176 func (s *ClientConn) SendUDP(data []byte, addr string) (respData []byte, err error) { 177 178 c, err := net.DialTimeout("udp", s.UDPAddr, s.timeout) 179 if err != nil { 180 return 181 } 182 conn := c.(*net.UDPConn) 183 184 p := NewPacketUDP() 185 p.Build(addr, data) 186 conn.SetDeadline(time.Now().Add(s.timeout)) 187 conn.Write(p.Bytes()) 188 conn.SetDeadline(time.Time{}) 189 190 buf := make([]byte, 1024) 191 conn.SetDeadline(time.Now().Add(s.timeout)) 192 n, _, err := conn.ReadFrom(buf) 193 conn.SetDeadline(time.Time{}) 194 if err != nil { 195 return 196 } 197 respData = buf[:n] 198 return 199 } 200 func (s *ClientConn) handshake(host string) error { 201 202 // the size here is just an estimate 203 buf := make([]byte, 0, 6+len(host)) 204 205 buf = append(buf, VERSION_V5) 206 if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 { 207 buf = append(buf, 2 /* num auth methods */, Method_NO_AUTH, Method_USER_PASS) 208 } else { 209 buf = append(buf, 1 /* num auth methods */, Method_NO_AUTH) 210 } 211 (*s.conn).SetDeadline(time.Now().Add(s.timeout)) 212 if _, err := (*s.conn).Write(buf); err != nil { 213 return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error()) 214 } 215 (*s.conn).SetDeadline(time.Time{}) 216 217 (*s.conn).SetDeadline(time.Now().Add(s.timeout)) 218 if _, err := io.ReadFull((*s.conn), buf[:2]); err != nil { 219 return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error()) 220 } 221 (*s.conn).SetDeadline(time.Time{}) 222 223 if buf[0] != 5 { 224 return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0]))) 225 } 226 if buf[1] == 0xff { 227 return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication") 228 } 229 230 // See RFC 1929 231 if buf[1] == Method_USER_PASS { 232 buf = buf[:0] 233 buf = append(buf, 1 /* password protocol version */) 234 buf = append(buf, uint8(len(s.user))) 235 buf = append(buf, s.user...) 236 buf = append(buf, uint8(len(s.password))) 237 buf = append(buf, s.password...) 238 (*s.conn).SetDeadline(time.Now().Add(s.timeout)) 239 if _, err := (*s.conn).Write(buf); err != nil { 240 return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) 241 } 242 (*s.conn).SetDeadline(time.Time{}) 243 (*s.conn).SetDeadline(time.Now().Add(s.timeout)) 244 if _, err := io.ReadFull((*s.conn), buf[:2]); err != nil { 245 return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) 246 } 247 (*s.conn).SetDeadline(time.Time{}) 248 if buf[1] != 0 { 249 return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password") 250 } 251 } 252 return nil 253 }