//go:build !linux package multicast import ( "fmt" "net" "strconv" "syscall" "time" "golang.org/x/net/ipv4" ) // multiConn is a multicast connection // that works in parallel on all interfaces. type multiConn struct { addr *net.UDPAddr readConn *net.UDPConn readConnIP *ipv4.PacketConn writeConns []*net.UDPConn writeConnIPs []*ipv4.PacketConn } // NewMultiConn allocates a multi-interface multicast connection. func NewMultiConn( address string, readOnly bool, listenPacket func(network, address string) (net.PacketConn, error), ) (Conn, error) { addr, err := net.ResolveUDPAddr("udp4", address) if err != nil { return nil, err } tmp, err := listenPacket("udp4", "224.0.0.0:"+strconv.FormatInt(int64(addr.Port), 10)) if err != nil { return nil, err } readConn := tmp.(*net.UDPConn) intfs, err := net.Interfaces() if err != nil { readConn.Close() //nolint:errcheck return nil, err } readConnIP := ipv4.NewPacketConn(readConn) var enabledInterfaces []*net.Interface //nolint:prealloc for _, intf := range intfs { if (intf.Flags & net.FlagMulticast) == 0 { continue } cintf := intf err = readConnIP.JoinGroup(&cintf, &net.UDPAddr{IP: addr.IP}) if err != nil { continue } enabledInterfaces = append(enabledInterfaces, &cintf) } if enabledInterfaces == nil { readConn.Close() //nolint:errcheck return nil, fmt.Errorf("no multicast-capable interfaces found") } var writeConns []*net.UDPConn var writeConnIPs []*ipv4.PacketConn if !readOnly { writeConns = make([]*net.UDPConn, len(enabledInterfaces)) writeConnIPs = make([]*ipv4.PacketConn, len(enabledInterfaces)) for i, intf := range enabledInterfaces { tmp, err := listenPacket("udp4", "224.0.0.0:"+strconv.FormatInt(int64(addr.Port), 10)) if err != nil { for j := 0; j < i; j++ { writeConns[j].Close() //nolint:errcheck } readConn.Close() //nolint:errcheck return nil, err } writeConn := tmp.(*net.UDPConn) writeConnIP := ipv4.NewPacketConn(writeConn) err = writeConnIP.SetMulticastInterface(intf) if err != nil { for j := 0; j < i; j++ { writeConns[j].Close() //nolint:errcheck } readConn.Close() //nolint:errcheck return nil, err } err = writeConnIP.SetMulticastTTL(multicastTTL) if err != nil { for j := 0; j < i; j++ { writeConns[j].Close() //nolint:errcheck } readConn.Close() //nolint:errcheck return nil, err } writeConns[i] = writeConn writeConnIPs[i] = writeConnIP } } return &multiConn{ addr: addr, readConn: readConn, readConnIP: readConnIP, writeConns: writeConns, writeConnIPs: writeConnIPs, }, nil } // Close implements Conn. func (c *multiConn) Close() error { for _, c := range c.writeConns { c.Close() //nolint:errcheck } c.readConn.Close() //nolint:errcheck return nil } // SetReadBuffer implements Conn. func (c *multiConn) SetReadBuffer(bytes int) error { return c.readConn.SetReadBuffer(bytes) } // SyscallConn implements Conn. func (c *multiConn) SyscallConn() (syscall.RawConn, error) { return c.readConn.SyscallConn() } // LocalAddr implements Conn. func (c *multiConn) LocalAddr() net.Addr { return c.readConn.LocalAddr() } // SetDeadline implements Conn. func (c *multiConn) SetDeadline(_ time.Time) error { panic("unimplemented") } // SetReadDeadline implements Conn. func (c *multiConn) SetReadDeadline(t time.Time) error { return c.readConn.SetReadDeadline(t) } // SetWriteDeadline implements Conn. func (c *multiConn) SetWriteDeadline(t time.Time) error { var err error for _, c := range c.writeConns { err2 := c.SetWriteDeadline(t) if err == nil { err = err2 } } return err } // WriteTo implements Conn. func (c *multiConn) WriteTo(b []byte, addr net.Addr) (int, error) { var n int var err error for _, c := range c.writeConns { var err2 error n, err2 = c.WriteTo(b, addr) if err == nil { err = err2 } } return n, err } // ReadFrom implements Conn. func (c *multiConn) ReadFrom(b []byte) (int, net.Addr, error) { return c.readConn.ReadFrom(b) }