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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
// Package egress keeps the core's own connections out of the TUN adapter of
// the desktop TUN mode. While a TUN runs (SetTun), the adapter holds the
// default routes, so a connection the core opens itself (to Mozilla, the
// account and server-list APIs, DNS over HTTPS, a destination routed
// directly) would go into the TUN, to the TUN service, and back to the
// core's own proxy. Every outgoing socket the core opens is therefore bound
// to the interface that has the default route besides the TUN (Control),
// chosen again for each socket, so a change of network (Wi-Fi to Ethernet)
// is followed; and the names the system's resolver would answer (with the
// TUN's fake addresses) are asked of that network's own DNS servers
// instead. Without a TUN nothing changes.
package egress
import (
"context"
"errors"
"net"
"net/netip"
"strings"
"sync"
"sync/atomic"
"syscall"
"time"
)
// tunState is the running TUN adapter.
type tunState struct {
index uint32
luid uint64
}
var current atomic.Pointer[tunState]
// SetTun binds the core's sockets outside the TUN adapter with this
// interface index and LUID, from now on.
func SetTun(index uint32, luid uint64) {
current.Store(&tunState{index: index, luid: luid})
forget()
}
// ClearTun ends the binding.
func ClearTun() {
current.Store(nil)
forget()
}
// Tun is the TUN adapter while one runs.
func Tun() (index uint32, luid uint64, ok bool) {
st := current.Load()
if st == nil {
return 0, 0, false
}
return st.index, st.luid, true
}
// ErrNoNetwork means no interface but the TUN has a default route: a
// connection now would go into the TUN, so it is not made.
var ErrNoNetwork = errors.New("no network outside the TUN adapter")
// Control binds a socket being connected to address outside the TUN while
// one runs (net.Dialer.Control). A destination whose best route is another
// network than the TUN and the default one (a second LAN, WSL's or a
// virtual machine's) keeps that route (keepRoute).
func Control(network, address string, c syscall.RawConn) error {
st := current.Load()
if st == nil {
return nil
}
var dst netip.Addr
if ap, err := netip.ParseAddrPort(address); err == nil {
dst = ap.Addr().Unmap()
}
if dst.IsLoopback() || local(dst) {
return nil
}
v6 := dst.Is6() || (!dst.IsValid() && network[len(network)-1] == '6')
index, ok := physical(v6, st)
if !ok {
return ErrNoNetwork
}
if dst.IsValid() && !dst.IsUnspecified() {
if best, ok := bestInterface(dst); ok && best != st.index && best != index {
return keepRoute(c, v6, best)
}
}
return bind(c, v6, index)
}
// localAddrs keeps this computer's addresses a moment.
var localAddrs struct {
mu sync.Mutex
at time.Time
addrs map[netip.Addr]bool
}
// local reports whether addr is one of this computer's own addresses (a
// DNS server can be one: WSL's is on loopback), which a connection reaches
// without leaving the computer, whatever it is bound to.
func local(addr netip.Addr) bool {
localAddrs.mu.Lock()
defer localAddrs.mu.Unlock()
if time.Since(localAddrs.at) > time.Second {
localAddrs.addrs = map[netip.Addr]bool{}
if addrs, err := net.InterfaceAddrs(); err == nil {
for _, a := range addrs {
if p, err := netip.ParsePrefix(a.String()); err == nil {
localAddrs.addrs[p.Addr().Unmap()] = true
}
}
}
localAddrs.at = time.Now()
}
return localAddrs.addrs[addr.WithZone("")]
}
// Dialer is a net.Dialer for this program's own connections: from outside
// the TUN while one runs, resolving names with the physical network's DNS,
// and noted by address (OwnDial). Make one for each connection, since a
// TUN may start or stop between them.
func Dialer(timeout time.Duration) *net.Dialer {
d := ClientDialer(timeout)
d.Control = func(network, address string, c syscall.RawConn) error {
noteOwn(address)
return Control(network, address, c)
}
return d
}
// ClientDialer is Dialer for a client's connection (a direct route), which
// is not noted as this program's own.
func ClientDialer(timeout time.Duration) *net.Dialer {
d := &net.Dialer{Timeout: timeout, Control: Control}
if current.Load() != nil {
d.Resolver = physicalResolver
}
return d
}
// ownWindow is how long the address of one of this program's own
// connections is remembered.
const ownWindow = 10 * time.Second
// ownDials are the addresses this program itself connected to lately, by
// address and port, so that one coming back into its proxy as a client's
// connection shows a loop: something (PassWall2's Localhost Proxy, another
// VPN program's TUN) sends this program's own traffic into its proxy, and
// nothing connects.
var ownDials struct {
mu sync.Mutex
at map[string]time.Time
}
// ownNow is the clock of ownDials; tests replace it.
var ownNow = time.Now
// maxOwnDials bounds ownDials.
const maxOwnDials = 256
func noteOwn(address string) {
key := ownKey(address)
if key == "" {
return
}
now := ownNow()
ownDials.mu.Lock()
defer ownDials.mu.Unlock()
if ownDials.at == nil {
ownDials.at = map[string]time.Time{}
}
if len(ownDials.at) >= maxOwnDials {
for k, at := range ownDials.at {
if now.Sub(at) > ownWindow {
delete(ownDials.at, k)
}
}
if len(ownDials.at) >= maxOwnDials {
return
}
}
ownDials.at[key] = now
}
// OwnDial reports whether target (host:port) is an address this program
// itself connected to in the last few seconds: a client's connection to it
// may be this program's own, sent back into its proxy.
func OwnDial(target string) bool {
key := ownKey(target)
if key == "" {
return false
}
ownDials.mu.Lock()
defer ownDials.mu.Unlock()
at, ok := ownDials.at[key]
return ok && ownNow().Sub(at) <= ownWindow
}
// ownKey writes an address one way: an IPv4 address mapped into IPv6 as
// IPv4, without a zone; a name in lower case. Loopback gives "": it never
// leaves this computer, so it never loops.
func ownKey(address string) string {
if ap, err := netip.ParseAddrPort(address); err == nil {
if ap.Addr().Unmap().IsLoopback() {
return ""
}
return netip.AddrPortFrom(ap.Addr().Unmap().WithZone(""), ap.Port()).String()
}
if host, _, err := net.SplitHostPort(address); err == nil && strings.EqualFold(host, "localhost") {
return ""
}
return strings.ToLower(address)
}
// DialContext connects with Dialer(0).
func DialContext(ctx context.Context, network, address string) (net.Conn, error) {
return Dialer(0).DialContext(ctx, network, address)
}
// LookupNetIP resolves host as the system's resolver does; while a TUN
// runs, by asking the physical network's DNS servers.
func LookupNetIP(ctx context.Context, host string) ([]netip.Addr, error) {
r := net.DefaultResolver
if current.Load() != nil {
r = physicalResolver
}
return r.LookupNetIP(ctx, "ip", host)
}
// ListenUDP opens an unconnected UDP socket for talking to dst (QUIC), of
// its family, bound outside the TUN while one runs.
func ListenUDP(dst netip.Addr) (*net.UDPConn, error) {
dst = dst.Unmap()
network := "udp4"
if dst.Is6() {
network = "udp6"
}
lc := net.ListenConfig{Control: func(network, _ string, c syscall.RawConn) error {
return Control(network, netip.AddrPortFrom(dst, 0).String(), c)
}}
pc, err := lc.ListenPacket(context.Background(), network, ":0")
if err != nil {
return nil, err
}
return pc.(*net.UDPConn), nil
}
// physicalResolver is Go's resolver asking the physical network's DNS
// servers (also for TCP), over sockets bound outside the TUN; it reads the
// hosts file like the system's.
var physicalResolver = &net.Resolver{PreferGo: true, Dial: dialDNS}
// dnsTurn spreads Go's resolver's attempts over the servers.
var dnsTurn atomic.Uint32
func dialDNS(ctx context.Context, network, _ string) (net.Conn, error) {
st := current.Load()
if st == nil {
return nil, errors.New("no TUN runs")
}
servers := dnsServers(st)
if len(servers) == 0 {
return nil, errors.New("the network outside the TUN adapter has no DNS servers")
}
server := servers[int(dnsTurn.Add(1))%len(servers)]
d := net.Dialer{Control: Control}
return d.DialContext(ctx, network, server.String())
}