From 21704d17decfcbb7114c48d73479faecd803d934 Mon Sep 17 00:00:00 2001 From: Marat Radchenko Date: Mon, 24 Jan 2022 01:27:05 +0300 Subject: [PATCH] Implement AF_UNIX sockets on Windows See https://github.com/moby/moby/issues/36442 Signed-off-by: Marat Radchenko --- go.mod | 5 +---- go.sum | 14 ------------ sockets/sockets.go | 25 +++++++++++++++++++++ sockets/sockets_unix.go | 23 +------------------ sockets/sockets_windows.go | 11 +++------- sockets/unix_socket.go | 6 ++--- sockets/unix_socket_test.go | 23 +------------------ sockets/unix_socket_test_unix.go | 34 +++++++++++++++++++++++++++++ sockets/unix_socket_test_windows.go | 25 +++++++++++++++++++++ sockets/unix_socket_unix.go | 10 +++++++++ sockets/unix_socket_windows.go | 5 +++++ 11 files changed, 107 insertions(+), 74 deletions(-) create mode 100644 sockets/unix_socket_test_unix.go create mode 100644 sockets/unix_socket_test_windows.go create mode 100644 sockets/unix_socket_unix.go create mode 100644 sockets/unix_socket_windows.go diff --git a/go.mod b/go.mod index 70ec3128f..6b9a9e183 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,4 @@ module github.com/docker/go-connections go 1.13 -require ( - github.com/Microsoft/go-winio v0.4.14 - github.com/pkg/errors v0.9.1 -) +require github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 2bcfb4ffb..7c401c3f5 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,2 @@ -github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/sockets/sockets.go b/sockets/sockets.go index 2e9e9006f..5bd3ca4f3 100644 --- a/sockets/sockets.go +++ b/sockets/sockets.go @@ -2,8 +2,13 @@ package sockets import ( + "context" "errors" + "fmt" + "net" "net/http" + "syscall" + "time" ) // ErrProtocolNotAvailable is returned when a given transport protocol is not provided by the operating system. @@ -24,3 +29,23 @@ func ConfigureTransport(tr *http.Transport, proto, addr string) error { } return nil } + +const ( + defaultTimeout = 10 * time.Second + maxUnixSocketPathSize = len(syscall.RawSockaddrUnix{}.Path) +) + +func configureUnixTransport(tr *http.Transport, proto, addr string) error { + if len(addr) > maxUnixSocketPathSize { + return fmt.Errorf("Unix socket path %q is too long", addr) + } + // No need for compression in local communications. + tr.DisableCompression = true + dialer := &net.Dialer{ + Timeout: defaultTimeout, + } + tr.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) { + return dialer.DialContext(ctx, proto, addr) + } + return nil +} diff --git a/sockets/sockets_unix.go b/sockets/sockets_unix.go index 10d763426..f5c7f0594 100644 --- a/sockets/sockets_unix.go +++ b/sockets/sockets_unix.go @@ -1,36 +1,15 @@ +//go:build !windows // +build !windows package sockets import ( - "context" - "fmt" "net" "net/http" "syscall" "time" ) -const ( - defaultTimeout = 10 * time.Second - maxUnixSocketPathSize = len(syscall.RawSockaddrUnix{}.Path) -) - -func configureUnixTransport(tr *http.Transport, proto, addr string) error { - if len(addr) > maxUnixSocketPathSize { - return fmt.Errorf("Unix socket path %q is too long", addr) - } - // No need for compression in local communications. - tr.DisableCompression = true - dialer := &net.Dialer{ - Timeout: defaultTimeout, - } - tr.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) { - return dialer.DialContext(ctx, proto, addr) - } - return nil -} - func configureNpipeTransport(tr *http.Transport, proto, addr string) error { return ErrProtocolNotAvailable } diff --git a/sockets/sockets_windows.go b/sockets/sockets_windows.go index 7acafc5a2..4199f032a 100644 --- a/sockets/sockets_windows.go +++ b/sockets/sockets_windows.go @@ -5,24 +5,19 @@ import ( "net" "net/http" "time" - - "github.com/Microsoft/go-winio" ) -func configureUnixTransport(tr *http.Transport, proto, addr string) error { - return ErrProtocolNotAvailable -} - func configureNpipeTransport(tr *http.Transport, proto, addr string) error { // No need for compression in local communications. tr.DisableCompression = true tr.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) { - return winio.DialPipeContext(ctx, addr) + var d net.Dialer + return d.DialContext(ctx, proto, addr) } return nil } // DialPipe connects to a Windows named pipe. func DialPipe(addr string, timeout time.Duration) (net.Conn, error) { - return winio.DialPipe(addr, &timeout) + return net.DialTimeout("npipe", addr, timeout) } diff --git a/sockets/unix_socket.go b/sockets/unix_socket.go index e7591e6ed..9799a26cc 100644 --- a/sockets/unix_socket.go +++ b/sockets/unix_socket.go @@ -1,5 +1,3 @@ -// +build !windows - /* Package sockets is a simple unix domain socket wrapper. @@ -103,9 +101,9 @@ func NewUnixSocketWithOpts(path string, opts ...SockOption) (net.Listener, error // We don't use "defer" here, to reset the umask to its original value as soon // as possible. Ideally we'd be able to detect if WithChmod() was passed as // an option, and skip changing umask if default permissions are used. - origUmask := syscall.Umask(0777) + origUmask := umask(0777) l, err := net.Listen("unix", path) - syscall.Umask(origUmask) + umask(origUmask) if err != nil { return nil, err } diff --git a/sockets/unix_socket_test.go b/sockets/unix_socket_test.go index 8957efd32..0784a971a 100644 --- a/sockets/unix_socket_test.go +++ b/sockets/unix_socket_test.go @@ -1,12 +1,9 @@ -// +build !windows - package sockets import ( "fmt" "net" "os" - "syscall" "testing" ) @@ -52,26 +49,8 @@ func TestNewUnixSocket(t *testing.T) { } func TestUnixSocketWithOpts(t *testing.T) { - uid, gid := os.Getuid(), os.Getgid() - perms := os.FileMode(0660) - path := "/tmp/test.sock" echoStr := "hello" - l, err := NewUnixSocketWithOpts(path, WithChown(uid, gid), WithChmod(perms)) - if err != nil { - t.Fatal(err) - } + l, path := createTestUnixSocket(t) defer l.Close() - p, err := os.Stat(path) - if err != nil { - t.Fatal(err) - } - if p.Mode().Perm() != perms { - t.Fatalf("unexpected file permissions: expected: %#o, got: %#o", perms, p.Mode().Perm()) - } - if stat, ok := p.Sys().(*syscall.Stat_t); ok { - if stat.Uid != uint32(uid) || stat.Gid != uint32(gid) { - t.Fatalf("unexpected file ownership: expected: %d:%d, got: %d:%d", uid, gid, stat.Uid, stat.Gid) - } - } runTest(t, path, l, echoStr) } diff --git a/sockets/unix_socket_test_unix.go b/sockets/unix_socket_test_unix.go new file mode 100644 index 000000000..909f330c4 --- /dev/null +++ b/sockets/unix_socket_test_unix.go @@ -0,0 +1,34 @@ +//go:build !windows +// +build !windows + +package sockets + +import ( + "os" + "syscall" + "testing" +) + +func createTestUnixSocket(t *testing.T) (listener net.Listener, path string) { + uid, gid := os.Getuid(), os.Getgid() + perms := os.FileMode(0660) + path = "/tmp/test.sock" + echoStr := "hello" + l, err := NewUnixSocketWithOpts(path, WithChown(uid, gid), WithChmod(perms)) + if err != nil { + t.Fatal(err) + } + p, err := os.Stat(path) + if err != nil { + t.Fatal(err) + } + if p.Mode().Perm() != perms { + t.Fatalf("unexpected file permissions: expected: %#o, got: %#o", perms, p.Mode().Perm()) + } + if stat, ok := p.Sys().(*syscall.Stat_t); ok { + if stat.Uid != uint32(uid) || stat.Gid != uint32(gid) { + t.Fatalf("unexpected file ownership: expected: %d:%d, got: %d:%d", uid, gid, stat.Uid, stat.Gid) + } + } + return l, path +} diff --git a/sockets/unix_socket_test_windows.go b/sockets/unix_socket_test_windows.go new file mode 100644 index 000000000..f6d458767 --- /dev/null +++ b/sockets/unix_socket_test_windows.go @@ -0,0 +1,25 @@ +package sockets + +import ( + "io/ioutil" + "net" + "testing" +) + +func testSocketPath() string { + file, err := ioutil.TempFile("", "test*.sock") + if err != nil { + panic(err) + } + defer file.Close() + return file.Name() +} + +func createTestUnixSocket(t *testing.T) (listener net.Listener, path string) { + path = testSocketPath() + l, err := NewUnixSocketWithOpts(path) + if err != nil { + t.Fatal(err) + } + return l, path +} diff --git a/sockets/unix_socket_unix.go b/sockets/unix_socket_unix.go new file mode 100644 index 000000000..0e5b668b2 --- /dev/null +++ b/sockets/unix_socket_unix.go @@ -0,0 +1,10 @@ +//go:build !windows +// +build !windows + +package sockets + +import "syscall" + +func umask(newmask int) (oldmask int) { + return syscall.Umask(0777) +} diff --git a/sockets/unix_socket_windows.go b/sockets/unix_socket_windows.go new file mode 100644 index 000000000..e8b295a64 --- /dev/null +++ b/sockets/unix_socket_windows.go @@ -0,0 +1,5 @@ +package sockets + +func umask(newmask int) (oldmask int) { + return newmask +}