1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
package util
import (
"fmt"
"net"
"slices"
"github.com/lxc/incus/v6/internal/ports"
)
// CanonicalNetworkAddress parses the given network address and returns a string of the form "host:port",
// possibly filling it with the default port if it's missing. It will also wrap a bare IPv6 address with square
// brackets if needed.
func CanonicalNetworkAddress(address string, defaultPort int) string {
host, port, err := net.SplitHostPort(address)
if err != nil {
ip := net.ParseIP(address)
if ip != nil {
// If the input address is a bare IP address, then convert it to a proper listen address
// using the canonical IP with default port and wrap IPv6 addresses in square brackets.
return net.JoinHostPort(ip.String(), fmt.Sprintf("%d", defaultPort))
}
// Otherwise assume this is either a host name or a partial address (e.g `[::]`) without
// a port number, so append the default port.
return fmt.Sprintf("%s:%d", address, defaultPort)
}
if port == "" && address[len(address)-1] == ':' {
// An address that ends with a trailing colon will be parsed as having an empty port.
return net.JoinHostPort(host, fmt.Sprintf("%d", defaultPort))
}
return address
}
// CanonicalNetworkAddressFromAddressAndPort returns a network address from separate address and port values.
// The address accepts values such as "[::]", "::" and "localhost".
func CanonicalNetworkAddressFromAddressAndPort(address string, port int, defaultPort int) string {
// Because we accept just the host part of an IPv6 listen address (e.g. `[::]`) don't use net.JoinHostPort.
// If a bare IP address is supplied then CanonicalNetworkAddress will use net.JoinHostPort if needed.
return CanonicalNetworkAddress(fmt.Sprintf("%s:%d", address, port), defaultPort)
}
// NetworkInterfaceAddress returns the first global unicast address of any of the system network interfaces.
// Return the empty string if none is found.
func NetworkInterfaceAddress() string {
ifaces, err := net.Interfaces()
if err != nil {
return ""
}
for _, iface := range ifaces {
addrs, err := iface.Addrs()
if err != nil {
continue
}
if len(addrs) == 0 {
continue
}
for _, addr := range addrs {
ipNet, ok := addr.(*net.IPNet)
if !ok {
continue
}
if !ipNet.IP.IsGlobalUnicast() {
continue
}
return ipNet.IP.String()
}
}
return ""
}
// IsAddressCovered detects if network address1 is actually covered by
// address2, in the sense that they are either the same address or address2 is
// specified using a wildcard with the same port of address1.
func IsAddressCovered(address1, address2 string) bool {
address1 = CanonicalNetworkAddress(address1, ports.HTTPSDefaultPort)
address2 = CanonicalNetworkAddress(address2, ports.HTTPSDefaultPort)
if address1 == address2 {
return true
}
host1, port1, err := net.SplitHostPort(address1)
if err != nil {
return false
}
host2, port2, err := net.SplitHostPort(address2)
if err != nil {
return false
}
// If the ports are different, then address1 is clearly not covered by
// address2.
if port2 != port1 {
return false
}
// If address1 contains a host name, let's try to resolve it, in order
// to compare the actual IPs.
var addresses1 []net.IP
if host1 != "" {
ip := net.ParseIP(host1)
if ip != nil {
addresses1 = append(addresses1, ip)
} else {
ips, err := net.LookupHost(host1)
if err == nil && len(ips) > 0 {
for _, ipStr := range ips {
ip := net.ParseIP(ipStr)
if ip != nil {
addresses1 = append(addresses1, ip)
}
}
}
}
}
// If address2 contains a host name, let's try to resolve it, in order
// to compare the actual IPs.
var addresses2 []net.IP
if host2 != "" {
ip := net.ParseIP(host2)
if ip != nil {
addresses2 = append(addresses2, ip)
} else {
ips, err := net.LookupHost(host2)
if err == nil && len(ips) > 0 {
for _, ipStr := range ips {
ip := net.ParseIP(ipStr)
if ip != nil {
addresses2 = append(addresses2, ip)
}
}
}
}
}
for _, a1 := range addresses1 {
if slices.ContainsFunc(addresses2, a1.Equal) {
return true
}
}
// If address2 is using an IPv4 wildcard for the host, then address2 is
// only covered if it's an IPv4 address.
if host2 == "0.0.0.0" {
ip1 := net.ParseIP(host1)
if ip1 != nil && ip1.To4() != nil {
return true
}
return false
}
// If address2 is using an IPv6 wildcard for the host, then address2 is
// always covered.
if host2 == "::" || host2 == "" {
return true
}
return false
}
// IsWildCardAddress returns whether the given address is a wildcard.
func IsWildCardAddress(address string) bool {
address = CanonicalNetworkAddress(address, ports.HTTPSDefaultPort)
host, _, err := net.SplitHostPort(address)
if err != nil {
return false
}
if host == "0.0.0.0" || host == "::" || host == "" {
return true
}
return false
}
|