113 lines
2.2 KiB
Go
113 lines
2.2 KiB
Go
package iface
|
|
|
|
import (
|
|
"errors"
|
|
"net"
|
|
"time"
|
|
|
|
"github.com/Dreamacro/clash/common/singledo"
|
|
)
|
|
|
|
type Interface struct {
|
|
Index int
|
|
Name string
|
|
Addrs []*net.IPNet
|
|
HardwareAddr net.HardwareAddr
|
|
}
|
|
|
|
var ErrIfaceNotFound = errors.New("interface not found")
|
|
var ErrAddrNotFound = errors.New("addr not found")
|
|
|
|
var interfaces = singledo.NewSingle(time.Second * 20)
|
|
|
|
func ResolveInterface(name string) (*Interface, error) {
|
|
value, err, _ := interfaces.Do(func() (interface{}, error) {
|
|
ifaces, err := net.Interfaces()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
r := map[string]*Interface{}
|
|
|
|
for _, iface := range ifaces {
|
|
addrs, err := iface.Addrs()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
ipNets := make([]*net.IPNet, 0, len(addrs))
|
|
for _, addr := range addrs {
|
|
ipNet := addr.(*net.IPNet)
|
|
if v4 := ipNet.IP.To4(); v4 != nil {
|
|
ipNet.IP = v4
|
|
}
|
|
|
|
ipNets = append(ipNets, ipNet)
|
|
}
|
|
|
|
r[iface.Name] = &Interface{
|
|
Index: iface.Index,
|
|
Name: iface.Name,
|
|
Addrs: ipNets,
|
|
HardwareAddr: iface.HardwareAddr,
|
|
}
|
|
}
|
|
|
|
return r, nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ifaces := value.(map[string]*Interface)
|
|
iface, ok := ifaces[name]
|
|
if !ok {
|
|
return nil, ErrIfaceNotFound
|
|
}
|
|
|
|
return iface, nil
|
|
}
|
|
|
|
func FlushCache() {
|
|
interfaces.Reset()
|
|
}
|
|
|
|
func (iface *Interface) PickIPv4Addr(destination net.IP) (*net.IPNet, error) {
|
|
return iface.pickIPAddr(destination, func(addr *net.IPNet) bool {
|
|
return addr.IP.To4() != nil
|
|
})
|
|
}
|
|
|
|
func (iface *Interface) PickIPv6Addr(destination net.IP) (*net.IPNet, error) {
|
|
return iface.pickIPAddr(destination, func(addr *net.IPNet) bool {
|
|
return addr.IP.To4() == nil
|
|
})
|
|
}
|
|
|
|
func (iface *Interface) pickIPAddr(destination net.IP, accept func(addr *net.IPNet) bool) (*net.IPNet, error) {
|
|
var fallback *net.IPNet
|
|
|
|
for _, addr := range iface.Addrs {
|
|
if !accept(addr) {
|
|
continue
|
|
}
|
|
|
|
if fallback == nil && !addr.IP.IsLinkLocalUnicast() {
|
|
fallback = addr
|
|
|
|
if destination == nil {
|
|
break
|
|
}
|
|
}
|
|
|
|
if destination != nil && addr.Contains(destination) {
|
|
return addr, nil
|
|
}
|
|
}
|
|
|
|
if fallback == nil {
|
|
return nil, ErrAddrNotFound
|
|
}
|
|
|
|
return fallback, nil
|
|
}
|