author | Mikio Hara
<mikioh.mikioh@gmail.com> 2016-08-24 21:03:21 UTC |
committer | Mikio Hara
<mikioh.mikioh@gmail.com> 2016-08-26 03:27:45 UTC |
parent | 3a1f9ef983bc408afd0a9e63fd9c962ae853e543 |
internal/netreflect/socket.go | +37 | -0 |
internal/netreflect/socket_posix.go | +30 | -0 |
internal/netreflect/socket_stub.go | +11 | -0 |
internal/netreflect/socket_test.go | +122 | -0 |
diff --git a/internal/netreflect/socket.go b/internal/netreflect/socket.go new file mode 100644 index 0000000..e82e51c --- /dev/null +++ b/internal/netreflect/socket.go @@ -0,0 +1,37 @@ +// Copyright 2016 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. + +// Package netreflect implements run-time reflection for the +// facilities of net package. +package netreflect + +import ( + "errors" + "net" +) + +var ( + errInvalidType = errors.New("invalid type") + errOpNoSupport = errors.New("operation not supported") +) + +// SocketOf returns the socket descriptor of c. +func SocketOf(c net.Conn) (uintptr, error) { + switch c.(type) { + case *net.TCPConn, *net.UDPConn, *net.IPConn, *net.UnixConn: + return socketOf(c) + default: + return 0, errInvalidType + } +} + +// PacketSocketOf returns the socket descriptor of c. +func PacketSocketOf(c net.PacketConn) (uintptr, error) { + switch c.(type) { + case *net.UDPConn, *net.IPConn, *net.UnixConn: + return socketOf(c.(net.Conn)) + default: + return 0, errInvalidType + } +} diff --git a/internal/netreflect/socket_posix.go b/internal/netreflect/socket_posix.go new file mode 100644 index 0000000..df475a2 --- /dev/null +++ b/internal/netreflect/socket_posix.go @@ -0,0 +1,30 @@ +// Copyright 2016 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 darwin dragonfly freebsd linux netbsd openbsd solaris windows + +package netreflect + +import ( + "net" + "reflect" + "runtime" +) + +func socketOf(c net.Conn) (uintptr, error) { + v := reflect.ValueOf(c) + switch e := v.Elem(); e.Kind() { + case reflect.Struct: + fd := e.FieldByName("conn").FieldByName("fd") + switch e := fd.Elem(); e.Kind() { + case reflect.Struct: + sysfd := e.FieldByName("sysfd") + if runtime.GOOS == "windows" { + return uintptr(sysfd.Uint()), nil + } + return uintptr(sysfd.Int()), nil + } + } + return 0, errInvalidType +} diff --git a/internal/netreflect/socket_stub.go b/internal/netreflect/socket_stub.go new file mode 100644 index 0000000..85adb4b --- /dev/null +++ b/internal/netreflect/socket_stub.go @@ -0,0 +1,11 @@ +// Copyright 2016 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 !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows + +package netreflect + +import "net" + +func socketOf(c net.Conn) (uintptr, error) { return 0, errOpNoSupport } diff --git a/internal/netreflect/socket_test.go b/internal/netreflect/socket_test.go new file mode 100644 index 0000000..cf814e2 --- /dev/null +++ b/internal/netreflect/socket_test.go @@ -0,0 +1,122 @@ +// Copyright 2016 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. + +package netreflect_test + +import ( + "fmt" + "io/ioutil" + "net" + "os" + "runtime" + "testing" + + "golang.org/x/net/internal/netreflect" +) + +func localPath() string { + f, err := ioutil.TempFile("", "netreflect") + if err != nil { + panic(err) + } + path := f.Name() + f.Close() + os.Remove(path) + return path +} + +func newLocalListener(network string) (net.Listener, error) { + switch network { + case "tcp": + if ln, err := net.Listen("tcp4", "127.0.0.1:0"); err == nil { + return ln, nil + } + return net.Listen("tcp6", "[::1]:0") + case "tcp4": + return net.Listen("tcp4", "127.0.0.1:0") + case "tcp6": + return net.Listen("tcp6", "[::1]:0") + case "unix", "unixpacket": + return net.Listen(network, localPath()) + } + return nil, fmt.Errorf("%s is not supported", network) +} + +func newLocalPacketListener(network string) (net.PacketConn, error) { + switch network { + case "udp": + if c, err := net.ListenPacket("udp4", "127.0.0.1:0"); err == nil { + return c, nil + } + return net.ListenPacket("udp6", "[::1]:0") + case "udp4": + return net.ListenPacket("udp4", "127.0.0.1:0") + case "udp6": + return net.ListenPacket("udp6", "[::1]:0") + case "unixgram": + return net.ListenPacket(network, localPath()) + } + return nil, fmt.Errorf("%s is not supported", network) +} + +func TestSocketOf(t *testing.T) { + for _, network := range []string{"tcp", "unix", "unixpacket"} { + switch network { + case "unix": + switch runtime.GOOS { + case "nacl", "plan9", "windows": + continue + } + case "unixpacket": + switch runtime.GOOS { + case "darwin", "nacl", "plan9", "windows": + continue + } + } + ln, err := newLocalListener(network) + if err != nil { + t.Error(err) + continue + } + defer func() { + path := ln.Addr().String() + ln.Close() + if network == "unix" || network == "unixpacket" { + os.Remove(path) + } + }() + c, err := net.Dial(ln.Addr().Network(), ln.Addr().String()) + if err != nil { + t.Error(err) + continue + } + defer c.Close() + if _, err := netreflect.SocketOf(c); err != nil { + t.Error(err) + continue + } + } +} + +func TestPacketSocketOf(t *testing.T) { + for _, network := range []string{"udp", "unixgram"} { + switch network { + case "unixgram": + switch runtime.GOOS { + case "nacl", "plan9", "windows": + continue + } + } + c, err := newLocalPacketListener(network) + if err != nil { + t.Error(err) + continue + } + defer c.Close() + if _, err := netreflect.PacketSocketOf(c); err != nil { + t.Error(err) + continue + } + } +}