diff options
author | Yasuhiro Matsumoto <mattn.jp@gmail.com> | 2018-07-23 01:47:11 +0900 |
---|---|---|
committer | Alex Brainman <alex.brainman@gmail.com> | 2018-08-28 09:26:45 +0000 |
commit | 76c45877c9e72ccc84db787dc08299e0182e0efb (patch) | |
tree | ce7f3169335049a4918df6a5b465a0ea1d727721 | |
parent | ded941158042d8b09164a9f8049fd7108b715680 (diff) | |
download | go-76c45877c9e72ccc84db787dc08299e0182e0efb.tar.gz go-76c45877c9e72ccc84db787dc08299e0182e0efb.zip |
syscall: implement Unix Socket for Windows
Add implementation of AF_UNIX. This works only on Windows 10.
https://blogs.msdn.microsoft.com/commandline/2017/12/19/af_unix-comes-to-windows/
Fixes #26072
Change-Id: I76a96a472385a17901885271622fbe55d66bb720
Reviewed-on: https://go-review.googlesource.com/125456
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
-rw-r--r-- | api/except.txt | 2 | ||||
-rw-r--r-- | src/net/unixsock_windows_test.go | 93 | ||||
-rw-r--r-- | src/syscall/syscall_windows.go | 58 | ||||
-rw-r--r-- | src/syscall/types_windows.go | 2 |
4 files changed, 151 insertions, 4 deletions
diff --git a/api/except.txt b/api/except.txt index 46dbb45892..850724196d 100644 --- a/api/except.txt +++ b/api/except.txt @@ -370,6 +370,7 @@ pkg syscall (windows-386), type CertContext struct, CertInfo uintptr pkg syscall (windows-386), type CertRevocationInfo struct, CrlInfo uintptr pkg syscall (windows-386), type CertRevocationInfo struct, OidSpecificInfo uintptr pkg syscall (windows-386), type CertSimpleChain struct, TrustListInfo uintptr +pkg syscall (windows-386), type RawSockaddrAny struct, Pad [96]int8 pkg syscall (windows-amd64), const TOKEN_ALL_ACCESS = 983295 pkg syscall (windows-amd64), type AddrinfoW struct, Addr uintptr pkg syscall (windows-amd64), type CertChainPolicyPara struct, ExtraPolicyPara uintptr @@ -378,3 +379,4 @@ pkg syscall (windows-amd64), type CertContext struct, CertInfo uintptr pkg syscall (windows-amd64), type CertRevocationInfo struct, CrlInfo uintptr pkg syscall (windows-amd64), type CertRevocationInfo struct, OidSpecificInfo uintptr pkg syscall (windows-amd64), type CertSimpleChain struct, TrustListInfo uintptr +pkg syscall (windows-amd64), type RawSockaddrAny struct, Pad [96]int8 diff --git a/src/net/unixsock_windows_test.go b/src/net/unixsock_windows_test.go new file mode 100644 index 0000000000..a1da5d4062 --- /dev/null +++ b/src/net/unixsock_windows_test.go @@ -0,0 +1,93 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package net + +import ( + "internal/syscall/windows/registry" + "os" + "reflect" + "strconv" + "testing" +) + +func isBuild17063() bool { + k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.READ) + if err != nil { + return false + } + defer k.Close() + + s, _, err := k.GetStringValue("CurrentBuild") + if err != nil { + return false + } + ver, err := strconv.Atoi(s) + if err != nil { + return false + } + return ver >= 17063 +} + +func TestUnixConnLocalWindows(t *testing.T) { + if !isBuild17063() { + t.Skip("unix test") + } + + handler := func(ls *localServer, ln Listener) {} + for _, laddr := range []string{"", testUnixAddr()} { + laddr := laddr + taddr := testUnixAddr() + ta, err := ResolveUnixAddr("unix", taddr) + if err != nil { + t.Fatal(err) + } + ln, err := ListenUnix("unix", ta) + if err != nil { + t.Fatal(err) + } + ls, err := (&streamListener{Listener: ln}).newLocalServer() + if err != nil { + t.Fatal(err) + } + defer ls.teardown() + if err := ls.buildup(handler); err != nil { + t.Fatal(err) + } + + la, err := ResolveUnixAddr("unix", laddr) + if err != nil { + t.Fatal(err) + } + c, err := DialUnix("unix", la, ta) + if err != nil { + t.Fatal(err) + } + defer func() { + c.Close() + if la != nil { + defer os.Remove(laddr) + } + }() + if _, err := c.Write([]byte("UNIXCONN LOCAL AND REMOTE NAME TEST")); err != nil { + t.Fatal(err) + } + + if laddr == "" { + laddr = "@" + } + var connAddrs = [3]struct{ got, want Addr }{ + {ln.Addr(), ta}, + {c.LocalAddr(), &UnixAddr{Name: laddr, Net: "unix"}}, + {c.RemoteAddr(), ta}, + } + for _, ca := range connAddrs { + if !reflect.DeepEqual(ca.got, ca.want) { + t.Fatalf("got %#v, expected %#v", ca.got, ca.want) + } + } + } +} diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go index 638a81882a..528ef4f26d 100644 --- a/src/syscall/syscall_windows.go +++ b/src/syscall/syscall_windows.go @@ -634,7 +634,7 @@ type RawSockaddr struct { type RawSockaddrAny struct { Addr RawSockaddr - Pad [96]int8 + Pad [100]int8 } type Sockaddr interface { @@ -683,19 +683,69 @@ func (sa *SockaddrInet6) sockaddr() (unsafe.Pointer, int32, error) { return unsafe.Pointer(&sa.raw), int32(unsafe.Sizeof(sa.raw)), nil } +type RawSockaddrUnix struct { + Family uint16 + Path [UNIX_PATH_MAX]int8 +} + type SockaddrUnix struct { Name string + raw RawSockaddrUnix } func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, int32, error) { - // TODO(brainman): implement SockaddrUnix.sockaddr() - return nil, 0, EWINDOWS + name := sa.Name + n := len(name) + if n > len(sa.raw.Path) { + return nil, 0, EINVAL + } + if n == len(sa.raw.Path) && name[0] != '@' { + return nil, 0, EINVAL + } + sa.raw.Family = AF_UNIX + for i := 0; i < n; i++ { + sa.raw.Path[i] = int8(name[i]) + } + // length is family (uint16), name, NUL. + sl := int32(2) + if n > 0 { + sl += int32(n) + 1 + } + if sa.raw.Path[0] == '@' { + sa.raw.Path[0] = 0 + // Don't count trailing NUL for abstract address. + sl-- + } + + return unsafe.Pointer(&sa.raw), sl, nil } func (rsa *RawSockaddrAny) Sockaddr() (Sockaddr, error) { switch rsa.Addr.Family { case AF_UNIX: - return nil, EWINDOWS + pp := (*RawSockaddrUnix)(unsafe.Pointer(rsa)) + sa := new(SockaddrUnix) + if pp.Path[0] == 0 { + // "Abstract" Unix domain socket. + // Rewrite leading NUL as @ for textual display. + // (This is the standard convention.) + // Not friendly to overwrite in place, + // but the callers below don't care. + pp.Path[0] = '@' + } + + // Assume path ends at NUL. + // This is not technically the Linux semantics for + // abstract Unix domain sockets--they are supposed + // to be uninterpreted fixed-size binary blobs--but + // everyone uses this convention. + n := 0 + for n < len(pp.Path) && pp.Path[n] != 0 { + n++ + } + bytes := (*[10000]byte)(unsafe.Pointer(&pp.Path[0]))[0:n] + sa.Name = string(bytes) + return sa, nil case AF_INET: pp := (*RawSockaddrInet4)(unsafe.Pointer(rsa)) diff --git a/src/syscall/types_windows.go b/src/syscall/types_windows.go index 6911fe509c..0b839339d2 100644 --- a/src/syscall/types_windows.go +++ b/src/syscall/types_windows.go @@ -1139,3 +1139,5 @@ const ( SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1 _SYMLINK_FLAG_RELATIVE = 1 ) + +const UNIX_PATH_MAX = 108 // defined in afunix.h |