131 lines
3.1 KiB
Go
131 lines
3.1 KiB
Go
|
//go:build windows
|
||
|
|
||
|
package netroute
|
||
|
|
||
|
import (
|
||
|
"net"
|
||
|
"strconv"
|
||
|
"syscall"
|
||
|
"unsafe"
|
||
|
|
||
|
"golang.org/x/sys/windows"
|
||
|
)
|
||
|
|
||
|
// socklen is a type for the length of a sockaddr.
|
||
|
type socklen uint
|
||
|
|
||
|
// ipAndZoneToSockaddr converts a net.IP (with optional IPv6 Zone) to a Sockaddr
|
||
|
// Returns nil if conversion fails.
|
||
|
func ipAndZoneToSockaddr(ip net.IP, zone string) windows.Sockaddr {
|
||
|
// Unspecified?
|
||
|
if ip == nil {
|
||
|
if zone != "" {
|
||
|
return &windows.SockaddrInet6{ZoneId: uint32(ip6ZoneToInt(zone))}
|
||
|
}
|
||
|
return new(windows.SockaddrInet4)
|
||
|
}
|
||
|
|
||
|
// Valid IPv4?
|
||
|
if ip4 := ip.To4(); ip4 != nil && zone == "" {
|
||
|
var buf [4]byte
|
||
|
copy(buf[:], ip4) // last 4 bytes
|
||
|
return &windows.SockaddrInet4{Addr: buf}
|
||
|
}
|
||
|
|
||
|
// Valid IPv6 address?
|
||
|
if ip6 := ip.To16(); ip6 != nil {
|
||
|
var buf [16]byte
|
||
|
copy(buf[:], ip6)
|
||
|
return &windows.SockaddrInet6{Addr: buf, ZoneId: uint32(ip6ZoneToInt(zone))}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// sockaddrToIPAndZone converts a Sockaddr to a net.IP (with optional IPv6 Zone)
|
||
|
// Returns nil if conversion fails.
|
||
|
func sockaddrToIPAndZone(sa windows.Sockaddr) (net.IP, string) {
|
||
|
switch sa := sa.(type) {
|
||
|
case *windows.SockaddrInet4:
|
||
|
ip := make([]byte, 16)
|
||
|
// V4InV6Prefix
|
||
|
ip[10] = 0xff
|
||
|
ip[11] = 0xff
|
||
|
copy(ip[12:16], sa.Addr[:])
|
||
|
return ip, ""
|
||
|
case *windows.SockaddrInet6:
|
||
|
ip := make([]byte, 16)
|
||
|
copy(ip, sa.Addr[:])
|
||
|
return ip, ip6ZoneToString(int(sa.ZoneId))
|
||
|
}
|
||
|
return nil, ""
|
||
|
}
|
||
|
|
||
|
func sockaddrToAny(sa windows.Sockaddr) (*windows.RawSockaddrAny, socklen, error) {
|
||
|
if sa == nil {
|
||
|
return nil, 0, syscall.EINVAL
|
||
|
}
|
||
|
|
||
|
switch sa := sa.(type) {
|
||
|
case *windows.SockaddrInet4:
|
||
|
if sa.Port < 0 || sa.Port > 0xFFFF {
|
||
|
return nil, 0, syscall.EINVAL
|
||
|
}
|
||
|
raw := new(windows.RawSockaddrAny)
|
||
|
raw.Addr.Family = windows.AF_INET
|
||
|
raw4 := (*windows.RawSockaddrInet4)(unsafe.Pointer(raw))
|
||
|
p := (*[2]byte)(unsafe.Pointer(&raw4.Port))
|
||
|
p[0] = byte(sa.Port >> 8)
|
||
|
p[1] = byte(sa.Port)
|
||
|
for i := 0; i < len(sa.Addr); i++ {
|
||
|
raw4.Addr[i] = sa.Addr[i]
|
||
|
}
|
||
|
return raw, socklen(unsafe.Sizeof(*raw4)), nil
|
||
|
case *windows.SockaddrInet6:
|
||
|
if sa.Port < 0 || sa.Port > 0xFFFF {
|
||
|
return nil, 0, syscall.EINVAL
|
||
|
}
|
||
|
raw := new(windows.RawSockaddrAny)
|
||
|
raw.Addr.Family = windows.AF_INET6
|
||
|
raw6 := (*windows.RawSockaddrInet6)(unsafe.Pointer(raw))
|
||
|
p := (*[2]byte)(unsafe.Pointer(&raw6.Port))
|
||
|
p[0] = byte(sa.Port >> 8)
|
||
|
p[1] = byte(sa.Port)
|
||
|
raw6.Scope_id = sa.ZoneId
|
||
|
for i := 0; i < len(sa.Addr); i++ {
|
||
|
raw6.Addr[i] = sa.Addr[i]
|
||
|
}
|
||
|
return raw, socklen(unsafe.Sizeof(*raw6)), nil
|
||
|
case *windows.SockaddrUnix:
|
||
|
return nil, 0, syscall.EWINDOWS
|
||
|
}
|
||
|
return nil, 0, syscall.EAFNOSUPPORT
|
||
|
}
|
||
|
|
||
|
// from: go/src/pkg/net/ipsock.go
|
||
|
|
||
|
// ip6ZoneToString converts an IP6 Zone unix int to a net string
|
||
|
// returns "" if zone is 0
|
||
|
func ip6ZoneToString(zone int) string {
|
||
|
if zone == 0 {
|
||
|
return ""
|
||
|
}
|
||
|
if ifi, err := net.InterfaceByIndex(zone); err == nil {
|
||
|
return ifi.Name
|
||
|
}
|
||
|
return strconv.Itoa(zone)
|
||
|
}
|
||
|
|
||
|
// ip6ZoneToInt converts an IP6 Zone net string to a unix int
|
||
|
// returns 0 if zone is ""
|
||
|
func ip6ZoneToInt(zone string) int {
|
||
|
if zone == "" {
|
||
|
return 0
|
||
|
}
|
||
|
if ifi, err := net.InterfaceByName(zone); err == nil {
|
||
|
return ifi.Index
|
||
|
}
|
||
|
n, _ := strconv.ParseInt(zone, 10, 32)
|
||
|
return int(n)
|
||
|
}
|