OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / net / proxy / socks5.go
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         if err := s.connect(conn, addr); err != nil {
76                 conn.Close()
77                 return nil, err
78         }
79         return conn, nil
80 }
81
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
89         }
90
91         port, err := strconv.Atoi(portStr)
92         if err != nil {
93                 return errors.New("proxy: failed to parse port number: " + portStr)
94         }
95         if port < 1 || port > 0xffff {
96                 return errors.New("proxy: port number out of range: " + portStr)
97         }
98
99         // the size here is just an estimate
100         buf := make([]byte, 0, 6+len(host))
101
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)
107         }
108
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())
111         }
112
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())
115         }
116         if buf[0] != 5 {
117                 return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
118         }
119         if buf[1] == 0xff {
120                 return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
121         }
122
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...)
130
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())
133                 }
134
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())
137                 }
138
139                 if buf[1] != 0 {
140                         return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
141                 }
142         }
143
144         buf = buf[:0]
145         buf = append(buf, socks5Version, socks5Connect, 0 /* reserved */)
146
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)
153                 }
154                 buf = append(buf, ip...)
155         } else {
156                 if len(host) > 255 {
157                         return errors.New("proxy: destination host name too long: " + host)
158                 }
159                 buf = append(buf, socks5Domain)
160                 buf = append(buf, byte(len(host)))
161                 buf = append(buf, host...)
162         }
163         buf = append(buf, byte(port>>8), byte(port))
164
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())
167         }
168
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())
171         }
172
173         failure := "unknown error"
174         if int(buf[1]) < len(socks5Errors) {
175                 failure = socks5Errors[buf[1]]
176         }
177
178         if len(failure) > 0 {
179                 return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
180         }
181
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())
192                 }
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)
196         }
197
198         if cap(buf) < bytesToDiscard {
199                 buf = make([]byte, bytesToDiscard)
200         } else {
201                 buf = buf[:bytesToDiscard]
202         }
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())
205         }
206
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())
210         }
211
212         return nil
213 }