]> Cypherpunks repositories - gostls13.git/commitdiff
net: add IPv4 multicast to UDPConn
authorDave Cheney <dave@cheney.net>
Wed, 16 Feb 2011 20:07:13 +0000 (15:07 -0500)
committerRuss Cox <rsc@golang.org>
Wed, 16 Feb 2011 20:07:13 +0000 (15:07 -0500)
notes:
Darwin is very particular about joining a multicast group if the
listneing socket is not created in "udp4" mode, the other supported
OS's are more flexible.

A simple example sets up a socket to listen on the mdns/bonjour
group 224.0.0.251:5353

// ensure the sock is udp4, and the IP is a 4 byte IPv4
socket, err := net.ListenUDP("udp4", &net.UDPAddr {
        IP: net.IPv4zero,
        // currently darwin will not allow you to bind to
        // a port if it is already bound to another process
        Port: 5353,
})
if err != nil {
        log.Exitf("listen %s", err)
}
defer socket.Close()
err = socket.JoinGroup(net.IPv4(224, 0, 0, 251))
if err != nil {
        log.Exitf("join group %s", err)
}

R=adg, rsc
CC=golang-dev
https://golang.org/cl/4066044

src/pkg/net/multicast_test.go [new file with mode: 0644]
src/pkg/net/udpsock.go

diff --git a/src/pkg/net/multicast_test.go b/src/pkg/net/multicast_test.go
new file mode 100644 (file)
index 0000000..0c9a035
--- /dev/null
@@ -0,0 +1,56 @@
+// 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 (
+       "testing"
+)
+
+func TestMulticastJoinAndLeave(t *testing.T) {
+       addr := &UDPAddr{
+               IP:   IPv4zero,
+               Port: 0,
+       }
+       // open a UDPConn
+       conn, err := ListenUDP("udp4", addr)
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer conn.Close()
+
+       // try to join group
+       mcast := IPv4(224, 0, 0, 251)
+       err = conn.JoinGroup(mcast)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       // try to leave group
+       err = conn.LeaveGroup(mcast)
+       if err != nil {
+               t.Fatal(err)
+       }
+}
+
+func TestJoinFailureWithIPv6Address(t *testing.T) {
+       addr := &UDPAddr{
+               IP:   IPv4zero,
+               Port: 0,
+       }
+
+       // open a UDPConn
+       conn, err := ListenUDP("udp4", addr)
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer conn.Close()
+       // try to join group
+       mcast := ParseIP("ff02::1")
+       err = conn.JoinGroup(mcast)
+       if err == nil {
+               t.Fatal("JoinGroup succeeded, should fail")
+       }
+       t.Logf("%s", err)
+}
index 0270954c176ddf89f7d4dd387ff416af200c9aaa..f9274493e602a39173cf40b0c4240342887ba632 100644 (file)
@@ -279,3 +279,44 @@ func (c *UDPConn) BindToDevice(device string) os.Error {
 // It is the caller's responsibility to close f when finished.
 // Closing c does not affect f, and closing f does not affect c.
 func (c *UDPConn) File() (f *os.File, err os.Error) { return c.fd.dup() }
+
+var errInvalidMulticast = os.ErrorString("invalid IPv4 multicast address")
+
+// JoinGroup joins the IPv4 multicast group named by addr.
+// The UDPConn must use the "udp4" network.
+func (c *UDPConn) JoinGroup(addr IP) os.Error {
+       if !c.ok() {
+               return os.EINVAL
+       }
+       ip := addr.To4()
+       if ip == nil {
+               return &OpError{"joingroup", "udp", &IPAddr{ip}, errInvalidMulticast}
+       }
+       mreq := &syscall.IpMreq{
+               Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]},
+       }
+       err := os.NewSyscallError("setsockopt", syscall.SetsockoptIpMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq))
+       if err != nil {
+               return &OpError{"joingroup", "udp", &IPAddr{ip}, err}
+       }
+       return nil
+}
+
+// LeaveGroup exits the IPv4 multicast group named by addr.
+func (c *UDPConn) LeaveGroup(addr IP) os.Error {
+       if !c.ok() {
+               return os.EINVAL
+       }
+       ip := addr.To4()
+       if ip == nil {
+               return &OpError{"leavegroup", "udp", &IPAddr{ip}, errInvalidMulticast}
+       }
+       mreq := &syscall.IpMreq{
+               Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]},
+       }
+       err := os.NewSyscallError("setsockopt", syscall.SetsockoptIpMreq(c.fd.sysfd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq))
+       if err != nil {
+               return &OpError{"leavegroup", "udp", &IPAddr{ip}, err}
+       }
+       return nil
+}