package common import ( "context" "encoding/json" "errors" "fmt" "io/fs" "net" "os" "regexp" "strings" "syscall" "time" "unicode/utf8" ) const ( httpTimeout = 5 * time.Minute certsPath = "/etc/containers/certs.d" homeCertsDir = ".config/containers/certs.d" ClientCertFilename = "client.cert" ClientKeyFilename = "client.key" CaCertFilename = "ca.crt" CosignSignature = "cosign" CosignSigKey = "dev.cosignproject.cosign/signature" NotationSignature = "notation" // same value as github.com/notaryproject/notation-go/registry.ArtifactTypeNotation (assert by internal test). // reason used: to reduce zot minimal binary size (otherwise adds oras.land/oras-go/v2 deps). ArtifactTypeNotation = "application/vnd.cncf.notary.signature" ArtifactTypeCosign = "application/vnd.dev.cosign.artifact.sig.v1+json" ) var cosignTagRule = regexp.MustCompile(`sha256\-.+\.sig`) func IsCosignTag(tag string) bool { return cosignTagRule.MatchString(tag) } func Contains[T comparable](elems []T, v T) bool { for _, s := range elems { if v == s { return true } } return false } // first match of item in []. func Index(slice []string, item string) int { for k, v := range slice { if item == v { return k } } return -1 } // remove matches of item in []. func RemoveFrom(inputSlice []string, item string) []string { var newSlice []string for _, v := range inputSlice { if item != v { newSlice = append(newSlice, v) } } return newSlice } func TypeOf(v interface{}) string { return fmt.Sprintf("%T", v) } func DirExists(d string) bool { if !utf8.ValidString(d) { return false } fileInfo, err := os.Stat(d) if err != nil { if e, ok := err.(*fs.PathError); ok && errors.Is(e.Err, syscall.ENAMETOOLONG) || //nolint: errorlint errors.Is(e.Err, syscall.EINVAL) { return false } } if err != nil && os.IsNotExist(err) { return false } if !fileInfo.IsDir() { return false } return true } // Used to filter a json fields by using an intermediate struct. func MarshalThroughStruct(obj interface{}, throughStruct interface{}) ([]byte, error) { toJSON, err := json.Marshal(obj) if err != nil { return []byte{}, err } err = json.Unmarshal(toJSON, throughStruct) if err != nil { return []byte{}, err } toJSON, err = json.Marshal(throughStruct) if err != nil { return []byte{}, err } return toJSON, nil } func ContainsStringIgnoreCase(strSlice []string, str string) bool { for _, val := range strSlice { if strings.EqualFold(val, str) { return true } } return false } // this function will check if tag is a referrers tag // (https://github.com/opencontainers/distribution-spec/blob/main/spec.md#referrers-tag-schema). func IsReferrersTag(tag string) bool { referrersTagRule := regexp.MustCompile(`sha256\-[A-Za-z0-9]*$`) return referrersTagRule.MatchString(tag) } func IsContextDone(ctx context.Context) bool { select { case <-ctx.Done(): return true default: return false } } // get a list of IP addresses configured on the host's // interfaces. func GetLocalIPs() ([]string, error) { var localIPs []string ifaces, err := net.Interfaces() if err != nil { return []string{}, err } for _, i := range ifaces { addrs, err := i.Addrs() if err != nil { return localIPs, err } for _, addr := range addrs { if localIP, ok := addr.(*net.IPNet); ok { localIPs = append(localIPs, localIP.IP.String()) } } } return localIPs, nil } // get a list of listening sockets on the host (IP:port). // IPv6 is returned as [host]:port. func GetLocalSockets(port string) ([]string, error) { localIPs, err := GetLocalIPs() if err != nil { return []string{}, err } localSockets := make([]string, len(localIPs)) for idx, ip := range localIPs { // JoinHostPort automatically wraps IPv6 addresses in [] localSockets[idx] = net.JoinHostPort(ip, port) } return localSockets, nil } func GetIPFromHostName(host string) ([]string, error) { addrs, err := net.LookupIP(host) if err != nil { return []string{}, err } ips := make([]string, 0, len(addrs)) for _, ip := range addrs { ips = append(ips, ip.String()) } return ips, nil } // checks if 2 sockets are equal at the host port level. func AreSocketsEqual(socketA string, socketB string) (bool, error) { hostA, portA, err := net.SplitHostPort(socketA) if err != nil { return false, err } hostB, portB, err := net.SplitHostPort(socketB) if err != nil { return false, err } hostAIP := net.ParseIP(hostA) if hostAIP == nil { // this could be a fully-qualified domain name (FQDN) // for FQDN, just a normal compare is enough return hostA == hostB, nil } hostBIP := net.ParseIP(hostB) if hostBIP == nil { // if the host part of socketA was parsed successfully, it was an IP // if the host part of socketA was an FQDN, then the comparison is // already done as the host of socketB is also assumed to be an FQDN. // since the parsing failed, assume that A and B are not equal. return false, nil } return (hostAIP.Equal(hostBIP) && (portA == portB)), nil }