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

156 lines
3.5 KiB
Go
Raw Normal View History

2023-05-19 20:23:55 +00:00
package multicast
2019-06-09 07:24:20 +00:00
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"
2023-05-19 20:23:55 +00:00
"github.com/koron/go-ssdp/internal/ssdplog"
2019-06-09 07:24:20 +00:00
"golang.org/x/net/ipv4"
)
2023-05-19 20:23:55 +00:00
// Conn is multicast connection.
type Conn struct {
2019-06-09 07:24:20 +00:00
laddr *net.UDPAddr
conn *net.UDPConn
pconn *ipv4.PacketConn
iflist []net.Interface
}
2023-05-19 20:23:55 +00:00
// Listen starts to receiving multicast messages.
func Listen(r *AddrResolver) (*Conn, 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
}
2023-05-19 20:23:55 +00:00
return &Conn{
2019-06-09 07:24:20 +00:00
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
}
2023-05-19 20:23:55 +00:00
addr, err := SendAddr()
2021-10-19 13:43:41 +00:00
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 {
2023-05-19 20:23:55 +00:00
ssdplog.Printf("failed to join group %s on %s: %s", gaddr.String(), ifi.Name, err)
2019-06-09 07:24:20 +00:00
continue
}
joined++
2023-05-19 20:23:55 +00:00
ssdplog.Printf("joined group %s on %s (#%d)", gaddr.String(), ifi.Name, ifi.Index)
2019-06-09 07:24:20 +00:00
}
if joined == 0 {
return nil, errors.New("no interfaces had joined to group")
}
return wrap, nil
}
2023-05-19 20:23:55 +00:00
// Close closes a multicast connection.
func (mc *Conn) Close() error {
2019-06-09 07:24:20 +00:00
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
}
2023-05-19 20:23:55 +00:00
// DataProvider provides a body of multicast message to send.
type DataProvider interface {
Bytes(*net.Interface) []byte
}
//type multicastDataProviderFunc func(*net.Interface) []byte
//
//func (f multicastDataProviderFunc) Bytes(ifi *net.Interface) []byte {
// return f(ifi)
//}
type BytesDataProvider []byte
func (b BytesDataProvider) Bytes(ifi *net.Interface) []byte {
return []byte(b)
}
// WriteTo sends a multicast message to interfaces.
func (mc *Conn) WriteTo(dataProv DataProvider, to net.Addr) (int, error) {
2019-06-09 07:24:20 +00:00
if uaddr, ok := to.(*net.UDPAddr); ok && !uaddr.IP.IsMulticast() {
2023-05-19 20:23:55 +00:00
return mc.conn.WriteTo(dataProv.Bytes(nil), to)
2019-06-09 07:24:20 +00:00
}
2023-05-19 20:23:55 +00:00
sum := 0
2019-06-09 07:24:20 +00:00
for _, ifi := range mc.iflist {
if err := mc.pconn.SetMulticastInterface(&ifi); err != nil {
return 0, err
}
2023-05-19 20:23:55 +00:00
n, err := mc.pconn.WriteTo(dataProv.Bytes(&ifi), nil, to)
if err != nil {
2019-06-09 07:24:20 +00:00
return 0, err
}
2023-05-19 20:23:55 +00:00
sum += n
2019-06-09 07:24:20 +00:00
}
2023-05-19 20:23:55 +00:00
return sum, nil
2019-06-09 07:24:20 +00:00
}
2023-05-19 20:23:55 +00:00
// LocalAddr returns local address to listen multicast packets.
func (mc *Conn) LocalAddr() net.Addr {
2019-06-09 07:24:20 +00:00
return mc.laddr
}
2023-05-19 20:23:55 +00:00
// ReadPackets reads multicast packets.
func (mc *Conn) ReadPackets(timeout time.Duration, h PacketHandler) error {
2019-06-09 07:24:20 +00:00
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
}
}
}