github.com/chwjbn/xclash@v0.2.0/component/iface/iface.go (about) 1 package iface 2 3 import ( 4 "errors" 5 "net" 6 "time" 7 8 "github.com/chwjbn/xclash/common/singledo" 9 ) 10 11 type Interface struct { 12 Index int 13 Name string 14 Addrs []*net.IPNet 15 HardwareAddr net.HardwareAddr 16 } 17 18 var ( 19 ErrIfaceNotFound = errors.New("interface not found") 20 ErrAddrNotFound = errors.New("addr not found") 21 ) 22 23 var interfaces = singledo.NewSingle(time.Second * 20) 24 25 func ResolveInterface(name string) (*Interface, error) { 26 value, err, _ := interfaces.Do(func() (interface{}, error) { 27 ifaces, err := net.Interfaces() 28 if err != nil { 29 return nil, err 30 } 31 32 r := map[string]*Interface{} 33 34 for _, iface := range ifaces { 35 addrs, err := iface.Addrs() 36 if err != nil { 37 continue 38 } 39 40 ipNets := make([]*net.IPNet, 0, len(addrs)) 41 for _, addr := range addrs { 42 ipNet := addr.(*net.IPNet) 43 if v4 := ipNet.IP.To4(); v4 != nil { 44 ipNet.IP = v4 45 } 46 47 ipNets = append(ipNets, ipNet) 48 } 49 50 r[iface.Name] = &Interface{ 51 Index: iface.Index, 52 Name: iface.Name, 53 Addrs: ipNets, 54 HardwareAddr: iface.HardwareAddr, 55 } 56 } 57 58 return r, nil 59 }) 60 if err != nil { 61 return nil, err 62 } 63 64 ifaces := value.(map[string]*Interface) 65 iface, ok := ifaces[name] 66 if !ok { 67 return nil, ErrIfaceNotFound 68 } 69 70 return iface, nil 71 } 72 73 func FlushCache() { 74 interfaces.Reset() 75 } 76 77 func (iface *Interface) PickIPv4Addr(destination net.IP) (*net.IPNet, error) { 78 return iface.pickIPAddr(destination, func(addr *net.IPNet) bool { 79 return addr.IP.To4() != nil 80 }) 81 } 82 83 func (iface *Interface) PickIPv6Addr(destination net.IP) (*net.IPNet, error) { 84 return iface.pickIPAddr(destination, func(addr *net.IPNet) bool { 85 return addr.IP.To4() == nil 86 }) 87 } 88 89 func (iface *Interface) pickIPAddr(destination net.IP, accept func(addr *net.IPNet) bool) (*net.IPNet, error) { 90 var fallback *net.IPNet 91 92 for _, addr := range iface.Addrs { 93 if !accept(addr) { 94 continue 95 } 96 97 if fallback == nil && !addr.IP.IsLinkLocalUnicast() { 98 fallback = addr 99 100 if destination == nil { 101 break 102 } 103 } 104 105 if destination != nil && addr.Contains(destination) { 106 return addr, nil 107 } 108 } 109 110 if fallback == nil { 111 return nil, ErrAddrNotFound 112 } 113 114 return fallback, nil 115 }