-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathdialer.go
133 lines (114 loc) · 2.42 KB
/
dialer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package usocksd
import (
"errors"
"hash/fnv"
"net"
"strconv"
"syscall"
"time"
"github.com/cybozu-go/usocksd/socks"
)
const (
dialTimeout = 10 * time.Second
)
type dialer struct {
*AddressGroup
}
func calcHint(caddr, daddr net.IP) uint32 {
hash := fnv.New32a()
hash.Write(caddr)
hash.Write(daddr)
return hash.Sum32()
}
func (d dialer) Dial(r *socks.Request) (net.Conn, error) {
var clientIP net.IP
if tca, ok := r.Conn.RemoteAddr().(*net.TCPAddr); ok {
clientIP = tca.IP
}
destIPs := []net.IP{r.IP}
if len(r.Hostname) > 0 {
ips, err := net.LookupIP(r.Hostname)
if err != nil {
return nil, err
}
destIPs = ips
}
deadline, ok := r.Context().Deadline()
if !ok {
deadline = time.Now().Add(dialTimeout)
}
var err error
for _, ip := range destIPs {
if time.Now().After(deadline) {
err = errors.New("dial timeout")
break
}
hint := calcHint(clientIP, ip)
laddr := &net.TCPAddr{
IP: d.PickAddress(hint),
}
raddr := &net.TCPAddr{
IP: ip,
Port: r.Port,
}
conn, err2 := net.DialTCP("tcp", laddr, raddr)
if err2 == nil {
return conn, nil
}
err = err2
}
return nil, err
}
type controlFn = func(network, address string, c syscall.RawConn) error
func bindControl(ifaceName string) controlFn {
return func(network, address string, c syscall.RawConn) error {
ipStr, _, err := net.SplitHostPort(address)
if err == nil {
ip := net.ParseIP(ipStr)
if ip != nil && !ip.IsGlobalUnicast() {
return nil
}
}
var bindErr error
callErr := c.Control(func(fd uintptr) {
bindErr = syscall.BindToDevice(int(fd), ifaceName)
})
if callErr != nil {
return callErr
}
return bindErr
}
}
type dumbDialer struct {
*net.Dialer
}
func (d dumbDialer) Dial(r *socks.Request) (net.Conn, error) {
var addr string
if len(r.Hostname) > 0 {
addr = net.JoinHostPort(r.Hostname, strconv.Itoa(r.Port))
} else {
addr = net.JoinHostPort(r.IP.String(), strconv.Itoa(r.Port))
}
return d.DialContext(r.Context(), "tcp", addr)
}
func createDialer(c *Config) socks.Dialer {
if c.Outgoing.IFace != "" {
return dumbDialer{
&net.Dialer{
KeepAlive: 3 * time.Minute,
DualStack: true,
Control: bindControl(c.Outgoing.IFace),
},
}
}
if len(c.Outgoing.Addresses) == 0 {
return dumbDialer{
&net.Dialer{
KeepAlive: 3 * time.Minute,
DualStack: true,
},
}
}
ag := NewAddressGroup(c.Outgoing.Addresses, c.Outgoing.DNSBLDomain)
return dialer{ag}
}