status-go/vendor/github.com/koron/go-ssdp/multicast.go

129 lines
2.8 KiB
Go
Raw Normal View History

2019-06-09 07:24:20 +00:00
package ssdp
import (
"errors"
2021-10-19 13:43:41 +00:00
"io"
2019-06-09 07:24:20 +00:00
"net"
2021-10-19 13:43:41 +00:00
"strings"
2019-06-09 07:24:20 +00:00
"time"
"golang.org/x/net/ipv4"
)
type multicastConn struct {
laddr *net.UDPAddr
conn *net.UDPConn
pconn *ipv4.PacketConn
iflist []net.Interface
}
2021-10-19 13:43:41 +00:00
func multicastListen(r *udpAddrResolver) (*multicastConn, error) {
2019-06-09 07:24:20 +00:00
// prepare parameters.
2021-10-19 13:43:41 +00:00
laddr, err := r.resolve()
2019-06-09 07:24:20 +00:00
if err != nil {
return nil, err
}
// connect.
conn, err := net.ListenUDP("udp4", laddr)
if err != nil {
return nil, err
}
// configure socket to use with multicast.
2021-10-19 13:43:41 +00:00
pconn, iflist, err := newIPv4MulticastConn(conn)
2019-06-09 07:24:20 +00:00
if err != nil {
conn.Close()
return nil, err
}
return &multicastConn{
laddr: laddr,
conn: conn,
pconn: pconn,
iflist: iflist,
}, nil
}
2021-10-19 13:43:41 +00:00
func newIPv4MulticastConn(conn *net.UDPConn) (*ipv4.PacketConn, []net.Interface, error) {
iflist, err := interfaces()
if err != nil {
return nil, nil, err
}
addr, err := multicastSendAddr()
if err != nil {
return nil, nil, err
}
pconn, err := joinGroupIPv4(conn, iflist, addr)
if err != nil {
return nil, nil, err
}
return pconn, iflist, nil
}
2019-06-09 07:24:20 +00:00
// joinGroupIPv4 makes the connection join to a group on interfaces.
func joinGroupIPv4(conn *net.UDPConn, iflist []net.Interface, gaddr net.Addr) (*ipv4.PacketConn, error) {
wrap := ipv4.NewPacketConn(conn)
wrap.SetMulticastLoopback(true)
// add interfaces to multicast group.
joined := 0
for _, ifi := range iflist {
if err := wrap.JoinGroup(&ifi, gaddr); err != nil {
logf("failed to join group %s on %s: %s", gaddr.String(), ifi.Name, err)
continue
}
joined++
logf("joined group %s on %s", gaddr.String(), ifi.Name)
}
if joined == 0 {
return nil, errors.New("no interfaces had joined to group")
}
return wrap, nil
}
func (mc *multicastConn) Close() error {
if err := mc.pconn.Close(); err != nil {
return err
}
2021-10-19 13:43:41 +00:00
// mc.conn is closed by mc.pconn.Close()
2019-06-09 07:24:20 +00:00
return nil
}
func (mc *multicastConn) WriteTo(data []byte, to net.Addr) (int, error) {
if uaddr, ok := to.(*net.UDPAddr); ok && !uaddr.IP.IsMulticast() {
return mc.conn.WriteTo(data, to)
}
for _, ifi := range mc.iflist {
if err := mc.pconn.SetMulticastInterface(&ifi); err != nil {
return 0, err
}
if _, err := mc.pconn.WriteTo(data, nil, to); err != nil {
return 0, err
}
}
return len(data), nil
}
func (mc *multicastConn) LocalAddr() net.Addr {
return mc.laddr
}
func (mc *multicastConn) readPackets(timeout time.Duration, h packetHandler) error {
buf := make([]byte, 65535)
if timeout > 0 {
mc.pconn.SetReadDeadline(time.Now().Add(timeout))
}
for {
n, _, addr, err := mc.pconn.ReadFrom(buf)
if err != nil {
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
return nil
}
2021-10-19 13:43:41 +00:00
if strings.Contains(err.Error(), "use of closed network connection") {
return io.EOF
}
2019-06-09 07:24:20 +00:00
return err
}
if err := h(addr, buf[:n]); err != nil {
return err
}
}
}