|
| 1 | +//go:build windows |
| 2 | +// +build windows |
| 3 | + |
| 4 | +package netroute |
| 5 | + |
| 6 | +import ( |
| 7 | + "net" |
| 8 | + "strconv" |
| 9 | + "syscall" |
| 10 | + "unsafe" |
| 11 | + |
| 12 | + "golang.org/x/sys/windows" |
| 13 | +) |
| 14 | + |
| 15 | +// socklen is a type for the length of a sockaddr. |
| 16 | +type socklen uint |
| 17 | + |
| 18 | +// ipAndZoneToSockaddr converts a net.IP (with optional IPv6 Zone) to a Sockaddr |
| 19 | +// Returns nil if conversion fails. |
| 20 | +func ipAndZoneToSockaddr(ip net.IP, zone string) windows.Sockaddr { |
| 21 | + // Unspecified? |
| 22 | + if ip == nil { |
| 23 | + if zone != "" { |
| 24 | + return &windows.SockaddrInet6{ZoneId: uint32(ip6ZoneToInt(zone))} |
| 25 | + } |
| 26 | + return new(windows.SockaddrInet4) |
| 27 | + } |
| 28 | + |
| 29 | + // Valid IPv4? |
| 30 | + if ip4 := ip.To4(); ip4 != nil && zone == "" { |
| 31 | + var buf [4]byte |
| 32 | + copy(buf[:], ip4) // last 4 bytes |
| 33 | + return &windows.SockaddrInet4{Addr: buf} |
| 34 | + } |
| 35 | + |
| 36 | + // Valid IPv6 address? |
| 37 | + if ip6 := ip.To16(); ip6 != nil { |
| 38 | + var buf [16]byte |
| 39 | + copy(buf[:], ip6) |
| 40 | + return &windows.SockaddrInet6{Addr: buf, ZoneId: uint32(ip6ZoneToInt(zone))} |
| 41 | + } |
| 42 | + |
| 43 | + return nil |
| 44 | +} |
| 45 | + |
| 46 | +// sockaddrToIPAndZone converts a Sockaddr to a net.IP (with optional IPv6 Zone) |
| 47 | +// Returns nil if conversion fails. |
| 48 | +func sockaddrToIPAndZone(sa windows.Sockaddr) (net.IP, string) { |
| 49 | + switch sa := sa.(type) { |
| 50 | + case *windows.SockaddrInet4: |
| 51 | + ip := make([]byte, 16) |
| 52 | + // V4InV6Prefix |
| 53 | + ip[10] = 0xff |
| 54 | + ip[11] = 0xff |
| 55 | + copy(ip[12:16], sa.Addr[:]) |
| 56 | + return ip, "" |
| 57 | + case *windows.SockaddrInet6: |
| 58 | + ip := make([]byte, 16) |
| 59 | + copy(ip, sa.Addr[:]) |
| 60 | + return ip, ip6ZoneToString(int(sa.ZoneId)) |
| 61 | + } |
| 62 | + return nil, "" |
| 63 | +} |
| 64 | + |
| 65 | +func sockaddrToAny(sa windows.Sockaddr) (*windows.RawSockaddrAny, socklen, error) { |
| 66 | + if sa == nil { |
| 67 | + return nil, 0, syscall.EINVAL |
| 68 | + } |
| 69 | + |
| 70 | + switch sa := sa.(type) { |
| 71 | + case *windows.SockaddrInet4: |
| 72 | + if sa.Port < 0 || sa.Port > 0xFFFF { |
| 73 | + return nil, 0, syscall.EINVAL |
| 74 | + } |
| 75 | + raw := new(windows.RawSockaddrAny) |
| 76 | + raw.Addr.Family = windows.AF_INET |
| 77 | + raw4 := (*windows.RawSockaddrInet4)(unsafe.Pointer(raw)) |
| 78 | + p := (*[2]byte)(unsafe.Pointer(&raw4.Port)) |
| 79 | + p[0] = byte(sa.Port >> 8) |
| 80 | + p[1] = byte(sa.Port) |
| 81 | + for i := 0; i < len(sa.Addr); i++ { |
| 82 | + raw4.Addr[i] = sa.Addr[i] |
| 83 | + } |
| 84 | + return raw, socklen(unsafe.Sizeof(*raw4)), nil |
| 85 | + case *windows.SockaddrInet6: |
| 86 | + if sa.Port < 0 || sa.Port > 0xFFFF { |
| 87 | + return nil, 0, syscall.EINVAL |
| 88 | + } |
| 89 | + raw := new(windows.RawSockaddrAny) |
| 90 | + raw.Addr.Family = windows.AF_INET6 |
| 91 | + raw6 := (*windows.RawSockaddrInet6)(unsafe.Pointer(raw)) |
| 92 | + p := (*[2]byte)(unsafe.Pointer(&raw6.Port)) |
| 93 | + p[0] = byte(sa.Port >> 8) |
| 94 | + p[1] = byte(sa.Port) |
| 95 | + raw6.Scope_id = sa.ZoneId |
| 96 | + for i := 0; i < len(sa.Addr); i++ { |
| 97 | + raw6.Addr[i] = sa.Addr[i] |
| 98 | + } |
| 99 | + return raw, socklen(unsafe.Sizeof(*raw6)), nil |
| 100 | + case *windows.SockaddrUnix: |
| 101 | + return nil, 0, syscall.EWINDOWS |
| 102 | + } |
| 103 | + return nil, 0, syscall.EAFNOSUPPORT |
| 104 | +} |
| 105 | + |
| 106 | +// from: go/src/pkg/net/ipsock.go |
| 107 | + |
| 108 | +// ip6ZoneToString converts an IP6 Zone unix int to a net string |
| 109 | +// returns "" if zone is 0 |
| 110 | +func ip6ZoneToString(zone int) string { |
| 111 | + if zone == 0 { |
| 112 | + return "" |
| 113 | + } |
| 114 | + if ifi, err := net.InterfaceByIndex(zone); err == nil { |
| 115 | + return ifi.Name |
| 116 | + } |
| 117 | + return strconv.Itoa(zone) |
| 118 | +} |
| 119 | + |
| 120 | +// ip6ZoneToInt converts an IP6 Zone net string to a unix int |
| 121 | +// returns 0 if zone is "" |
| 122 | +func ip6ZoneToInt(zone string) int { |
| 123 | + if zone == "" { |
| 124 | + return 0 |
| 125 | + } |
| 126 | + if ifi, err := net.InterfaceByName(zone); err == nil { |
| 127 | + return ifi.Index |
| 128 | + } |
| 129 | + n, _ := strconv.ParseInt(zone, 10, 32) |
| 130 | + return int(n) |
| 131 | +} |
0 commit comments