]> Cypherpunks repositories - gostls13.git/commitdiff
net: add IP-level socket option helpers for Unix variants
authorMikio Hara <mikioh.mikioh@gmail.com>
Wed, 11 Jan 2012 00:53:32 +0000 (09:53 +0900)
committerMikio Hara <mikioh.mikioh@gmail.com>
Wed, 11 Jan 2012 00:53:32 +0000 (09:53 +0900)
Also reorganize socket options stuff but there are no API behavioral
changes.

R=rsc, fullung
CC=golang-dev
https://golang.org/cl/5494067

18 files changed:
src/pkg/net/Makefile
src/pkg/net/interface.go
src/pkg/net/multicast_test.go
src/pkg/net/sock.go
src/pkg/net/sockopt.go [new file with mode: 0644]
src/pkg/net/sockopt_bsd.go [moved from src/pkg/net/sock_bsd.go with 96% similarity]
src/pkg/net/sockopt_linux.go [moved from src/pkg/net/sock_linux.go with 95% similarity]
src/pkg/net/sockopt_windows.go [moved from src/pkg/net/sock_windows.go with 96% similarity]
src/pkg/net/sockoptip.go [new file with mode: 0644]
src/pkg/net/sockoptip_bsd.go [new file with mode: 0644]
src/pkg/net/sockoptip_darwin.go [new file with mode: 0644]
src/pkg/net/sockoptip_freebsd.go [new file with mode: 0644]
src/pkg/net/sockoptip_linux.go [new file with mode: 0644]
src/pkg/net/sockoptip_openbsd.go [new file with mode: 0644]
src/pkg/net/sockoptip_windows.go [new file with mode: 0644]
src/pkg/net/udpsock_posix.go
src/pkg/net/unicast_test.go [new file with mode: 0644]
src/pkg/syscall/syscall_windows.go

index 15b733c5a94fd2b3688b148031d8de23ce09cf50..f3d77c744fce18675c11c0a368c8f9e8634d9f25 100644 (file)
@@ -36,7 +36,11 @@ GOFILES_darwin=\
        port.go\
        sendfile_stub.go\
        sock.go\
-       sock_bsd.go\
+       sockopt.go\
+       sockopt_bsd.go\
+       sockoptip.go\
+       sockoptip_bsd.go\
+       sockoptip_darwin.go\
        tcpsock_posix.go\
        udpsock_posix.go\
        unixsock_posix.go\
@@ -64,7 +68,11 @@ GOFILES_freebsd=\
        port.go\
        sendfile_stub.go\
        sock.go\
-       sock_bsd.go\
+       sockopt.go\
+       sockopt_bsd.go\
+       sockoptip.go\
+       sockoptip_bsd.go\
+       sockoptip_freebsd.go\
        tcpsock_posix.go\
        udpsock_posix.go\
        unixsock_posix.go\
@@ -91,7 +99,10 @@ GOFILES_linux=\
        port.go\
        sendfile_linux.go\
        sock.go\
-       sock_linux.go\
+       sockopt.go\
+       sockopt_linux.go\
+       sockoptip.go\
+       sockoptip_linux.go\
        tcpsock_posix.go\
        udpsock_posix.go\
        unixsock_posix.go\
@@ -119,7 +130,11 @@ GOFILES_netbsd=\
        port.go\
        sendfile_stub.go\
        sock.go\
-       sock_bsd.go\
+       sockopt.go\
+       sockopt_bsd.go\
+       sockoptip.go\
+       sockoptip_bsd.go\
+       sockoptip_netbsd.go\
        tcpsock_posix.go\
        udpsock_posix.go\
        unixsock_posix.go\
@@ -140,7 +155,11 @@ GOFILES_openbsd=\
        port.go\
        sendfile_stub.go\
        sock.go\
-       sock_bsd.go\
+       sockopt.go\
+       sockopt_bsd.go\
+       sockoptip.go\
+       sockoptip_bsd.go\
+       sockoptip_openbsd.go\
        tcpsock_posix.go\
        udpsock_posix.go\
        unixsock_posix.go\
@@ -165,7 +184,10 @@ GOFILES_windows=\
        lookup_windows.go\
        sendfile_windows.go\
        sock.go\
-       sock_windows.go\
+       sockopt.go\
+       sockopt_windows.go\
+       sockoptip.go\
+       sockoptip_windows.go\
        tcpsock_posix.go\
        udpsock_posix.go\
        unixsock_posix.go\
index 95486a6301ecb846a3ec9665c40724f99aa1fa21..5e7b352ed50012943a323d6c5847d7d281953e2a 100644 (file)
@@ -12,6 +12,14 @@ import (
        "fmt"
 )
 
+var (
+       errInvalidInterface         = errors.New("net: invalid interface")
+       errInvalidInterfaceIndex    = errors.New("net: invalid interface index")
+       errInvalidInterfaceName     = errors.New("net: invalid interface name")
+       errNoSuchInterface          = errors.New("net: no such interface")
+       errNoSuchMulticastInterface = errors.New("net: no such multicast interface")
+)
+
 // A HardwareAddr represents a physical hardware address.
 type HardwareAddr []byte
 
@@ -131,7 +139,7 @@ func (f Flags) String() string {
 // Addrs returns interface addresses for a specific interface.
 func (ifi *Interface) Addrs() ([]Addr, error) {
        if ifi == nil {
-               return nil, errors.New("net: invalid interface")
+               return nil, errInvalidInterface
        }
        return interfaceAddrTable(ifi.Index)
 }
@@ -140,7 +148,7 @@ func (ifi *Interface) Addrs() ([]Addr, error) {
 // a specific interface.
 func (ifi *Interface) MulticastAddrs() ([]Addr, error) {
        if ifi == nil {
-               return nil, errors.New("net: invalid interface")
+               return nil, errInvalidInterface
        }
        return interfaceMulticastAddrTable(ifi.Index)
 }
@@ -159,7 +167,7 @@ func InterfaceAddrs() ([]Addr, error) {
 // InterfaceByIndex returns the interface specified by index.
 func InterfaceByIndex(index int) (*Interface, error) {
        if index <= 0 {
-               return nil, errors.New("net: invalid interface index")
+               return nil, errInvalidInterfaceIndex
        }
        ift, err := interfaceTable(index)
        if err != nil {
@@ -168,13 +176,13 @@ func InterfaceByIndex(index int) (*Interface, error) {
        for _, ifi := range ift {
                return &ifi, nil
        }
-       return nil, errors.New("net: no such interface")
+       return nil, errNoSuchInterface
 }
 
 // InterfaceByName returns the interface specified by name.
 func InterfaceByName(name string) (*Interface, error) {
        if name == "" {
-               return nil, errors.New("net: invalid interface name")
+               return nil, errInvalidInterfaceName
        }
        ift, err := interfaceTable(0)
        if err != nil {
@@ -185,5 +193,5 @@ func InterfaceByName(name string) (*Interface, error) {
                        return &ifi, nil
                }
        }
-       return nil, errors.New("net: no such interface")
+       return nil, errNoSuchInterface
 }
index a66250c844b43fe6763449b6016e8192c89d98ef..96bac458da7bcd6ffb14b65b2432c9acf7622606 100644 (file)
@@ -13,7 +13,7 @@ import (
 
 var multicast = flag.Bool("multicast", false, "enable multicast tests")
 
-var joinAndLeaveGroupUDPTests = []struct {
+var multicastUDPTests = []struct {
        net   string
        laddr IP
        gaddr IP
@@ -32,8 +32,8 @@ var joinAndLeaveGroupUDPTests = []struct {
        {"udp6", IPv6unspecified, ParseIP("ff0e::114"), (FlagUp | FlagLoopback), true},
 }
 
-func TestJoinAndLeaveGroupUDP(t *testing.T) {
-       if runtime.GOOS == "windows" {
+func TestMulticastUDP(t *testing.T) {
+       if runtime.GOOS == "plan9" || runtime.GOOS == "windows" {
                return
        }
        if !*multicast {
@@ -41,7 +41,7 @@ func TestJoinAndLeaveGroupUDP(t *testing.T) {
                return
        }
 
-       for _, tt := range joinAndLeaveGroupUDPTests {
+       for _, tt := range multicastUDPTests {
                var (
                        ifi   *Interface
                        found bool
@@ -51,7 +51,7 @@ func TestJoinAndLeaveGroupUDP(t *testing.T) {
                }
                ift, err := Interfaces()
                if err != nil {
-                       t.Fatalf("Interfaces() failed: %v", err)
+                       t.Fatalf("Interfaces failed: %v", err)
                }
                for _, x := range ift {
                        if x.Flags&tt.flags == tt.flags {
@@ -65,15 +65,20 @@ func TestJoinAndLeaveGroupUDP(t *testing.T) {
                }
                c, err := ListenUDP(tt.net, &UDPAddr{IP: tt.laddr})
                if err != nil {
-                       t.Fatal(err)
+                       t.Fatalf("ListenUDP failed: %v", err)
                }
                defer c.Close()
                if err := c.JoinGroup(ifi, tt.gaddr); err != nil {
-                       t.Fatal(err)
+                       t.Fatalf("JoinGroup failed: %v", err)
+               }
+               if !tt.ipv6 {
+                       testIPv4MulticastSocketOptions(t, c.fd, ifi)
+               } else {
+                       testIPv6MulticastSocketOptions(t, c.fd, ifi)
                }
                ifmat, err := ifi.MulticastAddrs()
                if err != nil {
-                       t.Fatalf("MulticastAddrs() failed: %v", err)
+                       t.Fatalf("MulticastAddrs failed: %v", err)
                }
                for _, ifma := range ifmat {
                        if ifma.(*IPAddr).IP.Equal(tt.gaddr) {
@@ -85,7 +90,71 @@ func TestJoinAndLeaveGroupUDP(t *testing.T) {
                        t.Fatalf("%q not found in RIB", tt.gaddr.String())
                }
                if err := c.LeaveGroup(ifi, tt.gaddr); err != nil {
-                       t.Fatal(err)
+                       t.Fatalf("LeaveGroup failed: %v", err)
                }
        }
 }
+
+func testIPv4MulticastSocketOptions(t *testing.T, fd *netFD, ifi *Interface) {
+       ifmc, err := ipv4MulticastInterface(fd)
+       if err != nil {
+               t.Fatalf("ipv4MulticastInterface failed: %v", err)
+       }
+       t.Logf("IPv4 multicast interface: %v", ifmc)
+       err = setIPv4MulticastInterface(fd, ifi)
+       if err != nil {
+               t.Fatalf("setIPv4MulticastInterface failed: %v", err)
+       }
+
+       ttl, err := ipv4MulticastTTL(fd)
+       if err != nil {
+               t.Fatalf("ipv4MulticastTTL failed: %v", err)
+       }
+       t.Logf("IPv4 multicast TTL: %v", ttl)
+       err = setIPv4MulticastTTL(fd, 1)
+       if err != nil {
+               t.Fatalf("setIPv4MulticastTTL failed: %v", err)
+       }
+
+       loop, err := ipv4MulticastLoopback(fd)
+       if err != nil {
+               t.Fatalf("ipv4MulticastLoopback failed: %v", err)
+       }
+       t.Logf("IPv4 multicast loopback: %v", loop)
+       err = setIPv4MulticastLoopback(fd, false)
+       if err != nil {
+               t.Fatalf("setIPv4MulticastLoopback failed: %v", err)
+       }
+}
+
+func testIPv6MulticastSocketOptions(t *testing.T, fd *netFD, ifi *Interface) {
+       ifmc, err := ipv6MulticastInterface(fd)
+       if err != nil {
+               t.Fatalf("ipv6MulticastInterface failed: %v", err)
+       }
+       t.Logf("IPv6 multicast interface: %v", ifmc)
+       err = setIPv6MulticastInterface(fd, ifi)
+       if err != nil {
+               t.Fatalf("setIPv6MulticastInterface failed: %v", err)
+       }
+
+       hoplim, err := ipv6MulticastHopLimit(fd)
+       if err != nil {
+               t.Fatalf("ipv6MulticastHopLimit failed: %v", err)
+       }
+       t.Logf("IPv6 multicast hop limit: %v", hoplim)
+       err = setIPv6MulticastHopLimit(fd, 1)
+       if err != nil {
+               t.Fatalf("setIPv6MulticastHopLimit failed: %v", err)
+       }
+
+       loop, err := ipv6MulticastLoopback(fd)
+       if err != nil {
+               t.Fatalf("ipv6MulticastLoopback failed: %v", err)
+       }
+       t.Logf("IPv6 multicast loopback: %v", loop)
+       err = setIPv6MulticastLoopback(fd, false)
+       if err != nil {
+               t.Fatalf("setIPv6MulticastLoopback failed: %v", err)
+       }
+}
index dc073927eb4211ee5b80d2fb690fb4b966181fe6..3ed072047792487d3ad5ea12719b73ca2b20dd9d 100644 (file)
@@ -10,19 +10,10 @@ package net
 
 import (
        "io"
-       "os"
        "reflect"
        "syscall"
 )
 
-// Boolean to int.
-func boolint(b bool) int {
-       if b {
-               return 1
-       }
-       return 0
-}
-
 // Generic socket creation.
 func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
        // See ../syscall/exec.go for description of ForkLock.
@@ -67,83 +58,6 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal
        return fd, nil
 }
 
-func setsockoptInt(fd *netFD, level, opt int, value int) error {
-       return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, level, opt, value))
-}
-
-func setsockoptNsec(fd *netFD, level, opt int, nsec int64) error {
-       var tv = syscall.NsecToTimeval(nsec)
-       return os.NewSyscallError("setsockopt", syscall.SetsockoptTimeval(fd.sysfd, level, opt, &tv))
-}
-
-func setReadBuffer(fd *netFD, bytes int) error {
-       fd.incref()
-       defer fd.decref()
-       return setsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes)
-}
-
-func setWriteBuffer(fd *netFD, bytes int) error {
-       fd.incref()
-       defer fd.decref()
-       return setsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes)
-}
-
-func setReadTimeout(fd *netFD, nsec int64) error {
-       fd.rdeadline_delta = nsec
-       return nil
-}
-
-func setWriteTimeout(fd *netFD, nsec int64) error {
-       fd.wdeadline_delta = nsec
-       return nil
-}
-
-func setTimeout(fd *netFD, nsec int64) error {
-       if e := setReadTimeout(fd, nsec); e != nil {
-               return e
-       }
-       return setWriteTimeout(fd, nsec)
-}
-
-func setReuseAddr(fd *netFD, reuse bool) error {
-       fd.incref()
-       defer fd.decref()
-       return setsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse))
-}
-
-func setDontRoute(fd *netFD, dontroute bool) error {
-       fd.incref()
-       defer fd.decref()
-       return setsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_DONTROUTE, boolint(dontroute))
-}
-
-func setKeepAlive(fd *netFD, keepalive bool) error {
-       fd.incref()
-       defer fd.decref()
-       return setsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive))
-}
-
-func setNoDelay(fd *netFD, noDelay bool) error {
-       fd.incref()
-       defer fd.decref()
-       return setsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(noDelay))
-}
-
-func setLinger(fd *netFD, sec int) error {
-       var l syscall.Linger
-       if sec >= 0 {
-               l.Onoff = 1
-               l.Linger = int32(sec)
-       } else {
-               l.Onoff = 0
-               l.Linger = 0
-       }
-       fd.incref()
-       defer fd.decref()
-       e := syscall.SetsockoptLinger(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_LINGER, &l)
-       return os.NewSyscallError("setsockopt", e)
-}
-
 type UnknownSocketError struct {
        sa syscall.Sockaddr
 }
diff --git a/src/pkg/net/sockopt.go b/src/pkg/net/sockopt.go
new file mode 100644 (file)
index 0000000..7fa1052
--- /dev/null
@@ -0,0 +1,171 @@
+// Copyright 2009 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 freebsd linux netbsd openbsd windows
+
+// Socket options
+
+package net
+
+import (
+       "bytes"
+       "os"
+       "syscall"
+)
+
+// Boolean to int.
+func boolint(b bool) int {
+       if b {
+               return 1
+       }
+       return 0
+}
+
+func ipv4AddrToInterface(ip IP) (*Interface, error) {
+       ift, err := Interfaces()
+       if err != nil {
+               return nil, err
+       }
+       for _, ifi := range ift {
+               ifat, err := ifi.Addrs()
+               if err != nil {
+                       return nil, err
+               }
+               for _, ifa := range ifat {
+                       switch v := ifa.(type) {
+                       case *IPAddr:
+                               if ip.Equal(v.IP) {
+                                       return &ifi, nil
+                               }
+                       case *IPNet:
+                               if ip.Equal(v.IP) {
+                                       return &ifi, nil
+                               }
+                       }
+               }
+       }
+       if ip.Equal(IPv4zero) {
+               return nil, nil
+       }
+       return nil, errNoSuchInterface
+}
+
+func interfaceToIPv4Addr(ifi *Interface) (IP, error) {
+       if ifi == nil {
+               return IPv4zero, nil
+       }
+       ifat, err := ifi.Addrs()
+       if err != nil {
+               return nil, err
+       }
+       for _, ifa := range ifat {
+               switch v := ifa.(type) {
+               case *IPAddr:
+                       if v.IP.To4() != nil {
+                               return v.IP, nil
+                       }
+               case *IPNet:
+                       if v.IP.To4() != nil {
+                               return v.IP, nil
+                       }
+               }
+       }
+       return nil, errNoSuchInterface
+}
+
+func setIPv4MreqToInterface(mreq *syscall.IPMreq, ifi *Interface) error {
+       if ifi == nil {
+               return nil
+       }
+       ifat, err := ifi.Addrs()
+       if err != nil {
+               return err
+       }
+       for _, ifa := range ifat {
+               switch v := ifa.(type) {
+               case *IPAddr:
+                       if a := v.IP.To4(); a != nil {
+                               copy(mreq.Interface[:], a)
+                               goto done
+                       }
+               case *IPNet:
+                       if a := v.IP.To4(); a != nil {
+                               copy(mreq.Interface[:], a)
+                               goto done
+                       }
+               }
+       }
+done:
+       if bytes.Equal(mreq.Multiaddr[:], IPv4zero.To4()) {
+               return errNoSuchMulticastInterface
+       }
+       return nil
+}
+
+func setReadBuffer(fd *netFD, bytes int) error {
+       fd.incref()
+       defer fd.decref()
+       return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes))
+}
+
+func setWriteBuffer(fd *netFD, bytes int) error {
+       fd.incref()
+       defer fd.decref()
+       return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes))
+}
+
+func setReadTimeout(fd *netFD, nsec int64) error {
+       fd.rdeadline_delta = nsec
+       return nil
+}
+
+func setWriteTimeout(fd *netFD, nsec int64) error {
+       fd.wdeadline_delta = nsec
+       return nil
+}
+
+func setTimeout(fd *netFD, nsec int64) error {
+       if e := setReadTimeout(fd, nsec); e != nil {
+               return e
+       }
+       return setWriteTimeout(fd, nsec)
+}
+
+func setReuseAddr(fd *netFD, reuse bool) error {
+       fd.incref()
+       defer fd.decref()
+       return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse)))
+}
+
+func setDontRoute(fd *netFD, dontroute bool) error {
+       fd.incref()
+       defer fd.decref()
+       return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_DONTROUTE, boolint(dontroute)))
+}
+
+func setKeepAlive(fd *netFD, keepalive bool) error {
+       fd.incref()
+       defer fd.decref()
+       return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive)))
+}
+
+func setNoDelay(fd *netFD, noDelay bool) error {
+       fd.incref()
+       defer fd.decref()
+       return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(noDelay)))
+}
+
+func setLinger(fd *netFD, sec int) error {
+       var l syscall.Linger
+       if sec >= 0 {
+               l.Onoff = 1
+               l.Linger = int32(sec)
+       } else {
+               l.Onoff = 0
+               l.Linger = 0
+       }
+       fd.incref()
+       defer fd.decref()
+       return os.NewSyscallError("setsockopt", syscall.SetsockoptLinger(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_LINGER, &l))
+}
similarity index 96%
rename from src/pkg/net/sock_bsd.go
rename to src/pkg/net/sockopt_bsd.go
index 816e4fc3f741cfa97d4ab34905dff9f60712d5de..370831fe5f4c18831077aa454725612065e603e7 100644 (file)
@@ -4,7 +4,7 @@
 
 // +build darwin freebsd netbsd openbsd
 
-// Sockets for BSD variants
+// Socket options for BSD variants
 
 package net
 
similarity index 95%
rename from src/pkg/net/sock_linux.go
rename to src/pkg/net/sockopt_linux.go
index ec31e803b6f29b9b2ac589d09b37b128d657a983..e55c3c5ce8cc6939978d69b1807c755944903e93 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Sockets for Linux
+// Socket options for Linux
 
 package net
 
similarity index 96%
rename from src/pkg/net/sock_windows.go
rename to src/pkg/net/sockopt_windows.go
index 9b9cd9e368bf5dc3dd3d897d1433c81e082ee949..df15b8c4c83bccc675680f51f40d9d7d104f33c8 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Sockets for Windows
+// Socket options for Windows
 
 package net
 
diff --git a/src/pkg/net/sockoptip.go b/src/pkg/net/sockoptip.go
new file mode 100644 (file)
index 0000000..90b6f75
--- /dev/null
@@ -0,0 +1,187 @@
+// Copyright 2011 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 freebsd linux netbsd openbsd windows
+
+// IP-level socket options
+
+package net
+
+import (
+       "os"
+       "syscall"
+)
+
+func ipv4TOS(fd *netFD) (int, error) {
+       fd.incref()
+       defer fd.decref()
+       v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_TOS)
+       if err != nil {
+               return -1, os.NewSyscallError("getsockopt", err)
+       }
+       return v, nil
+}
+
+func setIPv4TOS(fd *netFD, v int) error {
+       fd.incref()
+       defer fd.decref()
+       err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_TOS, v)
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
+
+func ipv4TTL(fd *netFD) (int, error) {
+       fd.incref()
+       defer fd.decref()
+       v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_TTL)
+       if err != nil {
+               return -1, os.NewSyscallError("getsockopt", err)
+       }
+       return v, nil
+}
+
+func setIPv4TTL(fd *netFD, v int) error {
+       fd.incref()
+       defer fd.decref()
+       err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_TTL, v)
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
+
+func joinIPv4Group(fd *netFD, ifi *Interface, ip IP) error {
+       mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}}
+       if err := setIPv4MreqToInterface(mreq, ifi); err != nil {
+               return err
+       }
+       fd.incref()
+       defer fd.decref()
+       return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq))
+}
+
+func leaveIPv4Group(fd *netFD, ifi *Interface, ip IP) error {
+       mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}}
+       if err := setIPv4MreqToInterface(mreq, ifi); err != nil {
+               return err
+       }
+       fd.incref()
+       defer fd.decref()
+       return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq))
+}
+
+func ipv6HopLimit(fd *netFD) (int, error) {
+       fd.incref()
+       defer fd.decref()
+       v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS)
+       if err != nil {
+               return -1, os.NewSyscallError("getsockopt", err)
+       }
+       return v, nil
+}
+
+func setIPv6HopLimit(fd *netFD, v int) error {
+       fd.incref()
+       defer fd.decref()
+       err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, v)
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
+
+func ipv6MulticastInterface(fd *netFD) (*Interface, error) {
+       fd.incref()
+       defer fd.decref()
+       v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF)
+       if err != nil {
+               return nil, os.NewSyscallError("getsockopt", err)
+       }
+       if v == 0 {
+               return nil, nil
+       }
+       ifi, err := InterfaceByIndex(v)
+       if err != nil {
+               return nil, err
+       }
+       return ifi, nil
+}
+
+func setIPv6MulticastInterface(fd *netFD, ifi *Interface) error {
+       var v int
+       if ifi != nil {
+               v = ifi.Index
+       }
+       fd.incref()
+       defer fd.decref()
+       err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF, v)
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
+
+func ipv6MulticastHopLimit(fd *netFD) (int, error) {
+       fd.incref()
+       defer fd.decref()
+       v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_HOPS)
+       if err != nil {
+               return -1, os.NewSyscallError("getsockopt", err)
+       }
+       return v, nil
+}
+
+func setIPv6MulticastHopLimit(fd *netFD, v int) error {
+       fd.incref()
+       defer fd.decref()
+       err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_HOPS, v)
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
+
+func ipv6MulticastLoopback(fd *netFD) (bool, error) {
+       fd.incref()
+       defer fd.decref()
+       v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP)
+       if err != nil {
+               return false, os.NewSyscallError("getsockopt", err)
+       }
+       return v == 1, nil
+}
+
+func setIPv6MulticastLoopback(fd *netFD, v bool) error {
+       fd.incref()
+       defer fd.decref()
+       err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP, boolint(v))
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
+
+func joinIPv6Group(fd *netFD, ifi *Interface, ip IP) error {
+       mreq := &syscall.IPv6Mreq{}
+       copy(mreq.Multiaddr[:], ip)
+       if ifi != nil {
+               mreq.Interface = uint32(ifi.Index)
+       }
+       fd.incref()
+       defer fd.decref()
+       return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq))
+}
+
+func leaveIPv6Group(fd *netFD, ifi *Interface, ip IP) error {
+       mreq := &syscall.IPv6Mreq{}
+       copy(mreq.Multiaddr[:], ip)
+       if ifi != nil {
+               mreq.Interface = uint32(ifi.Index)
+       }
+       fd.incref()
+       defer fd.decref()
+       return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_LEAVE_GROUP, mreq))
+}
diff --git a/src/pkg/net/sockoptip_bsd.go b/src/pkg/net/sockoptip_bsd.go
new file mode 100644 (file)
index 0000000..5f7dff2
--- /dev/null
@@ -0,0 +1,54 @@
+// Copyright 2011 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 freebsd netbsd openbsd
+
+// IP-level socket options for BSD variants
+
+package net
+
+import (
+       "os"
+       "syscall"
+)
+
+func ipv4MulticastTTL(fd *netFD) (int, error) {
+       fd.incref()
+       defer fd.decref()
+       v, err := syscall.GetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL)
+       if err != nil {
+               return -1, os.NewSyscallError("getsockopt", err)
+       }
+       return int(v), nil
+}
+
+func setIPv4MulticastTTL(fd *netFD, v int) error {
+       fd.incref()
+       defer fd.decref()
+       err := syscall.SetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, byte(v))
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
+
+func ipv6TrafficClass(fd *netFD) (int, error) {
+       fd.incref()
+       defer fd.decref()
+       v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS)
+       if err != nil {
+               return -1, os.NewSyscallError("getsockopt", err)
+       }
+       return v, nil
+}
+
+func setIPv6TrafficClass(fd *netFD, v int) error {
+       fd.incref()
+       defer fd.decref()
+       err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS, v)
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
diff --git a/src/pkg/net/sockoptip_darwin.go b/src/pkg/net/sockoptip_darwin.go
new file mode 100644 (file)
index 0000000..dedfd6f
--- /dev/null
@@ -0,0 +1,78 @@
+// Copyright 2011 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.
+
+// IP-level socket options for Darwin
+
+package net
+
+import (
+       "os"
+       "syscall"
+)
+
+func ipv4MulticastInterface(fd *netFD) (*Interface, error) {
+       fd.incref()
+       defer fd.decref()
+       a, err := syscall.GetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF)
+       if err != nil {
+               return nil, os.NewSyscallError("getsockopt", err)
+       }
+       return ipv4AddrToInterface(IPv4(a[0], a[1], a[2], a[3]))
+}
+
+func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
+       ip, err := interfaceToIPv4Addr(ifi)
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       var x [4]byte
+       copy(x[:], ip.To4())
+       fd.incref()
+       defer fd.decref()
+       err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, x)
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
+
+func ipv4MulticastLoopback(fd *netFD) (bool, error) {
+       fd.incref()
+       defer fd.decref()
+       v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP)
+       if err != nil {
+               return false, os.NewSyscallError("getsockopt", err)
+       }
+       return v == 1, nil
+}
+
+func setIPv4MulticastLoopback(fd *netFD, v bool) error {
+       fd.incref()
+       defer fd.decref()
+       err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v))
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
+
+func ipv4ReceiveInterface(fd *netFD) (bool, error) {
+       fd.incref()
+       defer fd.decref()
+       v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF)
+       if err != nil {
+               return false, os.NewSyscallError("getsockopt", err)
+       }
+       return v == 1, nil
+}
+
+func setIPv4ReceiveInterface(fd *netFD, v bool) error {
+       fd.incref()
+       defer fd.decref()
+       err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF, boolint(v))
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
diff --git a/src/pkg/net/sockoptip_freebsd.go b/src/pkg/net/sockoptip_freebsd.go
new file mode 100644 (file)
index 0000000..55f7b1a
--- /dev/null
@@ -0,0 +1,80 @@
+// Copyright 2011 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.
+
+// IP-level socket options for FreeBSD
+
+package net
+
+import (
+       "os"
+       "syscall"
+)
+
+func ipv4MulticastInterface(fd *netFD) (*Interface, error) {
+       fd.incref()
+       defer fd.decref()
+       mreq, err := syscall.GetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF)
+       if err != nil {
+               return nil, os.NewSyscallError("getsockopt", err)
+       }
+       if int(mreq.Ifindex) == 0 {
+               return nil, nil
+       }
+       return InterfaceByIndex(int(mreq.Ifindex))
+}
+
+func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
+       var v int32
+       if ifi != nil {
+               v = int32(ifi.Index)
+       }
+       mreq := &syscall.IPMreqn{Ifindex: v}
+       fd.incref()
+       defer fd.decref()
+       err := syscall.SetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, mreq)
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
+
+func ipv4MulticastLoopback(fd *netFD) (bool, error) {
+       fd.incref()
+       defer fd.decref()
+       v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP)
+       if err != nil {
+               return false, os.NewSyscallError("getsockopt", err)
+       }
+       return v == 1, nil
+}
+
+func setIPv4MulticastLoopback(fd *netFD, v bool) error {
+       fd.incref()
+       defer fd.decref()
+       err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v))
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
+
+func ipv4ReceiveInterface(fd *netFD) (bool, error) {
+       fd.incref()
+       defer fd.decref()
+       v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF)
+       if err != nil {
+               return false, os.NewSyscallError("getsockopt", err)
+       }
+       return v == 1, nil
+}
+
+func setIPv4ReceiveInterface(fd *netFD, v bool) error {
+       fd.incref()
+       defer fd.decref()
+       err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF, boolint(v))
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
diff --git a/src/pkg/net/sockoptip_linux.go b/src/pkg/net/sockoptip_linux.go
new file mode 100644 (file)
index 0000000..360f8de
--- /dev/null
@@ -0,0 +1,120 @@
+// Copyright 2011 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.
+
+// IP-level socket options for Linux
+
+package net
+
+import (
+       "os"
+       "syscall"
+)
+
+func ipv4MulticastInterface(fd *netFD) (*Interface, error) {
+       fd.incref()
+       defer fd.decref()
+       mreq, err := syscall.GetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF)
+       if err != nil {
+               return nil, os.NewSyscallError("getsockopt", err)
+       }
+       if int(mreq.Ifindex) == 0 {
+               return nil, nil
+       }
+       return InterfaceByIndex(int(mreq.Ifindex))
+}
+
+func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
+       var v int32
+       if ifi != nil {
+               v = int32(ifi.Index)
+       }
+       mreq := &syscall.IPMreqn{Ifindex: v}
+       fd.incref()
+       defer fd.decref()
+       err := syscall.SetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, mreq)
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
+
+func ipv4MulticastTTL(fd *netFD) (int, error) {
+       fd.incref()
+       defer fd.decref()
+       v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL)
+       if err != nil {
+               return -1, os.NewSyscallError("getsockopt", err)
+       }
+       return v, nil
+}
+
+func setIPv4MulticastTTL(fd *netFD, v int) error {
+       fd.incref()
+       defer fd.decref()
+       err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, v)
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
+
+func ipv4MulticastLoopback(fd *netFD) (bool, error) {
+       fd.incref()
+       defer fd.decref()
+       v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP)
+       if err != nil {
+               return false, os.NewSyscallError("getsockopt", err)
+       }
+       return v == 1, nil
+}
+
+func setIPv4MulticastLoopback(fd *netFD, v bool) error {
+       fd.incref()
+       defer fd.decref()
+       err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v))
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
+
+func ipv4ReceiveInterface(fd *netFD) (bool, error) {
+       fd.incref()
+       defer fd.decref()
+       v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_PKTINFO)
+       if err != nil {
+               return false, os.NewSyscallError("getsockopt", err)
+       }
+       return v == 1, nil
+}
+
+func setIPv4ReceiveInterface(fd *netFD, v bool) error {
+       fd.incref()
+       defer fd.decref()
+       err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_PKTINFO, boolint(v))
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
+
+func ipv6TrafficClass(fd *netFD) (int, error) {
+       fd.incref()
+       defer fd.decref()
+       v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS)
+       if err != nil {
+               return -1, os.NewSyscallError("getsockopt", err)
+       }
+       return v, nil
+}
+
+func setIPv6TrafficClass(fd *netFD, v int) error {
+       fd.incref()
+       defer fd.decref()
+       err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS, v)
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
diff --git a/src/pkg/net/sockoptip_openbsd.go b/src/pkg/net/sockoptip_openbsd.go
new file mode 100644 (file)
index 0000000..89b8e45
--- /dev/null
@@ -0,0 +1,78 @@
+// Copyright 2011 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.
+
+// IP-level socket options for OpenBSD
+
+package net
+
+import (
+       "os"
+       "syscall"
+)
+
+func ipv4MulticastInterface(fd *netFD) (*Interface, error) {
+       fd.incref()
+       defer fd.decref()
+       a, err := syscall.GetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF)
+       if err != nil {
+               return nil, os.NewSyscallError("getsockopt", err)
+       }
+       return ipv4AddrToInterface(IPv4(a[0], a[1], a[2], a[3]))
+}
+
+func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
+       ip, err := interfaceToIPv4Addr(ifi)
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       var x [4]byte
+       copy(x[:], ip.To4())
+       fd.incref()
+       defer fd.decref()
+       err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, x)
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
+
+func ipv4MulticastLoopback(fd *netFD) (bool, error) {
+       fd.incref()
+       defer fd.decref()
+       v, err := syscall.GetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP)
+       if err != nil {
+               return false, os.NewSyscallError("getsockopt", err)
+       }
+       return v == 1, nil
+}
+
+func setIPv4MulticastLoopback(fd *netFD, v bool) error {
+       fd.incref()
+       defer fd.decref()
+       err := syscall.SetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, byte(boolint(v)))
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
+
+func ipv4ReceiveInterface(fd *netFD) (bool, error) {
+       fd.incref()
+       defer fd.decref()
+       v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF)
+       if err != nil {
+               return false, os.NewSyscallError("getsockopt", err)
+       }
+       return v == 1, nil
+}
+
+func setIPv4ReceiveInterface(fd *netFD, v bool) error {
+       fd.incref()
+       defer fd.decref()
+       err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF, boolint(v))
+       if err != nil {
+               return os.NewSyscallError("setsockopt", err)
+       }
+       return nil
+}
diff --git a/src/pkg/net/sockoptip_windows.go b/src/pkg/net/sockoptip_windows.go
new file mode 100644 (file)
index 0000000..a603843
--- /dev/null
@@ -0,0 +1,61 @@
+// Copyright 2011 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.
+
+// IP-level socket options for Windows
+
+package net
+
+import (
+       "os"
+)
+
+func ipv4MulticastInterface(fd *netFD) (*Interface, error) {
+       // TODO: Implement this
+       return nil, os.EWINDOWS
+}
+
+func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
+       // TODO: Implement this
+       return os.EWINDOWS
+}
+
+func ipv4MulticastTTL(fd *netFD) (int, error) {
+       // TODO: Implement this
+       return -1, os.EWINDOWS
+}
+
+func setIPv4MulticastTTL(fd *netFD, v int) error {
+       // TODO: Implement this
+       return os.EWINDOWS
+}
+
+func ipv4MultiastLoopback(fd *netFD) (bool, error) {
+       // TODO: Implement this
+       return false, os.EWINDOWS
+}
+
+func setIPv4MulticastLoopback(fd *netFD, v bool) error {
+       // TODO: Implement this
+       return os.EWINDOWS
+}
+
+func ipv4ReceiveInterface(fd *netFD) (bool, error) {
+       // TODO: Implement this
+       return false, os.EWINDOWS
+}
+
+func setIPv4ReceiveInterface(fd *netFD, v bool) error {
+       // TODO: Implement this
+       return os.EWINDOWS
+}
+
+func ipv6TrafficClass(fd *netFD) (int, error) {
+       // TODO: Implement this
+       return os.EWINDOWS
+}
+
+func setIPv6TrafficClass(fd *netFD, v int) error {
+       // TODO: Implement this
+       return os.EWINDOWS
+}
index b7e8e03aec684e329134b544125f87620ffe4b1b..7bc4cb9f7ece0962e8f8a87a0151df3f86e3612c 100644 (file)
@@ -9,7 +9,6 @@
 package net
 
 import (
-       "bytes"
        "os"
        "syscall"
 )
@@ -272,66 +271,32 @@ func (c *UDPConn) LeaveGroup(ifi *Interface, addr IP) error {
 }
 
 func joinIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
-       mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}}
-       if err := setIPv4InterfaceToJoin(mreq, ifi); err != nil {
-               return &OpError{"joinipv4group", "udp", &IPAddr{ip}, err}
-       }
-       if err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)); err != nil {
+       err := joinIPv4Group(c.fd, ifi, ip)
+       if err != nil {
                return &OpError{"joinipv4group", "udp", &IPAddr{ip}, err}
        }
        return nil
 }
 
 func leaveIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
-       mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}}
-       if err := setIPv4InterfaceToJoin(mreq, ifi); err != nil {
-               return &OpError{"leaveipv4group", "udp", &IPAddr{ip}, err}
-       }
-       if err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq)); err != nil {
-               return &OpError{"leaveipv4group", "udp", &IPAddr{ip}, err}
-       }
-       return nil
-}
-
-func setIPv4InterfaceToJoin(mreq *syscall.IPMreq, ifi *Interface) error {
-       if ifi == nil {
-               return nil
-       }
-       ifat, err := ifi.Addrs()
+       err := leaveIPv4Group(c.fd, ifi, ip)
        if err != nil {
-               return err
-       }
-       for _, ifa := range ifat {
-               if x := ifa.(*IPAddr).IP.To4(); x != nil {
-                       copy(mreq.Interface[:], x)
-                       break
-               }
-       }
-       if bytes.Equal(mreq.Multiaddr[:], IPv4zero) {
-               return os.EINVAL
+               return &OpError{"leaveipv4group", "udp", &IPAddr{ip}, err}
        }
        return nil
 }
 
 func joinIPv6GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
-       mreq := &syscall.IPv6Mreq{}
-       copy(mreq.Multiaddr[:], ip)
-       if ifi != nil {
-               mreq.Interface = uint32(ifi.Index)
-       }
-       if err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(c.fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq)); err != nil {
+       err := joinIPv6Group(c.fd, ifi, ip)
+       if err != nil {
                return &OpError{"joinipv6group", "udp", &IPAddr{ip}, err}
        }
        return nil
 }
 
 func leaveIPv6GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
-       mreq := &syscall.IPv6Mreq{}
-       copy(mreq.Multiaddr[:], ip)
-       if ifi != nil {
-               mreq.Interface = uint32(ifi.Index)
-       }
-       if err := os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(c.fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_LEAVE_GROUP, mreq)); err != nil {
+       err := leaveIPv6Group(c.fd, ifi, ip)
+       if err != nil {
                return &OpError{"leaveipv6group", "udp", &IPAddr{ip}, err}
        }
        return nil
diff --git a/src/pkg/net/unicast_test.go b/src/pkg/net/unicast_test.go
new file mode 100644 (file)
index 0000000..6ed6f59
--- /dev/null
@@ -0,0 +1,99 @@
+// Copyright 2011 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 net
+
+import (
+       "runtime"
+       "testing"
+)
+
+var unicastTests = []struct {
+       net    string
+       laddr  string
+       ipv6   bool
+       packet bool
+}{
+       {"tcp4", "127.0.0.1:0", false, false},
+       {"tcp6", "[::1]:0", true, false},
+       {"udp4", "127.0.0.1:0", false, true},
+       {"udp6", "[::1]:0", true, true},
+}
+
+func TestUnicastTCPAndUDP(t *testing.T) {
+       if runtime.GOOS == "plan9" || runtime.GOOS == "windows" {
+               return
+       }
+
+       for _, tt := range unicastTests {
+               if tt.ipv6 && !supportsIPv6 {
+                       continue
+               }
+               var fd *netFD
+               if !tt.packet {
+                       c, err := Listen(tt.net, tt.laddr)
+                       if err != nil {
+                               t.Fatalf("Listen failed: %v", err)
+                       }
+                       defer c.Close()
+                       fd = c.(*TCPListener).fd
+               } else {
+                       c, err := ListenPacket(tt.net, tt.laddr)
+                       if err != nil {
+                               t.Fatalf("ListenPacket failed: %v", err)
+                       }
+                       defer c.Close()
+                       fd = c.(*UDPConn).fd
+               }
+               if !tt.ipv6 {
+                       testIPv4UnicastSocketOptions(t, fd)
+               } else {
+                       testIPv6UnicastSocketOptions(t, fd)
+               }
+       }
+}
+
+func testIPv4UnicastSocketOptions(t *testing.T, fd *netFD) {
+       tos, err := ipv4TOS(fd)
+       if err != nil {
+               t.Fatalf("ipv4TOS failed: %v", err)
+       }
+       t.Logf("IPv4 TOS: %v", tos)
+       err = setIPv4TOS(fd, 1)
+       if err != nil {
+               t.Fatalf("setIPv4TOS failed: %v", err)
+       }
+
+       ttl, err := ipv4TTL(fd)
+       if err != nil {
+               t.Fatalf("ipv4TTL failed: %v", err)
+       }
+       t.Logf("IPv4 TTL: %v", ttl)
+       err = setIPv4TTL(fd, 1)
+       if err != nil {
+               t.Fatalf("setIPv4TTL failed: %v", err)
+       }
+}
+
+func testIPv6UnicastSocketOptions(t *testing.T, fd *netFD) {
+       tos, err := ipv6TrafficClass(fd)
+       if err != nil {
+               t.Fatalf("ipv6TrafficClass failed: %v", err)
+       }
+       t.Logf("IPv6 TrafficClass: %v", tos)
+       err = setIPv6TrafficClass(fd, 1)
+       if err != nil {
+               t.Fatalf("setIPv6TrafficClass failed: %v", err)
+       }
+
+       hoplim, err := ipv6HopLimit(fd)
+       if err != nil {
+               t.Fatalf("ipv6HopLimit failed: %v", err)
+       }
+       t.Logf("IPv6 HopLimit: %v", hoplim)
+       err = setIPv6HopLimit(fd, 1)
+       if err != nil {
+               t.Fatalf("setIPv6HopLimit failed: %v", err)
+       }
+}
index 5f33364704fa84328920730dca26c6f6bcf84ce4..7ed14a4dc2425664b978cfc0109b08afa092b4a9 100644 (file)
@@ -641,10 +641,21 @@ type Linger struct {
 }
 
 const (
+       IP_TOS             = 0x3
+       IP_TTL             = 0x4
        IP_ADD_MEMBERSHIP  = 0xc
        IP_DROP_MEMBERSHIP = 0xd
 )
 
+const (
+       IPV6_UNICAST_HOPS   = 0x4
+       IPV6_MULTICAST_IF   = 0x9
+       IPV6_MULTICAST_HOPS = 0xa
+       IPV6_MULTICAST_LOOP = 0xb
+       IPV6_JOIN_GROUP     = 0xc
+       IPV6_LEAVE_GROUP    = 0xd
+)
+
 type IPMreq struct {
        Multiaddr [4]byte /* in_addr */
        Interface [4]byte /* in_addr */
@@ -655,6 +666,7 @@ type IPv6Mreq struct {
        Interface uint32
 }
 
+func GetsockoptInt(fd Handle, level, opt int) (int, error)              { return -1, EWINDOWS }
 func SetsockoptLinger(fd Handle, level, opt int, l *Linger) (err error) { return EWINDOWS }
 func SetsockoptIPMreq(fd Handle, level, opt int, mreq *IPMreq) (err error) {
        return Setsockopt(fd, int32(level), int32(opt), (*byte)(unsafe.Pointer(mreq)), int32(unsafe.Sizeof(*mreq)))