os: Use GetComputerNameEx to get Hostname on win32

The existing Hostname function uses the GetComputerName system
function in windows to determine the hostname. It has some downsides:

  - The name is limited to 15 characters.
  - The name returned is for NetBIOS, other OS's return a DNS name

This change adds to the internal/syscall/windows package a
GetComputerNameEx function, and related enum constants. They are used
instead of the syscall.ComputerName function to implement os.Hostname
on windows.

Fixes #9982

Change-Id: Idc8782785eb1eea37e64022bd201699ce9c4b39c
Reviewed-on: https://go-review.googlesource.com/5852
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
Run-TryBot: Alex Brainman <alex.brainman@gmail.com>
Reviewed-by: Carlos Castillo <cookieo9@gmail.com>
Reviewed-by: Yasuhiro MATSUMOTO <mattn.jp@gmail.com>
diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go
index c816ff7..a9b4988 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -1140,6 +1140,7 @@
 	"encoding/base64",
 	"syscall",
 	"time",
+	"internal/syscall/windows",
 	"os",
 	"reflect",
 	"fmt",
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index d186a17..7076f43 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -123,7 +123,7 @@
 	// Operating system access.
 	"syscall":       {"L0", "unicode/utf16"},
 	"time":          {"L0", "syscall"},
-	"os":            {"L1", "os", "syscall", "time"},
+	"os":            {"L1", "os", "syscall", "time", "internal/syscall/windows"},
 	"path/filepath": {"L2", "os", "syscall"},
 	"io/ioutil":     {"L2", "os", "path/filepath", "time"},
 	"os/exec":       {"L2", "os", "path/filepath", "syscall"},
diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go
index 2541a83..49bfeea 100644
--- a/src/internal/syscall/windows/syscall_windows.go
+++ b/src/internal/syscall/windows/syscall_windows.go
@@ -4,9 +4,7 @@
 
 package windows
 
-import (
-	"syscall"
-)
+import "syscall"
 
 //go:generate go run ../../../syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go
 
@@ -97,3 +95,17 @@
 )
 
 //sys GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizeOfPointer *uint32) (errcode error) = iphlpapi.GetAdaptersAddresses
+
+//sys	GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW
+
+const (
+	ComputerNameNetBIOS                   = 0
+	ComputerNameDnsHostname               = 1
+	ComputerNameDnsDomain                 = 2
+	ComputerNameDnsFullyQualified         = 3
+	ComputerNamePhysicalNetBIOS           = 4
+	ComputerNamePhysicalDnsHostname       = 5
+	ComputerNamePhysicalDnsDomain         = 6
+	ComputerNamePhysicalDnsFullyQualified = 7
+	ComputerNameMax                       = 8
+)
diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go
index 90e2034..50c7c51 100644
--- a/src/internal/syscall/windows/zsyscall_windows.go
+++ b/src/internal/syscall/windows/zsyscall_windows.go
@@ -5,10 +5,14 @@
 import "unsafe"
 import "syscall"
 
+var _ unsafe.Pointer
+
 var (
 	modiphlpapi = syscall.NewLazyDLL("iphlpapi.dll")
+	modkernel32 = syscall.NewLazyDLL("kernel32.dll")
 
 	procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
+	procGetComputerNameExW   = modkernel32.NewProc("GetComputerNameExW")
 )
 
 func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizeOfPointer *uint32) (errcode error) {
@@ -18,3 +22,15 @@
 	}
 	return
 }
+
+func GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) {
+	r1, _, e1 := syscall.Syscall(procGetComputerNameExW.Addr(), 3, uintptr(nameformat), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n)))
+	if r1 == 0 {
+		if e1 != 0 {
+			err = error(e1)
+		} else {
+			err = syscall.EINVAL
+		}
+	}
+	return
+}
diff --git a/src/os/sys_windows.go b/src/os/sys_windows.go
index 92617de..9490ea6 100644
--- a/src/os/sys_windows.go
+++ b/src/os/sys_windows.go
@@ -4,12 +4,30 @@
 
 package os
 
-import "syscall"
+import (
+	"internal/syscall/windows"
+	"syscall"
+)
 
 func hostname() (name string, err error) {
-	s, e := syscall.ComputerName()
-	if e != nil {
-		return "", NewSyscallError("ComputerName", e)
+	// Use PhysicalDnsHostname to uniquely identify host in a cluster
+	const format = windows.ComputerNamePhysicalDnsHostname
+
+	n := uint32(64)
+	for {
+		b := make([]uint16, n)
+		err := windows.GetComputerNameEx(format, &b[0], &n)
+		if err == nil {
+			return syscall.UTF16ToString(b[:n]), nil
+		}
+		if err != syscall.ERROR_MORE_DATA {
+			return "", NewSyscallError("ComputerNameEx", err)
+		}
+
+		// If we received a ERROR_MORE_DATA, but n doesn't get larger,
+		// something has gone wrong and we may be in an infinite loop
+		if n <= uint32(len(b)) {
+			return "", NewSyscallError("ComputerNameEx", err)
+		}
 	}
-	return s, nil
 }