93 lines
1.8 KiB
Go
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
|
||
|
|
}
|
||
|
|
}
|