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.
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
) {
24 s
.password
= auth
.Password
36 const socks5Version
= 5
40 socks5AuthPassword
= 2
43 const socks5Connect
= 1
51 var socks5Errors
= []string{
54 "connection forbidden",
55 "network unreachable",
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
) {
66 case "tcp", "tcp6", "tcp4":
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
)
75 if err
:= s
.connect(conn
, addr
); err
!= 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
)
91 port
, err
:= strconv
.Atoi(portStr
)
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
)
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())
117 return errors
.New("proxy: SOCKS5 proxy at " + s
.addr
+ " has unexpected version " + strconv
.Itoa(int(buf
[0])))
120 return errors
.New("proxy: SOCKS5 proxy at " + s
.addr
+ " requires authentication")
123 if buf
[1] == socks5AuthPassword
{
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())
140 return errors
.New("proxy: SOCKS5 proxy at " + s
.addr
+ " rejected username/password")
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
)
152 buf
= append(buf
, socks5IP6
)
154 buf
= append(buf
, ip
...)
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
)
185 bytesToDiscard
= net
.IPv4len
187 bytesToDiscard
= net
.IPv6len
189 _
, err
:= io
.ReadFull(conn
, buf
[:1])
191 return errors
.New("proxy: failed to read domain length from SOCKS5 proxy at " + s
.addr
+ ": " + err
.Error())
193 bytesToDiscard
= int(buf
[0])
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
)
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())