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
package egress
import (
"encoding/binary"
"errors"
"net/netip"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
// Socket options that pin a socket's outgoing interface (ws2ipdef.h).
const (
ipUnicastIf = 31
ipv6UnicastIf = 31
)
// bind pins the socket to interface index. For IPv4 the index is passed
// in network byte order, as an address in 0.0.0.0/8 would be.
func bind(c syscall.RawConn, v6 bool, index uint32) error {
var opErr error
err := c.Control(func(fd uintptr) {
if v6 {
opErr = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, ipv6UnicastIf, int(index))
return
}
var b [4]byte
binary.BigEndian.PutUint32(b[:], index)
opErr = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, ipUnicastIf, int(binary.NativeEndian.Uint32(b[:])))
})
if err != nil {
return err
}
return opErr
}
// bestInterface is the interface Windows would send to dst through.
func bestInterface(dst netip.Addr) (uint32, bool) {
var sa windows.Sockaddr
if dst.Is4() {
sa = &windows.SockaddrInet4{Addr: dst.As4()}
} else {
sa = &windows.SockaddrInet6{Addr: dst.As16()}
}
var index uint32
if err := windows.GetBestInterfaceEx(sa, &index); err != nil {
return 0, false
}
return index, true
}
// keepRoute: Windows sends a socket left unbound along its best route.
func keepRoute(syscall.RawConn, bool, uint32) error { return nil }
// Supported: the core's sockets can always be bound outside a TUN here.
func Supported() error { return nil }
// defaultInterface is the interface of the default route with the lowest
// metric (the route's and its interface's) whose interface is up and is
// not the TUN adapter.
func defaultInterface(v6 bool, st *tunState) (uint32, bool) {
family := uint16(windows.AF_INET)
if v6 {
family = windows.AF_INET6
}
tun := st.luid
var table *windows.MibIpForwardTable2
if err := windows.GetIpForwardTable2(family, &table); err != nil {
return 0, false
}
defer windows.FreeMibTable(unsafe.Pointer(table))
var best uint32
bestMetric := ^uint32(0)
for _, row := range table.Rows() {
if row.DestinationPrefix.PrefixLength != 0 || row.InterfaceLuid == tun {
continue
}
ifRow := windows.MibIfRow2{InterfaceLuid: row.InterfaceLuid}
if windows.GetIfEntry2Ex(windows.MibIfEntryNormal, &ifRow) != nil ||
ifRow.OperStatus != windows.IfOperStatusUp || ifRow.Type == windows.IF_TYPE_SOFTWARE_LOOPBACK {
continue
}
ipRow := windows.MibIpInterfaceRow{Family: family, InterfaceLuid: row.InterfaceLuid}
if windows.GetIpInterfaceEntry(&ipRow) != nil || ipRow.Connected == 0 {
continue
}
if metric := row.Metric + ipRow.Metric; metric < bestMetric {
best, bestMetric = row.InterfaceIndex, metric
}
}
return best, bestMetric != ^uint32(0)
}
// dnsServers are the DNS servers of the IPv4 default interface outside
// the TUN (else the IPv6 one), port 53; not Windows' unanswered fec0::
// placeholders.
func dnsServers(st *tunState) []netip.AddrPort {
index, ok := physical(false, st)
if !ok {
if index, ok = physical(true, st); !ok {
return nil
}
}
adapters, err := adapterAddresses()
if err != nil {
return nil
}
var v4, v6 []netip.AddrPort
for a := adapters; a != nil; a = a.Next {
if a.IfIndex != index && a.Ipv6IfIndex != index {
continue
}
for d := a.FirstDnsServerAddress; d != nil; d = d.Next {
ip, ok := netip.AddrFromSlice(d.Address.IP())
if !ok {
continue
}
ip = ip.Unmap()
if ip.Is4() {
v4 = append(v4, netip.AddrPortFrom(ip, 53))
} else if !netip.MustParsePrefix("fec0::/10").Contains(ip) {
v6 = append(v6, netip.AddrPortFrom(ip.WithZone(""), 53))
}
}
}
return append(v4, v6...)
}
func adapterAddresses() (*windows.IpAdapterAddresses, error) {
const flags = windows.GAA_FLAG_SKIP_ANYCAST | windows.GAA_FLAG_SKIP_MULTICAST | windows.GAA_FLAG_SKIP_FRIENDLY_NAME
size := uint32(16 << 10)
for range 3 {
buf := make([]byte, size)
first := (*windows.IpAdapterAddresses)(unsafe.Pointer(&buf[0]))
err := windows.GetAdaptersAddresses(windows.AF_UNSPEC, flags, 0, first, &size)
if errors.Is(err, windows.ERROR_BUFFER_OVERFLOW) {
continue
}
if err != nil {
return nil, err
}
return first, nil
}
return nil, windows.ERROR_BUFFER_OVERFLOW
}