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