From 2a5355b1f8c0fe99a5b92f127f5adacaf8bef571 Mon Sep 17 00:00:00 2001 From: Alex X Date: Wed, 26 Feb 2025 21:34:46 +0300 Subject: [PATCH] Fix WebRTC server with static UDP port --- pkg/mdns/client.go | 13 ++--------- pkg/net2/net.go | 32 +++++++++++++++++++++++++++ pkg/webrtc/api.go | 55 ++++++++++++++++++++++++++++++++-------------- 3 files changed, 72 insertions(+), 28 deletions(-) create mode 100644 pkg/net2/net.go diff --git a/pkg/mdns/client.go b/pkg/mdns/client.go index 6816f919..0e74952f 100644 --- a/pkg/mdns/client.go +++ b/pkg/mdns/client.go @@ -10,6 +10,7 @@ import ( "syscall" "time" + "github.com/AlexxIT/go2rtc/pkg/net2" "github.com/miekg/dns" // awesome library for parsing mDNS records ) @@ -370,16 +371,6 @@ func NewServiceEntries(msg *dns.Msg, ip net.IP) (entries []*ServiceEntry) { return } -// Common docker addresses (class B): -// https://en.wikipedia.org/wiki/Private_network -// - docker0 172.17.0.1/16 -// - br-xxxx 172.18.0.1/16 -// - hassio 172.30.32.1/23 -var docker = net.IPNet{ - IP: []byte{172, 16, 0, 0}, - Mask: []byte{255, 240, 0, 0}, -} - func IPNets() ([]*net.IPNet, error) { intfs, err := net.Interfaces() if err != nil { @@ -397,7 +388,7 @@ func IPNets() ([]*net.IPNet, error) { for _, addr := range addrs { switch v := addr.(type) { case *net.IPNet: - if ip := v.IP.To4(); ip != nil && !docker.Contains(ip) { + if ip := v.IP.To4(); ip != nil && !net2.Docker.Contains(ip) { nets = append(nets, v) } } diff --git a/pkg/net2/net.go b/pkg/net2/net.go new file mode 100644 index 00000000..6d02ad89 --- /dev/null +++ b/pkg/net2/net.go @@ -0,0 +1,32 @@ +package net2 + +import ( + "net" + "strconv" +) + +// Docker has common docker addresses (class B): +// https://en.wikipedia.org/wiki/Private_network +// - docker0 172.17.0.1/16 +// - br-xxxx 172.18.0.1/16 +// - hassio 172.30.32.1/23 +var Docker = net.IPNet{ + IP: []byte{172, 16, 0, 0}, + Mask: []byte{255, 240, 0, 0}, +} + +// ParseUnspecifiedPort will return port if address is unspecified +// ex. ":8555" or "0.0.0.0:8555" +func ParseUnspecifiedPort(address string) int { + host, port, err := net.SplitHostPort(address) + if err != nil { + return 0 + } + + if host != "" && host != "0.0.0.0" && host != "[::]" { + return 0 + } + + i, _ := strconv.Atoi(port) + return i +} diff --git a/pkg/webrtc/api.go b/pkg/webrtc/api.go index 0361e6b4..7fb68af7 100644 --- a/pkg/webrtc/api.go +++ b/pkg/webrtc/api.go @@ -4,6 +4,8 @@ import ( "net" "github.com/AlexxIT/go2rtc/pkg/core" + "github.com/AlexxIT/go2rtc/pkg/net2" + "github.com/pion/ice/v2" "github.com/pion/interceptor" "github.com/pion/webrtc/v3" ) @@ -44,39 +46,44 @@ func NewServerAPI(network, address string, filters *Filters) (*webrtc.API, error // fix https://github.com/pion/webrtc/pull/2407 s.SetDTLSInsecureSkipHelloVerify(true) + var interfaceFilter func(name string) bool if filters != nil && filters.Interfaces != nil { - s.SetIncludeLoopbackCandidate(true) - s.SetInterfaceFilter(func(name string) bool { + interfaceFilter = func(name string) bool { return core.Contains(filters.Interfaces, name) - }) + } } else { - // disable listen on Hassio docker interfaces - s.SetInterfaceFilter(func(name string) bool { - return name != "hassio" && name != "docker0" - }) + // default interfaces - all, except loopback } + s.SetInterfaceFilter(interfaceFilter) + var ipFilter func(ip net.IP) bool if filters != nil && filters.IPs != nil { - s.SetIncludeLoopbackCandidate(true) - s.SetIPFilter(func(ip net.IP) bool { + ipFilter = func(ip net.IP) bool { return core.Contains(filters.IPs, ip.String()) - }) + } + } else { + // default ips - all, except loopback and docker + ipFilter = func(ip net.IP) bool { + return !net2.Docker.Contains(ip) + } } + s.SetIPFilter(ipFilter) + var networkTypes []webrtc.NetworkType if filters != nil && filters.Networks != nil { - var networkTypes []webrtc.NetworkType for _, s := range filters.Networks { if networkType, err := webrtc.NewNetworkType(s); err == nil { networkTypes = append(networkTypes, networkType) } } - s.SetNetworkTypes(networkTypes) } else { - s.SetNetworkTypes([]webrtc.NetworkType{ + // default network types - all + networkTypes = []webrtc.NetworkType{ webrtc.NetworkTypeUDP4, webrtc.NetworkTypeUDP6, webrtc.NetworkTypeTCP4, webrtc.NetworkTypeTCP6, - }) + } } + s.SetNetworkTypes(networkTypes) if filters != nil && len(filters.UDPPorts) == 2 { _ = s.SetEphemeralUDPPortRange(filters.UDPPorts[0], filters.UDPPorts[1]) @@ -100,10 +107,24 @@ func NewServerAPI(network, address string, filters *Filters) (*webrtc.API, error } if network == "" || network == "udp" { - if ln, err := net.ListenPacket("udp", address); err == nil { - udpMux := webrtc.NewICEUDPMux(nil, ln) - s.SetICEUDPMux(udpMux) + // UDPMuxDefault should not listening on unspecified address, use NewMultiUDPMuxFromPort instead + var udpMux ice.UDPMux + if port := net2.ParseUnspecifiedPort(address); port != 0 { + var networks []ice.NetworkType + for _, ntype := range networkTypes { + networks = append(networks, ice.NetworkType(ntype)) + } + + udpMux, _ = ice.NewMultiUDPMuxFromPort( + port, + ice.UDPMuxFromPortWithInterfaceFilter(interfaceFilter), + ice.UDPMuxFromPortWithIPFilter(ipFilter), + ice.UDPMuxFromPortWithNetworks(networks...), + ) + } else if ln, err := net.ListenPacket("udp", address); err == nil { + udpMux = ice.NewUDPMuxDefault(ice.UDPMuxParams{UDPConn: ln}) } + s.SetICEUDPMux(udpMux) } }