From eb2e6e59ee4154d0cfa017d1c1a84c52ed2f624b Mon Sep 17 00:00:00 2001 From: Alex Brainman Date: Wed, 7 Nov 2012 16:58:20 +1100 Subject: [PATCH] net: implement IPv6 support for windows Thank you zhoumichaely for original CL 5175042. Fixes #1740. Fixes #2315. R=golang-dev, bradfitz, mikioh.mikioh CC=golang-dev, zhoumichaely https://golang.org/cl/6822045 --- src/pkg/net/dialgoogle_test.go | 7 +++- src/pkg/net/fd_unix.go | 3 ++ src/pkg/net/fd_windows.go | 5 ++- src/pkg/net/ipsock.go | 7 +++- src/pkg/net/ipsock_plan9.go | 3 ++ src/pkg/net/lookup_windows.go | 35 ++++++++++++++++++-- src/pkg/syscall/syscall_windows.go | 39 +++++++++++++++++++++-- src/pkg/syscall/zsyscall_windows_386.go | 15 +++++++++ src/pkg/syscall/zsyscall_windows_amd64.go | 15 +++++++++ src/pkg/syscall/ztypes_windows.go | 17 ++++++++++ 10 files changed, 138 insertions(+), 8 deletions(-) diff --git a/src/pkg/net/dialgoogle_test.go b/src/pkg/net/dialgoogle_test.go index 426e2ffb00..dd3c4ba7e1 100644 --- a/src/pkg/net/dialgoogle_test.go +++ b/src/pkg/net/dialgoogle_test.go @@ -116,7 +116,12 @@ func TestDialGoogleIPv6(t *testing.T) { return } // Only run tcp6 if the kernel will take it. - if !*testIPv6 || !supportsIPv6 { + if !supportsIPv6 { + t.Logf("skipping test; ipv6 is not supported") + return + } + if !*testIPv6 { + t.Logf("test disabled; use -ipv6 to enable") return } diff --git a/src/pkg/net/fd_unix.go b/src/pkg/net/fd_unix.go index ee82ead026..e1d1256fa1 100644 --- a/src/pkg/net/fd_unix.go +++ b/src/pkg/net/fd_unix.go @@ -262,6 +262,9 @@ var startServersOnce []func() var canCancelIO = true // used for testing current package +func sysInit() { +} + func init() { pollMaxN = runtime.NumCPU() if pollMaxN > 8 { diff --git a/src/pkg/net/fd_windows.go b/src/pkg/net/fd_windows.go index 5f43125892..5338def922 100644 --- a/src/pkg/net/fd_windows.go +++ b/src/pkg/net/fd_windows.go @@ -29,13 +29,16 @@ var initErr error var canCancelIO bool // determines if CancelIoEx API is present -func init() { +func sysInit() { var d syscall.WSAData e := syscall.WSAStartup(uint32(0x202), &d) if e != nil { initErr = os.NewSyscallError("WSAStartup", e) } canCancelIO = syscall.LoadCancelIoEx() == nil + if syscall.LoadGetAddrInfo() == nil { + lookupIP = newLookupIP + } } func closesocket(s syscall.Handle) error { diff --git a/src/pkg/net/ipsock.go b/src/pkg/net/ipsock.go index 84547c7a6a..b9b2a9b81e 100644 --- a/src/pkg/net/ipsock.go +++ b/src/pkg/net/ipsock.go @@ -6,7 +6,12 @@ package net -var supportsIPv6, supportsIPv4map = probeIPv6Stack() +var supportsIPv6, supportsIPv4map bool + +func init() { + sysInit() + supportsIPv6, supportsIPv4map = probeIPv6Stack() +} func firstFavoriteAddr(filter func(IP) IP, addrs []string) (addr IP) { if filter == nil { diff --git a/src/pkg/net/ipsock_plan9.go b/src/pkg/net/ipsock_plan9.go index f5be54cb2a..4111acfc2e 100644 --- a/src/pkg/net/ipsock_plan9.go +++ b/src/pkg/net/ipsock_plan9.go @@ -26,6 +26,9 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { var canCancelIO = true // used for testing current package +func sysInit() { +} + // parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80). func parsePlan9Addr(s string) (ip IP, iport int, err error) { addr := IPv4zero // address contains port only diff --git a/src/pkg/net/lookup_windows.go b/src/pkg/net/lookup_windows.go index 2a8d01ff40..390fe7f440 100644 --- a/src/pkg/net/lookup_windows.go +++ b/src/pkg/net/lookup_windows.go @@ -40,7 +40,9 @@ func lookupHost(name string) (addrs []string, err error) { return } -func lookupIP(name string) (addrs []IP, err error) { +var lookupIP = oldLookupIP + +func oldLookupIP(name string) (addrs []IP, err error) { hostentLock.Lock() defer hostentLock.Unlock() h, err := syscall.GetHostByName(name) @@ -56,7 +58,36 @@ func lookupIP(name string) (addrs []IP, err error) { } addrs = addrs[0:i] default: // TODO(vcc): Implement non IPv4 address lookups. - return nil, os.NewSyscallError("LookupHost", syscall.EWINDOWS) + return nil, os.NewSyscallError("LookupIP", syscall.EWINDOWS) + } + return addrs, nil +} + +func newLookupIP(name string) (addrs []IP, err error) { + hints := syscall.AddrinfoW{ + Family: syscall.AF_UNSPEC, + Socktype: syscall.SOCK_STREAM, + Protocol: syscall.IPPROTO_IP, + } + var result *syscall.AddrinfoW + e := syscall.GetAddrInfoW(syscall.StringToUTF16Ptr(name), nil, &hints, &result) + if e != nil { + return nil, os.NewSyscallError("GetAddrInfoW", e) + } + defer syscall.FreeAddrInfoW(result) + addrs = make([]IP, 0, 5) + for ; result != nil; result = result.Next { + addr := unsafe.Pointer(result.Addr) + switch result.Family { + case syscall.AF_INET: + a := (*syscall.RawSockaddrInet4)(addr).Addr + addrs = append(addrs, IPv4(a[0], a[1], a[2], a[3])) + case syscall.AF_INET6: + a := (*syscall.RawSockaddrInet6)(addr).Addr + addrs = append(addrs, IP{a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]}) + default: + return nil, os.NewSyscallError("LookupIP", syscall.EWINDOWS) + } } return addrs, nil } diff --git a/src/pkg/syscall/syscall_windows.go b/src/pkg/syscall/syscall_windows.go index 535bd55466..9fe2da385a 100644 --- a/src/pkg/syscall/syscall_windows.go +++ b/src/pkg/syscall/syscall_windows.go @@ -508,6 +508,8 @@ const socket_error = uintptr(^uint32(0)) //sys GetProtoByName(name string) (p *Protoent, err error) [failretval==nil] = ws2_32.getprotobyname //sys DnsQuery(name string, qtype uint16, options uint32, extra *byte, qrs **DNSRecord, pr *byte) (status error) = dnsapi.DnsQuery_W //sys DnsRecordListFree(rl *DNSRecord, freetype uint32) = dnsapi.DnsRecordListFree +//sys GetAddrInfoW(nodename *uint16, servicename *uint16, hints *AddrinfoW, result **AddrinfoW) (sockerr error) = ws2_32.GetAddrInfoW +//sys FreeAddrInfoW(addrinfo *AddrinfoW) = ws2_32.FreeAddrInfoW //sys GetIfEntry(pIfRow *MibIfRow) (errcode error) = iphlpapi.GetIfEntry //sys GetAdaptersInfo(ai *IpAdapterInfo, ol *uint32) (errcode error) = iphlpapi.GetAdaptersInfo @@ -522,6 +524,14 @@ type RawSockaddrInet4 struct { Zero [8]uint8 } +type RawSockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + type RawSockaddr struct { Family uint16 Data [14]int8 @@ -560,11 +570,22 @@ type SockaddrInet6 struct { Port int ZoneId uint32 Addr [16]byte + raw RawSockaddrInet6 } func (sa *SockaddrInet6) sockaddr() (uintptr, int32, error) { - // TODO(brainman): implement SockaddrInet6.sockaddr() - return 0, 0, EWINDOWS + if sa.Port < 0 || sa.Port > 0xFFFF { + return 0, 0, EINVAL + } + sa.raw.Family = AF_INET6 + p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port)) + p[0] = byte(sa.Port >> 8) + p[1] = byte(sa.Port) + sa.raw.Scope_id = sa.ZoneId + for i := 0; i < len(sa.Addr); i++ { + sa.raw.Addr[i] = sa.Addr[i] + } + return uintptr(unsafe.Pointer(&sa.raw)), int32(unsafe.Sizeof(sa.raw)), nil } type SockaddrUnix struct { @@ -592,7 +613,15 @@ func (rsa *RawSockaddrAny) Sockaddr() (Sockaddr, error) { return sa, nil case AF_INET6: - return nil, EWINDOWS + pp := (*RawSockaddrInet6)(unsafe.Pointer(rsa)) + sa := new(SockaddrInet6) + p := (*[2]byte)(unsafe.Pointer(&pp.Port)) + sa.Port = int(p[0])<<8 + int(p[1]) + sa.ZoneId = pp.Scope_id + for i := 0; i < len(sa.Addr); i++ { + sa.Addr[i] = pp.Addr[i] + } + return sa, nil } return nil, EAFNOSUPPORT } @@ -659,6 +688,10 @@ func WSASendto(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32 return WSASendTo(s, bufs, bufcnt, sent, flags, (*RawSockaddrAny)(unsafe.Pointer(rsa)), l, overlapped, croutine) } +func LoadGetAddrInfo() error { + return procGetAddrInfoW.Find() +} + // Invented structures to support what package os expects. type Rusage struct { CreationTime Filetime diff --git a/src/pkg/syscall/zsyscall_windows_386.go b/src/pkg/syscall/zsyscall_windows_386.go index debe3cd596..c90cdfc065 100644 --- a/src/pkg/syscall/zsyscall_windows_386.go +++ b/src/pkg/syscall/zsyscall_windows_386.go @@ -132,6 +132,8 @@ var ( procgetprotobyname = modws2_32.NewProc("getprotobyname") procDnsQuery_W = moddnsapi.NewProc("DnsQuery_W") procDnsRecordListFree = moddnsapi.NewProc("DnsRecordListFree") + procGetAddrInfoW = modws2_32.NewProc("GetAddrInfoW") + procFreeAddrInfoW = modws2_32.NewProc("FreeAddrInfoW") procGetIfEntry = modiphlpapi.NewProc("GetIfEntry") procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo") procTranslateNameW = modsecur32.NewProc("TranslateNameW") @@ -1537,6 +1539,19 @@ func DnsRecordListFree(rl *DNSRecord, freetype uint32) { return } +func GetAddrInfoW(nodename *uint16, servicename *uint16, hints *AddrinfoW, result **AddrinfoW) (sockerr error) { + r0, _, _ := Syscall6(procGetAddrInfoW.Addr(), 4, uintptr(unsafe.Pointer(nodename)), uintptr(unsafe.Pointer(servicename)), uintptr(unsafe.Pointer(hints)), uintptr(unsafe.Pointer(result)), 0, 0) + if r0 != 0 { + sockerr = Errno(r0) + } + return +} + +func FreeAddrInfoW(addrinfo *AddrinfoW) { + Syscall(procFreeAddrInfoW.Addr(), 1, uintptr(unsafe.Pointer(addrinfo)), 0, 0) + return +} + func GetIfEntry(pIfRow *MibIfRow) (errcode error) { r0, _, _ := Syscall(procGetIfEntry.Addr(), 1, uintptr(unsafe.Pointer(pIfRow)), 0, 0) if r0 != 0 { diff --git a/src/pkg/syscall/zsyscall_windows_amd64.go b/src/pkg/syscall/zsyscall_windows_amd64.go index 5a7e74c645..105fdda584 100644 --- a/src/pkg/syscall/zsyscall_windows_amd64.go +++ b/src/pkg/syscall/zsyscall_windows_amd64.go @@ -132,6 +132,8 @@ var ( procgetprotobyname = modws2_32.NewProc("getprotobyname") procDnsQuery_W = moddnsapi.NewProc("DnsQuery_W") procDnsRecordListFree = moddnsapi.NewProc("DnsRecordListFree") + procGetAddrInfoW = modws2_32.NewProc("GetAddrInfoW") + procFreeAddrInfoW = modws2_32.NewProc("FreeAddrInfoW") procGetIfEntry = modiphlpapi.NewProc("GetIfEntry") procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo") procTranslateNameW = modsecur32.NewProc("TranslateNameW") @@ -1537,6 +1539,19 @@ func DnsRecordListFree(rl *DNSRecord, freetype uint32) { return } +func GetAddrInfoW(nodename *uint16, servicename *uint16, hints *AddrinfoW, result **AddrinfoW) (sockerr error) { + r0, _, _ := Syscall6(procGetAddrInfoW.Addr(), 4, uintptr(unsafe.Pointer(nodename)), uintptr(unsafe.Pointer(servicename)), uintptr(unsafe.Pointer(hints)), uintptr(unsafe.Pointer(result)), 0, 0) + if r0 != 0 { + sockerr = Errno(r0) + } + return +} + +func FreeAddrInfoW(addrinfo *AddrinfoW) { + Syscall(procFreeAddrInfoW.Addr(), 1, uintptr(unsafe.Pointer(addrinfo)), 0, 0) + return +} + func GetIfEntry(pIfRow *MibIfRow) (errcode error) { r0, _, _ := Syscall(procGetIfEntry.Addr(), 1, uintptr(unsafe.Pointer(pIfRow)), 0, 0) if r0 != 0 { diff --git a/src/pkg/syscall/ztypes_windows.go b/src/pkg/syscall/ztypes_windows.go index 9827e129c0..1f7308796f 100644 --- a/src/pkg/syscall/ztypes_windows.go +++ b/src/pkg/syscall/ztypes_windows.go @@ -924,3 +924,20 @@ const ( REG_DWORD = REG_DWORD_LITTLE_ENDIAN REG_QWORD = REG_QWORD_LITTLE_ENDIAN ) + +type AddrinfoW struct { + Flags int32 + Family int32 + Socktype int32 + Protocol int32 + Addrlen uintptr + Canonname *uint16 + Addr uintptr + Next *AddrinfoW +} + +const ( + AI_PASSIVE = 1 + AI_CANONNAME = 2 + AI_NUMERICHOST = 4 +) -- 2.48.1