Files
pmomusic/internal/netutils/ip_detect.go
Eric Coissac 2e80eb85d2 Changes to be committed:
modified:   cmd/pmomusic/main.go
	new file:   internal/netutils/ip_detect.go
	new file:   internal/netutils/list_all_ip.go
	modified:   internal/soap/soap.go
	modified:   internal/ssdp/responder.go
	modified:   internal/ssdp/ssdp.go
	modified:   internal/upnp/http.go
	new file:   internal/upnp/server.go
	new file:   internal/upnp/xml/AVTransport.xml
	new file:   internal/upnp/xml/ConnectionManager.xml
	new file:   internal/upnp/xml/RenderingControl.xml
2025-06-08 17:41:52 +02:00

93 lines
1.8 KiB
Go

package netutils
import (
"errors"
"net"
"sort"
"strings"
)
// GuessLocalIP returns the best-guess local IP address usable for UPnP location,
// in order of preference: eth0 > en* > wl* > any private, non-loopback IP.
func GuessLocalIP() (string, error) {
ifaces, err := net.Interfaces()
if err != nil {
return "", err
}
type scoredIP struct {
ip net.IP
score int
}
var candidates []scoredIP
for _, iface := range ifaces {
// Skip interfaces that are down or loopback
if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
continue
}
addrs, err := iface.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
// Skip non-IPv4 or non-private addresses
if ip == nil || ip.IsLoopback() || ip.To4() == nil || !isPrivateIPv4(ip) {
continue
}
score := scoreInterfaceName(iface.Name)
candidates = append(candidates, scoredIP{ip: ip, score: score})
}
}
if len(candidates) == 0 {
return "", errors.New("no suitable local IP found")
}
// Prefer interfaces with higher score
sort.SliceStable(candidates, func(i, j int) bool {
return candidates[i].score > candidates[j].score
})
return candidates[0].ip.String(), nil
}
func isPrivateIPv4(ip net.IP) bool {
private := []string{
"10.", "172.16.", "172.17.", "172.18.", "172.19.", "172.2", "192.168.",
}
ipStr := ip.String()
for _, p := range private {
if strings.HasPrefix(ipStr, p) {
return true
}
}
return false
}
func scoreInterfaceName(name string) int {
switch {
case name == "eth0":
return 100
case strings.HasPrefix(name, "en"):
return 80
case name == "wlan0" || strings.HasPrefix(name, "wl"):
return 60
default:
return 10
}
}