mirror of
https://github.com/aler9/gortsplib
synced 2025-09-27 03:25:52 +08:00
198 lines
4.3 KiB
Go
198 lines
4.3 KiB
Go
//go:build linux
|
|
|
|
package multicast
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
// same size as GStreamer's rtspsrc
|
|
multicastTTL = 16
|
|
)
|
|
|
|
// https://cs.opensource.google/go/x/net/+/refs/tags/v0.15.0:ipv4/sys_asmreq.go;l=51
|
|
func setIPMreqInterface(mreq *syscall.IPMreq, ifi *net.Interface) error {
|
|
if ifi == nil {
|
|
return nil
|
|
}
|
|
ifat, err := ifi.Addrs()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, ifa := range ifat {
|
|
switch ifa := ifa.(type) {
|
|
case *net.IPAddr:
|
|
if ip := ifa.IP.To4(); ip != nil {
|
|
copy(mreq.Interface[:], ip)
|
|
return nil
|
|
}
|
|
case *net.IPNet:
|
|
if ip := ifa.IP.To4(); ip != nil {
|
|
copy(mreq.Interface[:], ip)
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
return fmt.Errorf("no such interface")
|
|
}
|
|
|
|
type rawConn struct {
|
|
fd uintptr
|
|
}
|
|
|
|
// Control implements syscall.RawConn.
|
|
func (c *rawConn) Control(f func(fd uintptr)) error {
|
|
f(c.fd)
|
|
return nil
|
|
}
|
|
|
|
// Read implements syscall.RawConn.
|
|
func (*rawConn) Read(_ func(fd uintptr) (done bool)) error {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// Write implements syscall.RawConn.
|
|
func (*rawConn) Write(_ func(fd uintptr) (done bool)) error {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// singleConn is a multicast connection
|
|
// that works on a single interface.
|
|
type singleConn struct {
|
|
addr *net.UDPAddr
|
|
file *os.File
|
|
conn net.PacketConn
|
|
}
|
|
|
|
// NewSingleConn allocates a singleConn.
|
|
func NewSingleConn(
|
|
intf *net.Interface,
|
|
address string,
|
|
_ func(network, address string) (net.PacketConn, error),
|
|
) (Conn, error) {
|
|
addr, err := net.ResolveUDPAddr("udp4", address)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sock, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = syscall.SetsockoptInt(sock, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
|
if err != nil {
|
|
syscall.Close(sock) //nolint:errcheck
|
|
return nil, err
|
|
}
|
|
|
|
err = syscall.SetsockoptString(sock, syscall.SOL_SOCKET, syscall.SO_BINDTODEVICE, intf.Name)
|
|
if err != nil {
|
|
syscall.Close(sock) //nolint:errcheck
|
|
return nil, err
|
|
}
|
|
|
|
var lsa syscall.SockaddrInet4
|
|
lsa.Port = addr.Port
|
|
copy(lsa.Addr[:], addr.IP.To4())
|
|
err = syscall.Bind(sock, &lsa)
|
|
if err != nil {
|
|
syscall.Close(sock) //nolint:errcheck
|
|
return nil, err
|
|
}
|
|
|
|
var mreq syscall.IPMreq
|
|
copy(mreq.Multiaddr[:], addr.IP.To4())
|
|
err = setIPMreqInterface(&mreq, intf)
|
|
if err != nil {
|
|
syscall.Close(sock) //nolint:errcheck
|
|
return nil, err
|
|
}
|
|
|
|
err = syscall.SetsockoptIPMreq(sock, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, &mreq)
|
|
if err != nil {
|
|
syscall.Close(sock) //nolint:errcheck
|
|
return nil, err
|
|
}
|
|
|
|
var mreqn syscall.IPMreqn
|
|
mreqn.Ifindex = int32(intf.Index)
|
|
|
|
err = syscall.SetsockoptIPMreqn(sock, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, &mreqn)
|
|
if err != nil {
|
|
syscall.Close(sock) //nolint:errcheck
|
|
return nil, err
|
|
}
|
|
|
|
err = syscall.SetsockoptInt(sock, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, multicastTTL)
|
|
if err != nil {
|
|
syscall.Close(sock) //nolint:errcheck
|
|
return nil, err
|
|
}
|
|
|
|
file := os.NewFile(uintptr(sock), "")
|
|
conn, err := net.FilePacketConn(file)
|
|
if err != nil {
|
|
file.Close()
|
|
return nil, err
|
|
}
|
|
|
|
return &singleConn{
|
|
addr: addr,
|
|
file: file,
|
|
conn: conn,
|
|
}, nil
|
|
}
|
|
|
|
// Close implements Conn.
|
|
func (c *singleConn) Close() error {
|
|
c.conn.Close()
|
|
c.file.Close()
|
|
return nil
|
|
}
|
|
|
|
// SetReadBuffer implements Conn.
|
|
func (c *singleConn) SetReadBuffer(bytes int) error {
|
|
return syscall.SetsockoptInt(int(c.file.Fd()), syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes)
|
|
}
|
|
|
|
// SyscallConn implements Conn.
|
|
func (c *singleConn) SyscallConn() (syscall.RawConn, error) {
|
|
return &rawConn{fd: c.file.Fd()}, nil
|
|
}
|
|
|
|
// LocalAddr implements Conn.
|
|
func (c *singleConn) LocalAddr() net.Addr {
|
|
return c.conn.LocalAddr()
|
|
}
|
|
|
|
// SetDeadline implements Conn.
|
|
func (c *singleConn) SetDeadline(_ time.Time) error {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// SetReadDeadline implements Conn.
|
|
func (c *singleConn) SetReadDeadline(t time.Time) error {
|
|
return c.conn.SetReadDeadline(t)
|
|
}
|
|
|
|
// SetWriteDeadline implements Conn.
|
|
func (c *singleConn) SetWriteDeadline(t time.Time) error {
|
|
return c.conn.SetWriteDeadline(t)
|
|
}
|
|
|
|
// WriteTo implements Conn.
|
|
func (c *singleConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
|
return c.conn.WriteTo(b, addr)
|
|
}
|
|
|
|
// ReadFrom implements Conn.
|
|
func (c *singleConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
|
return c.conn.ReadFrom(b)
|
|
}
|