go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/network/resources/domain/name.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package domain
     5  
     6  import (
     7  	"net/url"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"golang.org/x/net/publicsuffix"
    12  )
    13  
    14  // Domain Names https://datatracker.ietf.org/doc/html/rfc1035
    15  // Public Suffix List https://publicsuffix.org
    16  // Reserved Top-Level Domains https://datatracker.ietf.org/doc/html/rfc2606
    17  
    18  // Domain embeds net/url and adds extra fields ontop
    19  type Name struct {
    20  	Host                string
    21  	Port                int
    22  	EffectiveTLDPlusOne string
    23  	TLD                 string
    24  	IcannManagedTLD     bool
    25  	Labels              []string
    26  }
    27  
    28  // Parse mirrors net/url.Parse except instead it returns
    29  // a tld.URL, which contains extra fields.
    30  func Parse(fqdn string) (Name, error) {
    31  	// check if fqdn has a scheme otherwise go does not parse the host properly
    32  	if !strings.Contains(fqdn, "//") {
    33  		fqdn = "//" + fqdn
    34  	}
    35  
    36  	url, err := url.Parse(fqdn)
    37  	if err != nil {
    38  		return Name{}, err
    39  	}
    40  	if url.Host == "" {
    41  		return Name{}, nil
    42  	}
    43  	host, port := SplitHostPort(url.Host)
    44  
    45  	// effective top-level domain + one label
    46  	etldplusone, err := publicsuffix.EffectiveTLDPlusOne(host)
    47  	if err != nil {
    48  		return Name{}, err
    49  	}
    50  
    51  	suffix, icann := publicsuffix.PublicSuffix(strings.ToLower(host))
    52  	if err != nil {
    53  		return Name{}, err
    54  	}
    55  
    56  	return Name{
    57  		Host:                host,
    58  		Port:                port,
    59  		EffectiveTLDPlusOne: etldplusone,
    60  		TLD:                 suffix,
    61  		IcannManagedTLD:     icann,
    62  		Labels:              strings.Split(host, "."),
    63  	}, nil
    64  }
    65  
    66  // SplitHostPort separates host and port. If the port is not valid, it returns
    67  // the entire input as host, and it doesn't check the validity of the host.
    68  // Unlike net.SplitHostPort, but per RFC 3986, it requires ports to be numeric.
    69  // NOTE: method is copied from go package url under BSD license
    70  func SplitHostPort(hostPort string) (host string, port int) {
    71  	host = hostPort
    72  
    73  	colon := strings.LastIndexByte(host, ':')
    74  	if colon != -1 && validOptionalPort(host[colon:]) {
    75  		var sPort string
    76  		host, sPort = host[:colon], host[colon+1:]
    77  		port, _ = strconv.Atoi(sPort)
    78  	}
    79  
    80  	if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
    81  		host = host[1 : len(host)-1]
    82  	}
    83  
    84  	return
    85  }
    86  
    87  // validOptionalPort reports whether port is either an empty string
    88  // or matches /^:\d*$/
    89  // NOTE: method is copied from go package url under BSD license
    90  func validOptionalPort(port string) bool {
    91  	if port == "" {
    92  		return true
    93  	}
    94  	if port[0] != ':' {
    95  		return false
    96  	}
    97  	for _, b := range port[1:] {
    98  		if b < '0' || b > '9' {
    99  			return false
   100  		}
   101  	}
   102  	return true
   103  }