package manet

import (
	
	

	ma 
)

// Private4 and Private6 are well-known private networks
var Private4, Private6 []*net.IPNet
var privateCIDR4 = []string{
	// localhost
	"127.0.0.0/8",
	// private networks
	"10.0.0.0/8",
	"100.64.0.0/10",
	"172.16.0.0/12",
	"192.168.0.0/16",
	// link local
	"169.254.0.0/16",
}
var privateCIDR6 = []string{
	// localhost
	"::1/128",
	// ULA reserved
	"fc00::/7",
	// link local
	"fe80::/10",
}

// Unroutable4 and Unroutable6 are well known unroutable address ranges
var Unroutable4, Unroutable6 []*net.IPNet
var unroutableCIDR4 = []string{
	"0.0.0.0/8",
	"192.0.0.0/26",
	"192.0.2.0/24",
	"192.88.99.0/24",
	"198.18.0.0/15",
	"198.51.100.0/24",
	"203.0.113.0/24",
	"224.0.0.0/4",
	"240.0.0.0/4",
	"255.255.255.255/32",
}
var unroutableCIDR6 = []string{
	"ff00::/8",      // multicast
	"2001:db8::/32", // documentation
}

var globalUnicast []*net.IPNet
var globalUnicastCIDR6 = []string{
	"2000::/3",
}

var nat64CIDRs = []string{
	"64:ff9b:1::/48", // RFC 8215
	"64:ff9b::/96",   // RFC 6052
}

var nat64 []*net.IPNet

// unResolvableDomains do not resolve to an IP address.
// Ref: https://en.wikipedia.org/wiki/Special-use_domain_name#Reserved_domain_names
var unResolvableDomains = []string{
	// Reverse DNS Lookup
	".in-addr.arpa",
	".ip6.arpa",

	// RFC 6761: Users MAY assume that queries for "invalid" names will always return NXDOMAIN
	// responses
	".invalid",
}

// privateUseDomains are reserved for private use and have no central authority for consistent
// address resolution
// Ref: https://en.wikipedia.org/wiki/Special-use_domain_name#Reserved_domain_names
var privateUseDomains = []string{
	// RFC 8375: Reserved for home networks
	".home.arpa",

	// MDNS
	".local",

	// RFC 6761: No central authority for .test names
	".test",
}

// RFC 6761: Users may assume that IPv4 and IPv6 address queries for localhost names will
// always resolve to the respective IP loopback address
const localHostDomain = ".localhost"

func init() {
	Private4 = parseCIDR(privateCIDR4)
	Private6 = parseCIDR(privateCIDR6)
	Unroutable4 = parseCIDR(unroutableCIDR4)
	Unroutable6 = parseCIDR(unroutableCIDR6)
	globalUnicast = parseCIDR(globalUnicastCIDR6)
	nat64 = parseCIDR(nat64CIDRs)
}

func parseCIDR( []string) []*net.IPNet {
	 := make([]*net.IPNet, len())
	for ,  := range  {
		, ,  := net.ParseCIDR()
		if  != nil {
			panic()
		}
		[] = 
	}
	return 
}

// IsPublicAddr returns true if the IP part of the multiaddr is a publicly routable address
// or if it's a dns address without a special use domain e.g. .local.
func ( ma.Multiaddr) bool {
	 := false
	ma.ForEach(, func( ma.Component) bool {
		switch .Protocol().Code {
		case ma.P_IP6ZONE:
			return true
		case ma.P_IP4:
			 := net.IP(.RawValue())
			 = !inAddrRange(, Private4) && !inAddrRange(, Unroutable4)
		case ma.P_IP6:
			 := net.IP(.RawValue())
			// IP6 documentation prefix(part of Unroutable6) is a subset of the ip6
			// global unicast allocation so we ensure that it's not a documentation
			// prefix by diffing with Unroutable6
			 := inAddrRange(, globalUnicast) && !inAddrRange(, Unroutable6)
			if  {
				 = true
				return false
			}
			// The WellKnown NAT64 prefix(RFC 6052) can only reference a public IPv4
			// address.
			// The Local use NAT64 prefix(RFC 8215) can reference private IPv4
			// addresses. But since the translation from Local use NAT64 prefix to IPv4
			// address is left to the user we have no way of knowing which IPv4 address
			// is referenced. We count these as Public addresses because a false
			// negative for this method here is generally worse than a false positive.
			 = inAddrRange(, nat64)
			return false
		case ma.P_DNS, ma.P_DNS4, ma.P_DNS6, ma.P_DNSADDR:
			 := .Value()
			 = true
			if isSubdomain(, localHostDomain) {
				 = false
				return false
			}
			for ,  := range unResolvableDomains {
				if isSubdomain(, ) {
					 = false
					return false
				}
			}
			for ,  := range privateUseDomains {
				if isSubdomain(, ) {
					 = false
					break
				}
			}
		}
		return false
	})
	return 
}

// isSubdomain checks if child is sub domain of parent. It also returns true if child and parent are
// the same domain.
// Parent must have a "." prefix.
func isSubdomain(,  string) bool {
	return strings.HasSuffix(, ) ||  == [1:]
}

// IsPrivateAddr returns true if the IP part of the mutiaddr is in a private network
func ( ma.Multiaddr) bool {
	 := false
	ma.ForEach(, func( ma.Component) bool {
		switch .Protocol().Code {
		case ma.P_IP6ZONE:
			return true
		case ma.P_IP4:
			 = inAddrRange(net.IP(.RawValue()), Private4)
		case ma.P_IP6:
			 = inAddrRange(net.IP(.RawValue()), Private6)
		case ma.P_DNS, ma.P_DNS4, ma.P_DNS6, ma.P_DNSADDR:
			 := .Value()
			if isSubdomain(, localHostDomain) {
				 = true
			}
			// We don't check for privateUseDomains because private use domains can
			// resolve to public IP addresses
		}
		return false
	})
	return 
}

func inAddrRange( net.IP,  []*net.IPNet) bool {
	for ,  := range  {
		if .Contains() {
			return true
		}
	}

	return false
}