-
Notifications
You must be signed in to change notification settings - Fork 398
/
direct.go
148 lines (122 loc) · 3.32 KB
/
direct.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package proxy
import (
"context"
"errors"
"net"
"net/netip"
"time"
"github.com/nadoo/glider/pkg/sockopt"
)
// Direct proxy.
type Direct struct {
iface *net.Interface // interface specified by user
ip net.IP
dialTimeout time.Duration
relayTimeout time.Duration
}
func init() {
RegisterDialer("direct", NewDirectDialer)
}
// NewDirect returns a Direct dialer.
func NewDirect(intface string, dialTimeout, relayTimeout time.Duration) (*Direct, error) {
d := &Direct{dialTimeout: dialTimeout, relayTimeout: relayTimeout}
if intface != "" {
if addr, err := netip.ParseAddr(intface); err == nil {
d.ip = addr.AsSlice()
} else {
iface, err := net.InterfaceByName(intface)
if err != nil {
return nil, errors.New(err.Error() + ": " + intface)
}
d.iface = iface
}
}
return d, nil
}
// NewDirectDialer returns a direct dialer.
func NewDirectDialer(s string, d Dialer) (Dialer, error) {
if d == nil {
return NewDirect("", time.Duration(3)*time.Second, time.Duration(3)*time.Second)
}
return d, nil
}
// Addr returns forwarder's address.
func (d *Direct) Addr() string { return "DIRECT" }
// Dial connects to the address addr on the network net
func (d *Direct) Dial(network, addr string) (c net.Conn, err error) {
if d.iface == nil || d.ip != nil {
c, err = d.dial(network, addr, d.ip)
if err == nil {
return
}
}
for _, ip := range d.IFaceIPs() {
c, err = d.dial(network, addr, ip)
if err == nil {
d.ip = ip
break
}
}
// no ip available (so no dials made), maybe the interface link is down
if c == nil && err == nil {
err = errors.New("dial failed, maybe the interface link is down, please check it")
}
return c, err
}
func (d *Direct) dial(network, addr string, localIP net.IP) (net.Conn, error) {
var la net.Addr
switch network {
case "tcp":
la = &net.TCPAddr{IP: localIP}
case "udp":
la = &net.UDPAddr{IP: localIP}
}
dialer := &net.Dialer{LocalAddr: la, Timeout: d.dialTimeout}
if d.iface != nil {
dialer.Control = sockopt.Control(sockopt.Bind(d.iface))
}
c, err := dialer.Dial(network, addr)
if err != nil {
return nil, err
}
if c, ok := c.(*net.TCPConn); ok {
c.SetKeepAlive(true)
}
if d.relayTimeout > 0 {
c.SetDeadline(time.Now().Add(d.relayTimeout))
}
return c, err
}
// DialUDP connects to the given address.
func (d *Direct) DialUDP(network, addr string) (net.PacketConn, error) {
var la string
if d.ip != nil {
la = net.JoinHostPort(d.ip.String(), "0")
}
lc := &net.ListenConfig{}
if d.iface != nil {
lc.Control = sockopt.Control(sockopt.Bind(d.iface))
}
return lc.ListenPacket(context.Background(), network, la)
}
// IFaceIPs returns ip addresses according to the specified interface.
func (d *Direct) IFaceIPs() (ips []net.IP) {
ipNets, err := d.iface.Addrs()
if err != nil {
return
}
for _, ipNet := range ipNets {
ips = append(ips, ipNet.(*net.IPNet).IP) //!ip.IsLinkLocalUnicast()
}
return
}
func init() {
AddUsage("direct", `
Direct scheme:
direct://
Only needed when you want to load balance multiple interfaces directly:
glider -verbose -listen :8443 -forward direct://#interface=eth0 -forward direct://#interface=eth1 -strategy rr
Or you can use the high availability mode:
glider -verbose -listen :8443 -forward direct://#interface=eth0&priority=100 -forward direct://#interface=eth1&priority=200 -strategy ha
`)
}