OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / btcsuite / go-socks / socks / dial.go
1 // Copyright 2012 Samuel Stauffer. All rights reserved.
2 // Use of this source code is governed by a 3-clause BSD
3 // license that can be found in the LICENSE file.
4
5 /*
6 Current limitations:
7
8         - GSS-API authentication is not supported
9         - only SOCKS version 5 is supported
10         - TCP bind and UDP not yet supported
11
12 Example http client over SOCKS5:
13
14         proxy := &socks.Proxy{"127.0.0.1:1080"}
15         tr := &http.Transport{
16                 Dial: proxy.Dial,
17         }
18         client := &http.Client{Transport: tr}
19         resp, err := client.Get("https://example.com")
20 */
21 package socks
22
23 import (
24         "crypto/rand"
25         "encoding/hex"
26         "errors"
27         "io"
28         "net"
29         "strconv"
30         "time"
31 )
32
33 const (
34         protocolVersion = 5
35
36         defaultPort = 1080
37
38         authNone             = 0
39         authGssApi           = 1
40         authUsernamePassword = 2
41         authUnavailable      = 0xff
42
43         commandTcpConnect   = 1
44         commandTcpBind      = 2
45         commandUdpAssociate = 3
46
47         addressTypeIPv4   = 1
48         addressTypeDomain = 3
49         addressTypeIPv6   = 4
50
51         statusRequestGranted          = 0
52         statusGeneralFailure          = 1
53         statusConnectionNotAllowed    = 2
54         statusNetworkUnreachable      = 3
55         statusHostUnreachable         = 4
56         statusConnectionRefused       = 5
57         statusTtlExpired              = 6
58         statusCommandNotSupport       = 7
59         statusAddressTypeNotSupported = 8
60 )
61
62 var (
63         ErrAuthFailed             = errors.New("authentication failed")
64         ErrInvalidProxyResponse   = errors.New("invalid proxy response")
65         ErrNoAcceptableAuthMethod = errors.New("no acceptable authentication method")
66
67         statusErrors = map[byte]error{
68                 statusGeneralFailure:          errors.New("general failure"),
69                 statusConnectionNotAllowed:    errors.New("connection not allowed by ruleset"),
70                 statusNetworkUnreachable:      errors.New("network unreachable"),
71                 statusHostUnreachable:         errors.New("host unreachable"),
72                 statusConnectionRefused:       errors.New("connection refused by destination host"),
73                 statusTtlExpired:              errors.New("TTL expired"),
74                 statusCommandNotSupport:       errors.New("command not supported / protocol error"),
75                 statusAddressTypeNotSupported: errors.New("address type not supported"),
76         }
77 )
78
79 type Proxy struct {
80         Addr         string
81         Username     string
82         Password     string
83         TorIsolation bool
84 }
85
86 func (p *Proxy) Dial(network, addr string) (net.Conn, error) {
87         return p.dial(network, addr, 0)
88 }
89
90 func (p *Proxy) DialTimeout(network, addr string, timeout time.Duration) (net.Conn, error) {
91         return p.dial(network, addr, timeout)
92 }
93
94 func (p *Proxy) dial(network, addr string, timeout time.Duration) (net.Conn, error) {
95         host, strPort, err := net.SplitHostPort(addr)
96         if err != nil {
97                 return nil, err
98         }
99         port, err := strconv.Atoi(strPort)
100         if err != nil {
101                 return nil, err
102         }
103
104         conn, err := net.DialTimeout("tcp", p.Addr, timeout)
105         if err != nil {
106                 return nil, err
107         }
108
109         var user, pass string
110         if p.TorIsolation {
111                 var b [16]byte
112                 _, err := io.ReadFull(rand.Reader, b[:])
113                 if err != nil {
114                         conn.Close()
115                         return nil, err
116                 }
117                 user = hex.EncodeToString(b[0:8])
118                 pass = hex.EncodeToString(b[8:16])
119         } else {
120                 user = p.Username
121                 pass = p.Password
122         }
123         buf := make([]byte, 32+len(host)+len(user)+len(pass))
124
125         // Initial greeting
126         buf[0] = protocolVersion
127         if user != "" {
128                 buf = buf[:4]
129                 buf[1] = 2 // num auth methods
130                 buf[2] = authNone
131                 buf[3] = authUsernamePassword
132         } else {
133                 buf = buf[:3]
134                 buf[1] = 1 // num auth methods
135                 buf[2] = authNone
136         }
137
138         _, err = conn.Write(buf)
139         if err != nil {
140                 conn.Close()
141                 return nil, err
142         }
143
144         // Server's auth choice
145
146         if _, err := io.ReadFull(conn, buf[:2]); err != nil {
147                 conn.Close()
148                 return nil, err
149         }
150         if buf[0] != protocolVersion {
151                 conn.Close()
152                 return nil, ErrInvalidProxyResponse
153         }
154         err = nil
155         switch buf[1] {
156         default:
157                 err = ErrInvalidProxyResponse
158         case authUnavailable:
159                 err = ErrNoAcceptableAuthMethod
160         case authGssApi:
161                 err = ErrNoAcceptableAuthMethod
162         case authUsernamePassword:
163                 buf = buf[:3+len(user)+len(pass)]
164                 buf[0] = 1 // version
165                 buf[1] = byte(len(user))
166                 copy(buf[2:], user)
167                 buf[2+len(user)] = byte(len(pass))
168                 copy(buf[3+len(user):], pass)
169                 if _, err = conn.Write(buf); err != nil {
170                         conn.Close()
171                         return nil, err
172                 }
173                 if _, err = io.ReadFull(conn, buf[:2]); err != nil {
174                         conn.Close()
175                         return nil, err
176                 }
177                 if buf[0] != 1 { // version
178                         err = ErrInvalidProxyResponse
179                 } else if buf[1] != 0 { // 0 = succes, else auth failed
180                         err = ErrAuthFailed
181                 }
182         case authNone:
183                 // Do nothing
184         }
185         if err != nil {
186                 conn.Close()
187                 return nil, err
188         }
189
190         // Command / connection request
191
192         buf = buf[:7+len(host)]
193         buf[0] = protocolVersion
194         buf[1] = commandTcpConnect
195         buf[2] = 0 // reserved
196         buf[3] = addressTypeDomain
197         buf[4] = byte(len(host))
198         copy(buf[5:], host)
199         buf[5+len(host)] = byte(port >> 8)
200         buf[6+len(host)] = byte(port & 0xff)
201         if _, err := conn.Write(buf); err != nil {
202                 conn.Close()
203                 return nil, err
204         }
205
206         // Server response
207
208         if _, err := io.ReadFull(conn, buf[:4]); err != nil {
209                 conn.Close()
210                 return nil, err
211         }
212
213         if buf[0] != protocolVersion {
214                 conn.Close()
215                 return nil, ErrInvalidProxyResponse
216         }
217
218         if buf[1] != statusRequestGranted {
219                 conn.Close()
220                 err := statusErrors[buf[1]]
221                 if err == nil {
222                         err = ErrInvalidProxyResponse
223                 }
224                 return nil, err
225         }
226
227         paddr := &ProxiedAddr{Net: network}
228
229         switch buf[3] {
230         default:
231                 conn.Close()
232                 return nil, ErrInvalidProxyResponse
233         case addressTypeIPv4:
234                 if _, err := io.ReadFull(conn, buf[:4]); err != nil {
235                         conn.Close()
236                         return nil, err
237                 }
238                 paddr.Host = net.IP(buf).String()
239         case addressTypeIPv6:
240                 if _, err := io.ReadFull(conn, buf[:16]); err != nil {
241                         conn.Close()
242                         return nil, err
243                 }
244                 paddr.Host = net.IP(buf).String()
245         case addressTypeDomain:
246                 if _, err := io.ReadFull(conn, buf[:1]); err != nil {
247                         conn.Close()
248                         return nil, err
249                 }
250                 domainLen := buf[0]
251                 if _, err := io.ReadFull(conn, buf[:domainLen]); err != nil {
252                         conn.Close()
253                         return nil, err
254                 }
255                 paddr.Host = string(buf[:domainLen])
256         }
257
258         if _, err := io.ReadFull(conn, buf[:2]); err != nil {
259                 conn.Close()
260                 return nil, err
261         }
262         paddr.Port = int(buf[0])<<8 | int(buf[1])
263
264         return &proxiedConn{
265                 conn:       conn,
266                 boundAddr:  paddr,
267                 remoteAddr: &ProxiedAddr{network, host, port},
268         }, nil
269 }