From: Dave Cheney Date: Wed, 16 Feb 2011 20:07:13 +0000 (-0500) Subject: net: add IPv4 multicast to UDPConn X-Git-Tag: weekly.2011-02-24~85 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=0856731daad6026fed5118d15efc86daba65782c;p=gostls13.git net: add IPv4 multicast to UDPConn 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 --- diff --git a/src/pkg/net/multicast_test.go b/src/pkg/net/multicast_test.go new file mode 100644 index 0000000000..0c9a0355e6 --- /dev/null +++ b/src/pkg/net/multicast_test.go @@ -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) +} diff --git a/src/pkg/net/udpsock.go b/src/pkg/net/udpsock.go index 0270954c17..f9274493e6 100644 --- a/src/pkg/net/udpsock.go +++ b/src/pkg/net/udpsock.go @@ -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 +}