Files
coredns/core/dnsserver/udp_windows.go

108 lines
3.1 KiB
Go
Raw Normal View History

package dnsserver
import (
"encoding/binary"
"math"
"net"
"strconv"
"golang.org/x/sys/windows"
)
// WSACMSGHDR contains a native-size length followed by two 32-bit integers.
// Each control message is aligned to SIZE_T; its length excludes tail padding.
const udpCmsgWord = strconv.IntSize / 8
const udpCmsgHeader = udpCmsgWord + 8
var udpOOBSize = udpCmsgAlign(udpCmsgHeader+8) + udpCmsgAlign(udpCmsgHeader+20)
func udpCmsgAlign(n int) int { return (n + udpCmsgWord - 1) & ^(udpCmsgWord - 1) }
func enableUDPPacketInfo(conn *net.UDPConn) bool {
raw, err := conn.SyscallConn()
if err != nil {
return false
}
var err4, err6 error
err = raw.Control(func(fd uintptr) {
// Dual-stack sockets need both options to receive both families.
err4 = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, windows.IP_PKTINFO, 1)
err6 = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, windows.IPV6_PKTINFO, 1)
})
return err == nil && (err4 == nil || err6 == nil)
}
func parseUDPPacketInfo(oob []byte, flags int) udpPacketInfo {
if flags&windows.MSG_CTRUNC != 0 {
return udpPacketInfo{}
}
for len(oob) >= udpCmsgHeader {
var length uint64
if udpCmsgWord == 8 {
length = binary.NativeEndian.Uint64(oob)
} else {
length = uint64(binary.NativeEndian.Uint32(oob))
}
if length < udpCmsgHeader || length > uint64(len(oob)) || length > math.MaxInt32 {
return udpPacketInfo{}
}
level := binary.NativeEndian.Uint32(oob[udpCmsgWord:])
kind := binary.NativeEndian.Uint32(oob[udpCmsgWord+4:])
data := oob[udpCmsgHeader:length]
ipLen := 0
switch {
case level == windows.IPPROTO_IP && kind == windows.IP_PKTINFO:
ipLen = net.IPv4len
case level == windows.IPPROTO_IPV6 && kind == windows.IPV6_PKTINFO:
ipLen = net.IPv6len
}
if ipLen != 0 {
if len(data) < ipLen+4 {
return udpPacketInfo{}
}
ifIndex := binary.NativeEndian.Uint32(data[ipLen:])
if ifIndex > math.MaxInt32 {
return udpPacketInfo{}
}
return udpPacketInfo{dst: net.IP(data[:ipLen]), ifIndex: int(ifIndex)}
}
next := udpCmsgAlign(int(length))
if next < int(length) || next > len(oob) {
break
}
oob = oob[next:]
}
return udpPacketInfo{}
}
func udpSourceControl(info udpPacketInfo) []byte {
if info.dst == nil || info.dst.IsUnspecified() || info.dst.IsMulticast() {
return nil
}
ifIndex := info.replyInterface()
if ifIndex < 0 || ifIndex > math.MaxInt32 {
return nil
}
ip := info.dst.To4()
level, kind := uint32(windows.IPPROTO_IP), uint32(windows.IP_PKTINFO)
if ip == nil {
ip = info.dst.To16()
level, kind = windows.IPPROTO_IPV6, windows.IPV6_PKTINFO
}
if ip == nil {
return nil
}
length := udpCmsgHeader + len(ip) + 4
oob := make([]byte, udpCmsgAlign(length))
if udpCmsgWord == 8 {
binary.NativeEndian.PutUint64(oob, uint64(length))
} else {
binary.NativeEndian.PutUint32(oob, uint32(length)) //nolint:gosec // Header plus a 4- or 16-byte IP and a 4-byte interface.
}
binary.NativeEndian.PutUint32(oob[udpCmsgWord:], level)
binary.NativeEndian.PutUint32(oob[udpCmsgWord+4:], kind)
copy(oob[udpCmsgHeader:], ip)
binary.NativeEndian.PutUint32(oob[udpCmsgHeader+len(ip):], uint32(ifIndex))
return oob
}