// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Minimal RFC 6724 address selection.

package net

import (
	
	
)

func sortByRFC6724( []IPAddr) {
	if len() < 2 {
		return
	}
	sortByRFC6724withSrcs(, srcAddrs())
}

func sortByRFC6724withSrcs( []IPAddr,  []netip.Addr) {
	if len() != len() {
		panic("internal error")
	}
	 := make([]byRFC6724Info, len())
	for ,  := range  {
		,  := netip.AddrFromSlice(.IP)
		[] = byRFC6724Info{
			addr:     [],
			addrAttr: ipAttrOf(),
			src:      [],
			srcAttr:  ipAttrOf([]),
		}
	}
	slices.SortStableFunc(, compareByRFC6724)
	for  := range  {
		[] = [].addr
	}
}

// srcAddrs tries to UDP-connect to each address to see if it has a
// route. (This doesn't send any packets). The destination port
// number is irrelevant.
func srcAddrs( []IPAddr) []netip.Addr {
	 := make([]netip.Addr, len())
	 := UDPAddr{Port: 53}
	for  := range  {
		.IP = [].IP
		.Zone = [].Zone
		,  := DialUDP("udp", nil, &)
		if  == nil {
			if ,  := .LocalAddr().(*UDPAddr);  {
				[], _ = netip.AddrFromSlice(.IP)
			}
			.Close()
		}
	}
	return 
}

type ipAttr struct {
	Scope      scope
	Precedence uint8
	Label      uint8
}

func ipAttrOf( netip.Addr) ipAttr {
	if !.IsValid() {
		return ipAttr{}
	}
	 := rfc6724policyTable.Classify()
	return ipAttr{
		Scope:      classifyScope(),
		Precedence: .Precedence,
		Label:      .Label,
	}
}

type byRFC6724Info struct {
	addr     IPAddr
	addrAttr ipAttr
	src      netip.Addr
	srcAttr  ipAttr
}

// compareByRFC6724 compares two byRFC6724Info records and returns an integer
// indicating the order. It follows the algorithm and variable names from
// RFC 6724 section 6. Returns -1 if a is preferred, 1 if b is preferred,
// and 0 if they are equal.
func compareByRFC6724(,  byRFC6724Info) int {
	 := .addr.IP
	 := .addr.IP
	 := .src
	 := .src
	 := &.addrAttr
	 := &.addrAttr
	 := &.srcAttr
	 := &.srcAttr

	const  = -1
	const  = 1

	// Rule 1: Avoid unusable destinations.
	// If DB is known to be unreachable or if Source(DB) is undefined, then
	// prefer DA.  Similarly, if DA is known to be unreachable or if
	// Source(DA) is undefined, then prefer DB.
	if !.IsValid() && !.IsValid() {
		return 0 // "equal"
	}
	if !.IsValid() {
		return 
	}
	if !.IsValid() {
		return 
	}

	// Rule 2: Prefer matching scope.
	// If Scope(DA) = Scope(Source(DA)) and Scope(DB) <> Scope(Source(DB)),
	// then prefer DA.  Similarly, if Scope(DA) <> Scope(Source(DA)) and
	// Scope(DB) = Scope(Source(DB)), then prefer DB.
	if .Scope == .Scope && .Scope != .Scope {
		return 
	}
	if .Scope != .Scope && .Scope == .Scope {
		return 
	}

	// Rule 3: Avoid deprecated addresses.
	// If Source(DA) is deprecated and Source(DB) is not, then prefer DB.
	// Similarly, if Source(DA) is not deprecated and Source(DB) is
	// deprecated, then prefer DA.

	// TODO(bradfitz): implement? low priority for now.

	// Rule 4: Prefer home addresses.
	// If Source(DA) is simultaneously a home address and care-of address
	// and Source(DB) is not, then prefer DA.  Similarly, if Source(DB) is
	// simultaneously a home address and care-of address and Source(DA) is
	// not, then prefer DB.

	// TODO(bradfitz): implement? low priority for now.

	// Rule 5: Prefer matching label.
	// If Label(Source(DA)) = Label(DA) and Label(Source(DB)) <> Label(DB),
	// then prefer DA.  Similarly, if Label(Source(DA)) <> Label(DA) and
	// Label(Source(DB)) = Label(DB), then prefer DB.
	if .Label == .Label &&
		.Label != .Label {
		return 
	}
	if .Label != .Label &&
		.Label == .Label {
		return 
	}

	// Rule 6: Prefer higher precedence.
	// If Precedence(DA) > Precedence(DB), then prefer DA.  Similarly, if
	// Precedence(DA) < Precedence(DB), then prefer DB.
	if .Precedence > .Precedence {
		return 
	}
	if .Precedence < .Precedence {
		return 
	}

	// Rule 7: Prefer native transport.
	// If DA is reached via an encapsulating transition mechanism (e.g.,
	// IPv6 in IPv4) and DB is not, then prefer DB.  Similarly, if DB is
	// reached via encapsulation and DA is not, then prefer DA.

	// TODO(bradfitz): implement? low priority for now.

	// Rule 8: Prefer smaller scope.
	// If Scope(DA) < Scope(DB), then prefer DA.  Similarly, if Scope(DA) >
	// Scope(DB), then prefer DB.
	if .Scope < .Scope {
		return 
	}
	if .Scope > .Scope {
		return 
	}

	// Rule 9: Use the longest matching prefix.
	// When DA and DB belong to the same address family (both are IPv6 or
	// both are IPv4 [but see below]): If CommonPrefixLen(Source(DA), DA) >
	// CommonPrefixLen(Source(DB), DB), then prefer DA.  Similarly, if
	// CommonPrefixLen(Source(DA), DA) < CommonPrefixLen(Source(DB), DB),
	// then prefer DB.
	//
	// However, applying this rule to IPv4 addresses causes
	// problems (see issues 13283 and 18518), so limit to IPv6.
	if .To4() == nil && .To4() == nil {
		 := commonPrefixLen(, )
		 := commonPrefixLen(, )

		if  >  {
			return 
		}
		if  <  {
			return 
		}
	}

	// Rule 10: Otherwise, leave the order unchanged.
	// If DA preceded DB in the original list, prefer DA.
	// Otherwise, prefer DB.
	return 0 // "equal"
}

type policyTableEntry struct {
	Prefix     netip.Prefix
	Precedence uint8
	Label      uint8
}

type policyTable []policyTableEntry

// RFC 6724 section 2.1.
// Items are sorted by the size of their Prefix.Mask.Size,
var rfc6724policyTable = policyTable{
	{
		// "::1/128"
		Prefix:     netip.PrefixFrom(netip.AddrFrom16([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}), 128),
		Precedence: 50,
		Label:      0,
	},
	{
		// "::ffff:0:0/96"
		// IPv4-compatible, etc.
		Prefix:     netip.PrefixFrom(netip.AddrFrom16([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}), 96),
		Precedence: 35,
		Label:      4,
	},
	{
		// "::/96"
		Prefix:     netip.PrefixFrom(netip.AddrFrom16([16]byte{}), 96),
		Precedence: 1,
		Label:      3,
	},
	{
		// "2001::/32"
		// Teredo
		Prefix:     netip.PrefixFrom(netip.AddrFrom16([16]byte{0x20, 0x01}), 32),
		Precedence: 5,
		Label:      5,
	},
	{
		// "2002::/16"
		// 6to4
		Prefix:     netip.PrefixFrom(netip.AddrFrom16([16]byte{0x20, 0x02}), 16),
		Precedence: 30,
		Label:      2,
	},
	{
		// "3ffe::/16"
		Prefix:     netip.PrefixFrom(netip.AddrFrom16([16]byte{0x3f, 0xfe}), 16),
		Precedence: 1,
		Label:      12,
	},
	{
		// "fec0::/10"
		Prefix:     netip.PrefixFrom(netip.AddrFrom16([16]byte{0xfe, 0xc0}), 10),
		Precedence: 1,
		Label:      11,
	},
	{
		// "fc00::/7"
		Prefix:     netip.PrefixFrom(netip.AddrFrom16([16]byte{0xfc}), 7),
		Precedence: 3,
		Label:      13,
	},
	{
		// "::/0"
		Prefix:     netip.PrefixFrom(netip.AddrFrom16([16]byte{}), 0),
		Precedence: 40,
		Label:      1,
	},
}

// Classify returns the policyTableEntry of the entry with the longest
// matching prefix that contains ip.
// The table t must be sorted from largest mask size to smallest.
func ( policyTable) ( netip.Addr) policyTableEntry {
	// Prefix.Contains() will not match an IPv6 prefix for an IPv4 address.
	if .Is4() {
		 = netip.AddrFrom16(.As16())
	}
	for ,  := range  {
		if .Prefix.Contains() {
			return 
		}
	}
	return policyTableEntry{}
}

// RFC 6724 section 3.1.
type scope uint8

const (
	scopeInterfaceLocal scope = 0x1
	scopeLinkLocal      scope = 0x2
	scopeAdminLocal     scope = 0x4
	scopeSiteLocal      scope = 0x5
	scopeOrgLocal       scope = 0x8
	scopeGlobal         scope = 0xe
)

func classifyScope( netip.Addr) scope {
	if .IsLoopback() || .IsLinkLocalUnicast() {
		return scopeLinkLocal
	}
	 := .Is6() && !.Is4In6()
	 := .As16()
	if  && .IsMulticast() {
		return scope([1] & 0xf)
	}
	// Site-local addresses are defined in RFC 3513 section 2.5.6
	// (and deprecated in RFC 3879).
	if  && [0] == 0xfe && [1]&0xc0 == 0xc0 {
		return scopeSiteLocal
	}
	return scopeGlobal
}

// commonPrefixLen reports the length of the longest prefix (looking
// at the most significant, or leftmost, bits) that the
// two addresses have in common, up to the length of a's prefix (i.e.,
// the portion of the address not including the interface ID).
//
// If a or b is an IPv4 address as an IPv6 address, the IPv4 addresses
// are compared (with max common prefix length of 32).
// If a and b are different IP versions, 0 is returned.
//
// See https://tools.ietf.org/html/rfc6724#section-2.2
func commonPrefixLen( netip.Addr,  IP) ( int) {
	if  := .To4();  != nil {
		 = 
	}
	 := .AsSlice()
	if len() != len() {
		return 0
	}
	// If IPv6, only up to the prefix (first 64 bits)
	if len() > 8 {
		 = [:8]
		 = [:8]
	}
	for len() > 0 {
		if [0] == [0] {
			 += 8
			 = [1:]
			 = [1:]
			continue
		}
		 := 8
		,  := [0], [0]
		for {
			 >>= 1
			 >>= 1
			--
			if  ==  {
				 += 
				return
			}
		}
	}
	return
}