libgo: update to go1.9
[official-gcc.git] / libgo / go / golang_org / x / net / proxy / socks5.go
blob973f57f19703995561037a3f9f6c84d0f5c5048a
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.
5 package proxy
7 import (
8 "errors"
9 "io"
10 "net"
11 "strconv"
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,
22 if auth != nil {
23 s.user = auth.User
24 s.password = auth.Password
27 return s, nil
30 type socks5 struct {
31 user, password string
32 network, addr string
33 forward Dialer
36 const socks5Version = 5
38 const (
39 socks5AuthNone = 0
40 socks5AuthPassword = 2
43 const socks5Connect = 1
45 const (
46 socks5IP4 = 1
47 socks5Domain = 3
48 socks5IP6 = 4
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",
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)
71 conn, err := s.forward.Dial(s.network, s.addr)
72 if err != nil {
73 return nil, err
75 if err := s.connect(conn, addr); err != nil {
76 conn.Close()
77 return nil, err
79 return conn, nil
82 // connect takes an existing connection to a socks5 proxy server,
83 // and commands the server to extend that connection to target,
84 // which must be a canonical address with a host and port.
85 func (s *socks5) connect(conn net.Conn, target string) error {
86 host, portStr, err := net.SplitHostPort(target)
87 if err != nil {
88 return err
91 port, err := strconv.Atoi(portStr)
92 if err != nil {
93 return errors.New("proxy: failed to parse port number: " + portStr)
95 if port < 1 || port > 0xffff {
96 return errors.New("proxy: port number out of range: " + portStr)
99 // the size here is just an estimate
100 buf := make([]byte, 0, 6+len(host))
102 buf = append(buf, socks5Version)
103 if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
104 buf = append(buf, 2 /* num auth methods */, socks5AuthNone, socks5AuthPassword)
105 } else {
106 buf = append(buf, 1 /* num auth methods */, socks5AuthNone)
109 if _, err := conn.Write(buf); err != nil {
110 return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
113 if _, err := io.ReadFull(conn, buf[:2]); err != nil {
114 return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
116 if buf[0] != 5 {
117 return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
119 if buf[1] == 0xff {
120 return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
123 if buf[1] == socks5AuthPassword {
124 buf = buf[:0]
125 buf = append(buf, 1 /* password protocol version */)
126 buf = append(buf, uint8(len(s.user)))
127 buf = append(buf, s.user...)
128 buf = append(buf, uint8(len(s.password)))
129 buf = append(buf, s.password...)
131 if _, err := conn.Write(buf); err != nil {
132 return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
135 if _, err := io.ReadFull(conn, buf[:2]); err != nil {
136 return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
139 if buf[1] != 0 {
140 return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
144 buf = buf[:0]
145 buf = append(buf, socks5Version, socks5Connect, 0 /* reserved */)
147 if ip := net.ParseIP(host); ip != nil {
148 if ip4 := ip.To4(); ip4 != nil {
149 buf = append(buf, socks5IP4)
150 ip = ip4
151 } else {
152 buf = append(buf, socks5IP6)
154 buf = append(buf, ip...)
155 } else {
156 if len(host) > 255 {
157 return errors.New("proxy: destination hostname too long: " + host)
159 buf = append(buf, socks5Domain)
160 buf = append(buf, byte(len(host)))
161 buf = append(buf, host...)
163 buf = append(buf, byte(port>>8), byte(port))
165 if _, err := conn.Write(buf); err != nil {
166 return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
169 if _, err := io.ReadFull(conn, buf[:4]); err != nil {
170 return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
173 failure := "unknown error"
174 if int(buf[1]) < len(socks5Errors) {
175 failure = socks5Errors[buf[1]]
178 if len(failure) > 0 {
179 return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
182 bytesToDiscard := 0
183 switch buf[3] {
184 case socks5IP4:
185 bytesToDiscard = net.IPv4len
186 case socks5IP6:
187 bytesToDiscard = net.IPv6len
188 case socks5Domain:
189 _, err := io.ReadFull(conn, buf[:1])
190 if err != nil {
191 return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
193 bytesToDiscard = int(buf[0])
194 default:
195 return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
198 if cap(buf) < bytesToDiscard {
199 buf = make([]byte, bytesToDiscard)
200 } else {
201 buf = buf[:bytesToDiscard]
203 if _, err := io.ReadFull(conn, buf); err != nil {
204 return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
207 // Also need to discard the port number
208 if _, err := io.ReadFull(conn, buf[:2]); err != nil {
209 return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
212 return nil