1package socks
2
3import (
4	"errors"
5	"net"
6	"time"
7)
8
9func (cfg *config) dialSocks4(targetAddr string) (_ net.Conn, err error) {
10	socksType := cfg.Proto
11	proxy := cfg.Host
12
13	// dial TCP
14	conn, err := net.Dial("tcp", proxy)
15	if err != nil {
16		return nil, err
17	}
18	defer func() {
19		if err != nil {
20			conn.Close()
21		}
22	}()
23
24	// connection request
25	host, port, err := splitHostPort(targetAddr)
26	if err != nil {
27		return nil, err
28	}
29	ip := net.IPv4(0, 0, 0, 1).To4()
30	if socksType == SOCKS4 {
31		ip, err = lookupIPv4(host)
32		if err != nil {
33			return nil, err
34		}
35	}
36	req := []byte{
37		4,                          // version number
38		1,                          // command CONNECT
39		byte(port >> 8),            // higher byte of destination port
40		byte(port),                 // lower byte of destination port (big endian)
41		ip[0], ip[1], ip[2], ip[3], // special invalid IP address to indicate the host name is provided
42		0, // user id is empty, anonymous proxy only
43	}
44	if socksType == SOCKS4A {
45		req = append(req, []byte(host+"\x00")...)
46	}
47
48	resp, err := cfg.sendReceive(conn, req)
49	if err != nil {
50		return nil, err
51	} else if len(resp) != 8 {
52		return nil, errors.New("server does not respond properly")
53	}
54	switch resp[1] {
55	case 90:
56		// request granted
57	case 91:
58		return nil, errors.New("socks connection request rejected or failed")
59	case 92:
60		return nil, errors.New("socks connection request rejected because SOCKS server cannot connect to identd on the client")
61	case 93:
62		return nil, errors.New("socks connection request rejected because the client program and identd report different user-ids")
63	default:
64		return nil, errors.New("socks connection request failed, unknown error")
65	}
66	// clear the deadline before returning
67	if err := conn.SetDeadline(time.Time{}); err != nil {
68		return nil, err
69	}
70	return conn, nil
71}
72