From 105c744ed2138c85167c2d8b66cb3830694fba24 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Sat, 21 Jun 2025 16:07:53 -0300 Subject: [PATCH] feat: add IPv6 support for SSDP This starts SSDP servers on IPv6 link-local and site-local addresses in addition to the original server for IPv4. --- dlna/dms/dms.go | 25 +++++++++++++--------- ssdp/ssdp.go | 56 ++++++++++++++++++++++++++++++++++++------------- 2 files changed, 56 insertions(+), 25 deletions(-) diff --git a/dlna/dms/dms.go b/dlna/dms/dms.go index 26db29e..4c71797 100644 --- a/dlna/dms/dms.go +++ b/dlna/dms/dms.go @@ -172,23 +172,28 @@ const ssdpInterfaceFlags = net.FlagUp | net.FlagMulticast func (me *Server) doSSDP() { var wg sync.WaitGroup for _, if_ := range me.Interfaces { - if_ := if_ - wg.Add(1) - go func() { - defer wg.Done() - me.ssdpInterface(if_) - }() + for _, addr := range []string{ssdp.AddrString, ssdp.AddrString6LL, ssdp.AddrString6SL} { + if_ := if_ + addr := addr + wg.Add(1) + go func() { + defer wg.Done() + me.ssdpInterface(if_, addr) + }() + } } wg.Wait() } // Run SSDP server on an interface. -func (me *Server) ssdpInterface(if_ net.Interface) { +func (me *Server) ssdpInterface(if_ net.Interface, addrString string) { logger := me.Logger.WithNames("ssdp", if_.Name) s := ssdp.Server{ - Interface: if_, - Devices: devices(), - Services: serviceTypes(), + Interface: if_, + AddrString: addrString, + NetAddr: ssdp.AddrString2NetAdd[addrString], + Devices: devices(), + Services: serviceTypes(), Location: func(ip net.IP) string { return me.location(ip) }, diff --git a/ssdp/ssdp.go b/ssdp/ssdp.go index 2b83dca..ce49189 100644 --- a/ssdp/ssdp.go +++ b/ssdp/ssdp.go @@ -15,17 +15,23 @@ import ( "github.com/anacrolix/log" "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" ) const ( - AddrString = "239.255.255.250:1900" - rootDevice = "upnp:rootdevice" - aliveNTS = "ssdp:alive" - byebyeNTS = "ssdp:byebye" - mxMax = 10 + AddrString = "239.255.255.250:1900" + AddrString6LL = "[ff02::c]:1900" + AddrString6SL = "[ff05::c]:1900" + rootDevice = "upnp:rootdevice" + aliveNTS = "ssdp:alive" + byebyeNTS = "ssdp:byebye" + mxMax = 10 ) var NetAddr *net.UDPAddr +var NetAddr6LL *net.UDPAddr +var NetAddr6SL *net.UDPAddr +var AddrString2NetAdd map[string]*net.UDPAddr = make(map[string]*net.UDPAddr, 3) func init() { var err error @@ -33,6 +39,17 @@ func init() { if err != nil { log.Printf("Could not resolve %s: %s", AddrString, err) } + NetAddr6LL, err = net.ResolveUDPAddr("udp6", AddrString6LL) + if err != nil { + log.Printf("Could not resolve %s: %s", AddrString6LL, err) + } + NetAddr6SL, err = net.ResolveUDPAddr("udp6", AddrString6SL) + if err != nil { + log.Printf("Could not resolve %s: %s", AddrString6SL, err) + } + AddrString2NetAdd[AddrString] = NetAddr + AddrString2NetAdd[AddrString6LL] = NetAddr6LL + AddrString2NetAdd[AddrString6SL] = NetAddr6SL } type badStringError struct { @@ -81,6 +98,8 @@ func ReadRequest(b *bufio.Reader) (req *http.Request, err error) { type Server struct { conn *net.UDPConn Interface net.Interface + AddrString string + NetAddr *net.UDPAddr Server string Services []string Devices []string @@ -92,14 +111,21 @@ type Server struct { Logger log.Logger } -func makeConn(ifi net.Interface) (ret *net.UDPConn, err error) { - ret, err = net.ListenMulticastUDP("udp", &ifi, NetAddr) +func makeConn(ifi net.Interface, netAddr *net.UDPAddr) (ret *net.UDPConn, err error) { + ret, err = net.ListenMulticastUDP("udp", &ifi, netAddr) if err != nil { return } - p := ipv4.NewPacketConn(ret) - if err := p.SetMulticastTTL(2); err != nil { - log.Print(err) + if netAddr.IP.String() == AddrString { + p := ipv4.NewPacketConn(ret) + if err := p.SetMulticastTTL(2); err != nil { + log.Print(err) + } + } else { + p := ipv6.NewPacketConn(ret) + if err := p.SetMulticastHopLimit(2); err != nil { + log.Print(err) + } } // if err := p.SetMulticastLoopback(true); err != nil { // log.Println(err) @@ -132,7 +158,7 @@ func (me *Server) serve() { func (me *Server) Init() (err error) { me.closed = make(chan struct{}) - me.conn, err = makeConn(me.Interface) + me.conn, err = makeConn(me.Interface, me.NetAddr) if me.IPFilter == nil { me.IPFilter = func(net.IP) bool { return true } } @@ -195,7 +221,7 @@ func (me *Server) usnFromTarget(target string) string { func (me *Server) makeNotifyMessage(target, nts string, extraHdrs [][2]string) []byte { lines := [...][2]string{ - {"HOST", AddrString}, + {"HOST", me.AddrString}, {"NT", target}, {"NTS", nts}, {"SERVER", me.Server}, @@ -246,7 +272,7 @@ func (me *Server) log(args ...interface{}) { func (me *Server) sendByeBye() { for _, type_ := range me.allTypes() { buf := me.makeNotifyMessage(type_, byebyeNTS, nil) - me.send(buf, NetAddr) + me.send(buf, me.NetAddr) } } @@ -254,7 +280,7 @@ func (me *Server) notifyAll(nts string, extraHdrs [][2]string) { for _, type_ := range me.allTypes() { buf := me.makeNotifyMessage(type_, nts, extraHdrs) delay := time.Duration(rand.Int63n(int64(100 * time.Millisecond))) - me.delayedSend(delay, buf, NetAddr) + me.delayedSend(delay, buf, me.NetAddr) } } @@ -279,7 +305,7 @@ func (me *Server) handle(buf []byte, sender *net.UDPAddr) { return } var mx int64 - if req.Header.Get("Host") == AddrString { + if req.Header.Get("Host") == me.AddrString { mxHeader := req.Header.Get("mx") i, err := strconv.ParseUint(mxHeader, 0, 0) if err != nil {