git » go-net » commit 6c89f96

internal/netreflect: new package

author Mikio Hara
2016-08-24 21:03:21 UTC
committer Mikio Hara
2016-08-26 03:27:45 UTC
parent 3a1f9ef983bc408afd0a9e63fd9c962ae853e543

internal/netreflect: new package

This change introduces an internal package that provides the
manipulation of net package facilities by using runtime reflection.
The package is supposed to be used by ipv4 and ipv6 packages.

Change-Id: I73ec3b7d3762e675ca03ad9ee5e8a68e75ceb997
Reviewed-on: https://go-review.googlesource.com/27732
Run-TryBot: Mikio Hara <mikioh.mikioh@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>

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
+		}
+	}
+}