unarr/internal/engine/upnp_debug_test.go
Deivid Soto aa6acbabc9 feat(stream): add NAT-PMP port mapping for remote downloads
Replace anacrolix/upnp with huin/goupnp + custom NAT-PMP (RFC 6886)
implementation. NAT-PMP is tried first (faster, more compatible with
TP-Link routers), with UPnP-IGD SOAP as fallback. Gateway detection
reads /proc/net/route for accuracy. Includes unit tests with mock
NAT-PMP server and permanent e2e tests (build tag manual).
2026-04-06 10:09:07 +02:00

127 lines
3.6 KiB
Go

//go:build manual
package engine
import (
"context"
"fmt"
"net"
"testing"
"time"
"github.com/huin/goupnp"
"github.com/huin/goupnp/dcps/internetgateway2"
)
// TestUPnPDebug performs detailed UPnP discovery diagnostics.
// Run with: go test -tags manual -run TestUPnPDebug -v ./internal/engine/
func TestUPnPDebug(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
fmt.Println("=== UPnP Debug Diagnostics ===")
fmt.Println()
// 1. Check network interfaces
fmt.Println("--- Network Interfaces ---")
ifaces, _ := net.Interfaces()
for _, iface := range ifaces {
if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
continue
}
addrs, _ := iface.Addrs()
for _, addr := range addrs {
fmt.Printf(" %s: %s (flags: %s)\n", iface.Name, addr, iface.Flags)
}
}
fmt.Println()
// 2. Raw SSDP discovery — search for ALL UPnP root devices
fmt.Println("--- Raw SSDP Discovery (all root devices) ---")
devices, err := goupnp.DiscoverDevicesCtx(ctx, "upnp:rootdevice")
if err != nil {
fmt.Printf(" Error: %v\n", err)
} else {
fmt.Printf(" Found %d root device(s)\n", len(devices))
for i, dev := range devices {
if dev.Err != nil {
fmt.Printf(" [%d] Error: %v\n", i, dev.Err)
continue
}
rd := dev.Root
fmt.Printf(" [%d] %s — %s (%s)\n", i, rd.Device.FriendlyName, rd.Device.DeviceType, rd.URLBase.String())
// List services
for _, svc := range rd.Device.Services {
fmt.Printf(" Service: %s\n", svc.ServiceType)
}
// List sub-devices
for _, sub := range rd.Device.Devices {
fmt.Printf(" SubDevice: %s — %s\n", sub.FriendlyName, sub.DeviceType)
for _, svc := range sub.Services {
fmt.Printf(" Service: %s\n", svc.ServiceType)
}
for _, sub2 := range sub.Devices {
fmt.Printf(" SubDevice: %s — %s\n", sub2.FriendlyName, sub2.DeviceType)
for _, svc := range sub2.Services {
fmt.Printf(" Service: %s\n", svc.ServiceType)
}
}
}
}
}
fmt.Println()
// 3. Try specific IGD service types
fmt.Println("--- IGD Service Discovery ---")
fmt.Print(" WANIPConnection2: ")
c2, errs2, err2 := internetgateway2.NewWANIPConnection2ClientsCtx(ctx)
if err2 != nil {
fmt.Printf("error: %v\n", err2)
} else {
fmt.Printf("%d client(s), %d error(s)\n", len(c2), len(errs2))
for _, e := range errs2 {
fmt.Printf(" err: %v\n", e)
}
for _, c := range c2 {
ip, err := c.GetExternalIPAddress()
fmt.Printf(" device=%s external_ip=%s err=%v\n",
c.ServiceClient.RootDevice.Device.FriendlyName, ip, err)
}
}
fmt.Print(" WANIPConnection1: ")
c1, errs1, err1 := internetgateway2.NewWANIPConnection1ClientsCtx(ctx)
if err1 != nil {
fmt.Printf("error: %v\n", err1)
} else {
fmt.Printf("%d client(s), %d error(s)\n", len(c1), len(errs1))
for _, e := range errs1 {
fmt.Printf(" err: %v\n", e)
}
for _, c := range c1 {
ip, err := c.GetExternalIPAddress()
fmt.Printf(" device=%s external_ip=%s err=%v\n",
c.ServiceClient.RootDevice.Device.FriendlyName, ip, err)
}
}
fmt.Print(" WANPPPConnection1: ")
cp, errsp, errp := internetgateway2.NewWANPPPConnection1ClientsCtx(ctx)
if errp != nil {
fmt.Printf("error: %v\n", errp)
} else {
fmt.Printf("%d client(s), %d error(s)\n", len(cp), len(errsp))
for _, e := range errsp {
fmt.Printf(" err: %v\n", e)
}
for _, c := range cp {
ip, err := c.GetExternalIPAddress()
fmt.Printf(" device=%s external_ip=%s err=%v\n",
c.ServiceClient.RootDevice.Device.FriendlyName, ip, err)
}
}
fmt.Println()
fmt.Println("=== Done ===")
}