package ice import ( "fmt" "math/rand" "net" "strings" "sync/atomic" "time" ) func localInterfaces(networkTypes []NetworkType) (ips []net.IP) { ifaces, err := net.Interfaces() if err != nil { return ips } var IPv4Requested, IPv6Requested bool for _, typ := range networkTypes { if typ.IsIPv4() { IPv4Requested = true } if typ.IsIPv6() { IPv6Requested = true } } for _, iface := range ifaces { if iface.Flags&net.FlagUp == 0 { continue // interface down } if iface.Flags&net.FlagLoopback != 0 { continue // loopback interface } addrs, err := iface.Addrs() if err != nil { return ips } for _, addr := range addrs { var ip net.IP switch addr := addr.(type) { case *net.IPNet: ip = addr.IP case *net.IPAddr: ip = addr.IP } if ip == nil || ip.IsLoopback() { continue } if ipv4 := ip.To4(); ipv4 == nil { if !IPv6Requested { continue } else if !isSupportedIPv6(ip) { continue } } else if !IPv4Requested { continue } ips = append(ips, ip) } } return ips } type atomicError struct{ v atomic.Value } func (a *atomicError) Store(err error) { a.v.Store(struct{ error }{err}) } func (a *atomicError) Load() error { err, _ := a.v.Load().(struct{ error }) return err.error } // The conditions of invalidation written below are defined in // https://tools.ietf.org/html/rfc8445#section-5.1.1.1 func isSupportedIPv6(ip net.IP) bool { if len(ip) != net.IPv6len || !isZeros(ip[0:12]) || // !(IPv4-compatible IPv6) ip[0] == 0xfe && ip[1]&0xc0 == 0xc0 || // !(IPv6 site-local unicast) ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() { return false } return true } func isZeros(ip net.IP) bool { for i := 0; i < len(ip); i++ { if ip[i] != 0 { return false } } return true } // RandSeq generates a random alpha numeric sequence of the requested length func randSeq(n int) string { r := rand.New(rand.NewSource(time.Now().UnixNano())) letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") b := make([]rune, n) for i := range b { b[i] = letters[r.Intn(len(letters))] } return string(b) } // flattenErrs flattens multiple errors into one func flattenErrs(errs []error) error { var errstrings []string for _, err := range errs { if err != nil { errstrings = append(errstrings, err.Error()) } } if len(errstrings) == 0 { return nil } return fmt.Errorf(strings.Join(errstrings, "\n")) }