safing-portmaster/netenv/location_windows.go
2020-10-15 15:25:34 +02:00

63 lines
1.9 KiB
Go

package netenv
import (
"context"
"fmt"
"net"
"os"
"syscall"
"unsafe"
)
// Windows specific constants for the WSAIoctl interface.
//nolint:golint,stylecheck
const (
SIO_RCVALL = syscall.IOC_IN | syscall.IOC_VENDOR | 1
RCVALL_OFF = 0
RCVALL_ON = 1
RCVALL_SOCKETLEVELONLY = 2
RCVALL_IPLEVEL = 3
)
func newICMPListener(address string) (net.PacketConn, error) {
// This is an attempt to work around the problem described here:
// https://github.com/golang/go/issues/38427
// First, get the correct local interface address, as SIO_RCVALL can't be set on a 0.0.0.0 listeners.
dialedConn, err := net.Dial("ip4:icmp", address)
if err != nil {
return nil, fmt.Errorf("failed to dial: %s", err)
}
localAddr := dialedConn.LocalAddr()
dialedConn.Close()
// Configure the setup routine in order to extract the socket handle.
var socketHandle syscall.Handle
cfg := net.ListenConfig{
Control: func(network, address string, c syscall.RawConn) error {
return c.Control(func(s uintptr) {
socketHandle = syscall.Handle(s)
})
},
}
// Bind to interface.
conn, err := cfg.ListenPacket(context.Background(), "ip4:icmp", localAddr.String())
if err != nil {
return nil, err
}
// Set socket option to receive all packets, such as ICMP error messages.
// This is somewhat dirty, as there is guarantee that socketHandle is still valid.
// WARNING: The Windows Firewall might just drop the incoming packets you might want to receive.
unused := uint32(0) // Documentation states that this is unused, but WSAIoctl fails without it.
flag := uint32(RCVALL_IPLEVEL)
size := uint32(unsafe.Sizeof(flag))
err = syscall.WSAIoctl(socketHandle, SIO_RCVALL, (*byte)(unsafe.Pointer(&flag)), size, nil, 0, &unused, nil, 0)
if err != nil {
return nil, fmt.Errorf("failed to set socket to listen to all packests: %s", os.NewSyscallError("WSAIoctl", err))
}
return conn, nil
}