1// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.
2//go:generate bundle -o socks_bundle.go -prefix socks golang.org/x/net/internal/socks
3
4// Package socks provides a SOCKS version 5 client implementation.
5//
6// SOCKS protocol version 5 is defined in RFC 1928.
7// Username/Password authentication for SOCKS version 5 is defined in
8// RFC 1929.
9//
10
11package http
12
13import (
14	"context"
15	"errors"
16	"io"
17	"net"
18	"strconv"
19	"time"
20)
21
22var (
23	socksnoDeadline   = time.Time{}
24	socksaLongTimeAgo = time.Unix(1, 0)
25)
26
27func (d *socksDialer) connect(ctx context.Context, c net.Conn, address string) (_ net.Addr, ctxErr error) {
28	host, port, err := sockssplitHostPort(address)
29	if err != nil {
30		return nil, err
31	}
32	if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() {
33		c.SetDeadline(deadline)
34		defer c.SetDeadline(socksnoDeadline)
35	}
36	if ctx != context.Background() {
37		errCh := make(chan error, 1)
38		done := make(chan struct{})
39		defer func() {
40			close(done)
41			if ctxErr == nil {
42				ctxErr = <-errCh
43			}
44		}()
45		go func() {
46			select {
47			case <-ctx.Done():
48				c.SetDeadline(socksaLongTimeAgo)
49				errCh <- ctx.Err()
50			case <-done:
51				errCh <- nil
52			}
53		}()
54	}
55
56	b := make([]byte, 0, 6+len(host)) // the size here is just an estimate
57	b = append(b, socksVersion5)
58	if len(d.AuthMethods) == 0 || d.Authenticate == nil {
59		b = append(b, 1, byte(socksAuthMethodNotRequired))
60	} else {
61		ams := d.AuthMethods
62		if len(ams) > 255 {
63			return nil, errors.New("too many authentication methods")
64		}
65		b = append(b, byte(len(ams)))
66		for _, am := range ams {
67			b = append(b, byte(am))
68		}
69	}
70	if _, ctxErr = c.Write(b); ctxErr != nil {
71		return
72	}
73
74	if _, ctxErr = io.ReadFull(c, b[:2]); ctxErr != nil {
75		return
76	}
77	if b[0] != socksVersion5 {
78		return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0])))
79	}
80	am := socksAuthMethod(b[1])
81	if am == socksAuthMethodNoAcceptableMethods {
82		return nil, errors.New("no acceptable authentication methods")
83	}
84	if d.Authenticate != nil {
85		if ctxErr = d.Authenticate(ctx, c, am); ctxErr != nil {
86			return
87		}
88	}
89
90	b = b[:0]
91	b = append(b, socksVersion5, byte(d.cmd), 0)
92	if ip := net.ParseIP(host); ip != nil {
93		if ip4 := ip.To4(); ip4 != nil {
94			b = append(b, socksAddrTypeIPv4)
95			b = append(b, ip4...)
96		} else if ip6 := ip.To16(); ip6 != nil {
97			b = append(b, socksAddrTypeIPv6)
98			b = append(b, ip6...)
99		} else {
100			return nil, errors.New("unknown address type")
101		}
102	} else {
103		if len(host) > 255 {
104			return nil, errors.New("FQDN too long")
105		}
106		b = append(b, socksAddrTypeFQDN)
107		b = append(b, byte(len(host)))
108		b = append(b, host...)
109	}
110	b = append(b, byte(port>>8), byte(port))
111	if _, ctxErr = c.Write(b); ctxErr != nil {
112		return
113	}
114
115	if _, ctxErr = io.ReadFull(c, b[:4]); ctxErr != nil {
116		return
117	}
118	if b[0] != socksVersion5 {
119		return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0])))
120	}
121	if cmdErr := socksReply(b[1]); cmdErr != socksStatusSucceeded {
122		return nil, errors.New("unknown error " + cmdErr.String())
123	}
124	if b[2] != 0 {
125		return nil, errors.New("non-zero reserved field")
126	}
127	l := 2
128	var a socksAddr
129	switch b[3] {
130	case socksAddrTypeIPv4:
131		l += net.IPv4len
132		a.IP = make(net.IP, net.IPv4len)
133	case socksAddrTypeIPv6:
134		l += net.IPv6len
135		a.IP = make(net.IP, net.IPv6len)
136	case socksAddrTypeFQDN:
137		if _, err := io.ReadFull(c, b[:1]); err != nil {
138			return nil, err
139		}
140		l += int(b[0])
141	default:
142		return nil, errors.New("unknown address type " + strconv.Itoa(int(b[3])))
143	}
144	if cap(b) < l {
145		b = make([]byte, l)
146	} else {
147		b = b[:l]
148	}
149	if _, ctxErr = io.ReadFull(c, b); ctxErr != nil {
150		return
151	}
152	if a.IP != nil {
153		copy(a.IP, b)
154	} else {
155		a.Name = string(b[:len(b)-2])
156	}
157	a.Port = int(b[len(b)-2])<<8 | int(b[len(b)-1])
158	return &a, nil
159}
160
161func sockssplitHostPort(address string) (string, int, error) {
162	host, port, err := net.SplitHostPort(address)
163	if err != nil {
164		return "", 0, err
165	}
166	portnum, err := strconv.Atoi(port)
167	if err != nil {
168		return "", 0, err
169	}
170	if 1 > portnum || portnum > 0xffff {
171		return "", 0, errors.New("port number out of range " + port)
172	}
173	return host, portnum, nil
174}
175
176// A Command represents a SOCKS command.
177type socksCommand int
178
179func (cmd socksCommand) String() string {
180	switch cmd {
181	case socksCmdConnect:
182		return "socks connect"
183	case sockscmdBind:
184		return "socks bind"
185	default:
186		return "socks " + strconv.Itoa(int(cmd))
187	}
188}
189
190// An AuthMethod represents a SOCKS authentication method.
191type socksAuthMethod int
192
193// A Reply represents a SOCKS command reply code.
194type socksReply int
195
196func (code socksReply) String() string {
197	switch code {
198	case socksStatusSucceeded:
199		return "succeeded"
200	case 0x01:
201		return "general SOCKS server failure"
202	case 0x02:
203		return "connection not allowed by ruleset"
204	case 0x03:
205		return "network unreachable"
206	case 0x04:
207		return "host unreachable"
208	case 0x05:
209		return "connection refused"
210	case 0x06:
211		return "TTL expired"
212	case 0x07:
213		return "command not supported"
214	case 0x08:
215		return "address type not supported"
216	default:
217		return "unknown code: " + strconv.Itoa(int(code))
218	}
219}
220
221// Wire protocol constants.
222const (
223	socksVersion5 = 0x05
224
225	socksAddrTypeIPv4 = 0x01
226	socksAddrTypeFQDN = 0x03
227	socksAddrTypeIPv6 = 0x04
228
229	socksCmdConnect socksCommand = 0x01 // establishes an active-open forward proxy connection
230	sockscmdBind    socksCommand = 0x02 // establishes a passive-open forward proxy connection
231
232	socksAuthMethodNotRequired         socksAuthMethod = 0x00 // no authentication required
233	socksAuthMethodUsernamePassword    socksAuthMethod = 0x02 // use username/password
234	socksAuthMethodNoAcceptableMethods socksAuthMethod = 0xff // no acceptable authentication methods
235
236	socksStatusSucceeded socksReply = 0x00
237)
238
239// An Addr represents a SOCKS-specific address.
240// Either Name or IP is used exclusively.
241type socksAddr struct {
242	Name string // fully-qualified domain name
243	IP   net.IP
244	Port int
245}
246
247func (a *socksAddr) Network() string { return "socks" }
248
249func (a *socksAddr) String() string {
250	if a == nil {
251		return "<nil>"
252	}
253	port := strconv.Itoa(a.Port)
254	if a.IP == nil {
255		return net.JoinHostPort(a.Name, port)
256	}
257	return net.JoinHostPort(a.IP.String(), port)
258}
259
260// A Conn represents a forward proxy connection.
261type socksConn struct {
262	net.Conn
263
264	boundAddr net.Addr
265}
266
267// BoundAddr returns the address assigned by the proxy server for
268// connecting to the command target address from the proxy server.
269func (c *socksConn) BoundAddr() net.Addr {
270	if c == nil {
271		return nil
272	}
273	return c.boundAddr
274}
275
276// A Dialer holds SOCKS-specific options.
277type socksDialer struct {
278	cmd          socksCommand // either CmdConnect or cmdBind
279	proxyNetwork string       // network between a proxy server and a client
280	proxyAddress string       // proxy server address
281
282	// ProxyDial specifies the optional dial function for
283	// establishing the transport connection.
284	ProxyDial func(context.Context, string, string) (net.Conn, error)
285
286	// AuthMethods specifies the list of request authentication
287	// methods.
288	// If empty, SOCKS client requests only AuthMethodNotRequired.
289	AuthMethods []socksAuthMethod
290
291	// Authenticate specifies the optional authentication
292	// function. It must be non-nil when AuthMethods is not empty.
293	// It must return an error when the authentication is failed.
294	Authenticate func(context.Context, io.ReadWriter, socksAuthMethod) error
295}
296
297// DialContext connects to the provided address on the provided
298// network.
299//
300// The returned error value may be a net.OpError. When the Op field of
301// net.OpError contains "socks", the Source field contains a proxy
302// server address and the Addr field contains a command target
303// address.
304//
305// See func Dial of the net package of standard library for a
306// description of the network and address parameters.
307func (d *socksDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
308	if err := d.validateTarget(network, address); err != nil {
309		proxy, dst, _ := d.pathAddrs(address)
310		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
311	}
312	if ctx == nil {
313		proxy, dst, _ := d.pathAddrs(address)
314		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
315	}
316	var err error
317	var c net.Conn
318	if d.ProxyDial != nil {
319		c, err = d.ProxyDial(ctx, d.proxyNetwork, d.proxyAddress)
320	} else {
321		var dd net.Dialer
322		c, err = dd.DialContext(ctx, d.proxyNetwork, d.proxyAddress)
323	}
324	if err != nil {
325		proxy, dst, _ := d.pathAddrs(address)
326		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
327	}
328	a, err := d.connect(ctx, c, address)
329	if err != nil {
330		c.Close()
331		proxy, dst, _ := d.pathAddrs(address)
332		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
333	}
334	return &socksConn{Conn: c, boundAddr: a}, nil
335}
336
337// DialWithConn initiates a connection from SOCKS server to the target
338// network and address using the connection c that is already
339// connected to the SOCKS server.
340//
341// It returns the connection's local address assigned by the SOCKS
342// server.
343func (d *socksDialer) DialWithConn(ctx context.Context, c net.Conn, network, address string) (net.Addr, error) {
344	if err := d.validateTarget(network, address); err != nil {
345		proxy, dst, _ := d.pathAddrs(address)
346		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
347	}
348	if ctx == nil {
349		proxy, dst, _ := d.pathAddrs(address)
350		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
351	}
352	a, err := d.connect(ctx, c, address)
353	if err != nil {
354		proxy, dst, _ := d.pathAddrs(address)
355		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
356	}
357	return a, nil
358}
359
360// Dial connects to the provided address on the provided network.
361//
362// Unlike DialContext, it returns a raw transport connection instead
363// of a forward proxy connection.
364//
365// Deprecated: Use DialContext or DialWithConn instead.
366func (d *socksDialer) Dial(network, address string) (net.Conn, error) {
367	if err := d.validateTarget(network, address); err != nil {
368		proxy, dst, _ := d.pathAddrs(address)
369		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
370	}
371	var err error
372	var c net.Conn
373	if d.ProxyDial != nil {
374		c, err = d.ProxyDial(context.Background(), d.proxyNetwork, d.proxyAddress)
375	} else {
376		c, err = net.Dial(d.proxyNetwork, d.proxyAddress)
377	}
378	if err != nil {
379		proxy, dst, _ := d.pathAddrs(address)
380		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
381	}
382	if _, err := d.DialWithConn(context.Background(), c, network, address); err != nil {
383		c.Close()
384		return nil, err
385	}
386	return c, nil
387}
388
389func (d *socksDialer) validateTarget(network, address string) error {
390	switch network {
391	case "tcp", "tcp6", "tcp4":
392	default:
393		return errors.New("network not implemented")
394	}
395	switch d.cmd {
396	case socksCmdConnect, sockscmdBind:
397	default:
398		return errors.New("command not implemented")
399	}
400	return nil
401}
402
403func (d *socksDialer) pathAddrs(address string) (proxy, dst net.Addr, err error) {
404	for i, s := range []string{d.proxyAddress, address} {
405		host, port, err := sockssplitHostPort(s)
406		if err != nil {
407			return nil, nil, err
408		}
409		a := &socksAddr{Port: port}
410		a.IP = net.ParseIP(host)
411		if a.IP == nil {
412			a.Name = host
413		}
414		if i == 0 {
415			proxy = a
416		} else {
417			dst = a
418		}
419	}
420	return
421}
422
423// NewDialer returns a new Dialer that dials through the provided
424// proxy server's network and address.
425func socksNewDialer(network, address string) *socksDialer {
426	return &socksDialer{proxyNetwork: network, proxyAddress: address, cmd: socksCmdConnect}
427}
428
429const (
430	socksauthUsernamePasswordVersion = 0x01
431	socksauthStatusSucceeded         = 0x00
432)
433
434// UsernamePassword are the credentials for the username/password
435// authentication method.
436type socksUsernamePassword struct {
437	Username string
438	Password string
439}
440
441// Authenticate authenticates a pair of username and password with the
442// proxy server.
443func (up *socksUsernamePassword) Authenticate(ctx context.Context, rw io.ReadWriter, auth socksAuthMethod) error {
444	switch auth {
445	case socksAuthMethodNotRequired:
446		return nil
447	case socksAuthMethodUsernamePassword:
448		if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) > 255 {
449			return errors.New("invalid username/password")
450		}
451		b := []byte{socksauthUsernamePasswordVersion}
452		b = append(b, byte(len(up.Username)))
453		b = append(b, up.Username...)
454		b = append(b, byte(len(up.Password)))
455		b = append(b, up.Password...)
456		// TODO(mikio): handle IO deadlines and cancelation if
457		// necessary
458		if _, err := rw.Write(b); err != nil {
459			return err
460		}
461		if _, err := io.ReadFull(rw, b[:2]); err != nil {
462			return err
463		}
464		if b[0] != socksauthUsernamePasswordVersion {
465			return errors.New("invalid username/password version")
466		}
467		if b[1] != socksauthStatusSucceeded {
468			return errors.New("username/password authentication failed")
469		}
470		return nil
471	}
472	return errors.New("unsupported authentication method " + strconv.Itoa(int(auth)))
473}
474