github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/network/address.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package network
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/binary"
     9  	"fmt"
    10  	"net"
    11  	"sort"
    12  	"strings"
    13  
    14  	"github.com/juju/errors"
    15  	"github.com/juju/utils/set"
    16  )
    17  
    18  // Private network ranges for IPv4 and IPv6.
    19  // See: http://tools.ietf.org/html/rfc1918
    20  // Also: http://tools.ietf.org/html/rfc4193
    21  var (
    22  	classAPrivate   = mustParseCIDR("10.0.0.0/8")
    23  	classBPrivate   = mustParseCIDR("172.16.0.0/12")
    24  	classCPrivate   = mustParseCIDR("192.168.0.0/16")
    25  	ipv6UniqueLocal = mustParseCIDR("fc00::/7")
    26  )
    27  
    28  const (
    29  	// LoopbackIPv4CIDR is the loopback CIDR range for IPv4.
    30  	LoopbackIPv4CIDR = "127.0.0.0/8"
    31  
    32  	// LoopbackIPv6CIDR is the loopback CIDR range for IPv6.
    33  	LoopbackIPv6CIDR = "::1/128"
    34  )
    35  
    36  func mustParseCIDR(s string) *net.IPNet {
    37  	_, net, err := net.ParseCIDR(s)
    38  	if err != nil {
    39  		panic(err)
    40  	}
    41  	return net
    42  }
    43  
    44  // AddressType represents the possible ways of specifying a machine location by
    45  // either a hostname resolvable by dns lookup, or IPv4 or IPv6 address.
    46  type AddressType string
    47  
    48  const (
    49  	HostName    AddressType = "hostname"
    50  	IPv4Address AddressType = "ipv4"
    51  	IPv6Address AddressType = "ipv6"
    52  )
    53  
    54  // Scope denotes the context a location may apply to. If a name or
    55  // address can be reached from the wider internet, it is considered
    56  // public. A private network address is either specific to the cloud
    57  // or cloud subnet a machine belongs to, or to the machine itself for
    58  // containers.
    59  type Scope string
    60  
    61  // SpaceName holds the Juju space name of an address.
    62  type SpaceName string
    63  type spaceNameList []SpaceName
    64  
    65  func (s spaceNameList) String() string {
    66  	namesString := make([]string, len(s))
    67  	for i, v := range s {
    68  		namesString[i] = string(v)
    69  	}
    70  
    71  	return strings.Join(namesString, ", ")
    72  }
    73  
    74  func (s spaceNameList) IndexOf(name SpaceName) int {
    75  	for i := range s {
    76  		if s[i] == name {
    77  			return i
    78  		}
    79  	}
    80  	return -1
    81  }
    82  
    83  const (
    84  	ScopeUnknown      Scope = ""
    85  	ScopePublic       Scope = "public"
    86  	ScopeCloudLocal   Scope = "local-cloud"
    87  	ScopeMachineLocal Scope = "local-machine"
    88  	ScopeLinkLocal    Scope = "link-local"
    89  )
    90  
    91  // Address represents the location of a machine, including metadata
    92  // about what kind of location the address describes.
    93  type Address struct {
    94  	Value string
    95  	Type  AddressType
    96  	Scope
    97  	SpaceName
    98  	SpaceProviderId Id
    99  }
   100  
   101  // String returns a string representation of the address, in the form:
   102  // `<scope>:<address-value>@<space-name>(id:<space-provider-id)`; for example:
   103  //
   104  //	public:c2-54-226-162-124.compute-1.amazonaws.com@public-api(id:42)
   105  //
   106  // If the scope is ScopeUnknown, the initial "<scope>:" prefix will be omitted.
   107  // If the SpaceName is blank, the "@<space-name>" suffix will be omitted.
   108  // Finally, if the SpaceProviderId is empty the suffix
   109  // "(id:<space-provider-id>)" part will be omitted as well.
   110  func (a Address) String() string {
   111  	var buf bytes.Buffer
   112  	if a.Scope != ScopeUnknown {
   113  		buf.WriteString(string(a.Scope))
   114  		buf.WriteByte(':')
   115  	}
   116  	buf.WriteString(a.Value)
   117  
   118  	var spaceFound bool
   119  	if a.SpaceName != "" {
   120  		spaceFound = true
   121  		buf.WriteByte('@')
   122  		buf.WriteString(string(a.SpaceName))
   123  	}
   124  	if a.SpaceProviderId != Id("") {
   125  		if !spaceFound {
   126  			buf.WriteByte('@')
   127  		}
   128  		buf.WriteString(fmt.Sprintf("(id:%v)", string(a.SpaceProviderId)))
   129  	}
   130  	return buf.String()
   131  }
   132  
   133  // GoString implements fmt.GoStringer.
   134  func (a Address) GoString() string {
   135  	return a.String()
   136  }
   137  
   138  // NewAddress creates a new Address, deriving its type from the value
   139  // and using ScopeUnknown as scope. It's a shortcut to calling
   140  // NewScopedAddress(value, ScopeUnknown).
   141  func NewAddress(value string) Address {
   142  	return NewScopedAddress(value, ScopeUnknown)
   143  }
   144  
   145  // NewScopedAddress creates a new Address, deriving its type from the
   146  // value.
   147  //
   148  // If the specified scope is ScopeUnknown, then NewScopedAddress will
   149  // attempt derive the scope based on reserved IP address ranges.
   150  // Because passing ScopeUnknown is fairly common, NewAddress() above
   151  // does exactly that.
   152  func NewScopedAddress(value string, scope Scope) Address {
   153  	addr := Address{
   154  		Value: value,
   155  		Type:  DeriveAddressType(value),
   156  		Scope: scope,
   157  	}
   158  	if scope == ScopeUnknown {
   159  		addr.Scope = deriveScope(addr)
   160  	}
   161  	return addr
   162  }
   163  
   164  // NewAddressOnSpace creates a new Address, deriving its type and scope from the
   165  // value and associating it with the given spaceName.
   166  func NewAddressOnSpace(spaceName string, value string) Address {
   167  	addr := NewAddress(value)
   168  	addr.SpaceName = SpaceName(spaceName)
   169  	return addr
   170  }
   171  
   172  // NewAddresses is a convenience function to create addresses from a a variable
   173  // number of string arguments.
   174  func NewAddresses(inAddresses ...string) (outAddresses []Address) {
   175  	outAddresses = make([]Address, len(inAddresses))
   176  	for i, address := range inAddresses {
   177  		outAddresses[i] = NewAddress(address)
   178  	}
   179  	return outAddresses
   180  }
   181  
   182  // NewAddressesOnSpace is a convenience function to create addresses on the same
   183  // space, from a a variable number of string arguments.
   184  func NewAddressesOnSpace(spaceName string, inAddresses ...string) (outAddresses []Address) {
   185  	outAddresses = make([]Address, len(inAddresses))
   186  	for i, address := range inAddresses {
   187  		outAddresses[i] = NewAddressOnSpace(spaceName, address)
   188  	}
   189  	return outAddresses
   190  }
   191  
   192  // DeriveAddressType attempts to detect the type of address given.
   193  func DeriveAddressType(value string) AddressType {
   194  	ip := net.ParseIP(value)
   195  	switch {
   196  	case ip == nil:
   197  		// TODO(gz): Check value is a valid hostname
   198  		return HostName
   199  	case ip.To4() != nil:
   200  		return IPv4Address
   201  	case ip.To16() != nil:
   202  		return IPv6Address
   203  	default:
   204  		panic("Unknown form of IP address")
   205  	}
   206  }
   207  
   208  func isIPv4PrivateNetworkAddress(addrType AddressType, ip net.IP) bool {
   209  	if addrType != IPv4Address {
   210  		return false
   211  	}
   212  	return classAPrivate.Contains(ip) ||
   213  		classBPrivate.Contains(ip) ||
   214  		classCPrivate.Contains(ip)
   215  }
   216  
   217  func isIPv6UniqueLocalAddress(addrType AddressType, ip net.IP) bool {
   218  	if addrType != IPv6Address {
   219  		return false
   220  	}
   221  	return ipv6UniqueLocal.Contains(ip)
   222  }
   223  
   224  // deriveScope attempts to derive the network scope from an address's
   225  // type and value, returning the original network scope if no
   226  // deduction can be made.
   227  func deriveScope(addr Address) Scope {
   228  	if addr.Type == HostName {
   229  		return addr.Scope
   230  	}
   231  	ip := net.ParseIP(addr.Value)
   232  	if ip == nil {
   233  		return addr.Scope
   234  	}
   235  	if ip.IsLoopback() {
   236  		return ScopeMachineLocal
   237  	}
   238  	if isIPv4PrivateNetworkAddress(addr.Type, ip) ||
   239  		isIPv6UniqueLocalAddress(addr.Type, ip) {
   240  		return ScopeCloudLocal
   241  	}
   242  	if ip.IsLinkLocalMulticast() ||
   243  		ip.IsLinkLocalUnicast() ||
   244  		ip.IsInterfaceLocalMulticast() {
   245  		return ScopeLinkLocal
   246  	}
   247  	if ip.IsGlobalUnicast() {
   248  		return ScopePublic
   249  	}
   250  	return addr.Scope
   251  }
   252  
   253  // ExactScopeMatch checks if an address exactly matches any of the specified
   254  // scopes. An address will not match if globalPreferIPv6 is set and it isn't an
   255  // IPv6 address.
   256  func ExactScopeMatch(addr Address, addrScopes ...Scope) bool {
   257  	if PreferIPv6() && addr.Type != IPv6Address {
   258  		return false
   259  	}
   260  	for _, scope := range addrScopes {
   261  		if addr.Scope == scope {
   262  			return true
   263  		}
   264  	}
   265  	return false
   266  }
   267  
   268  // SelectAddressBySpaces picks the first address from the given slice that has
   269  // the given space name associated.
   270  func SelectAddressBySpaces(addresses []Address, spaceNames ...SpaceName) (Address, bool) {
   271  	for _, addr := range addresses {
   272  		if spaceNameList(spaceNames).IndexOf(addr.SpaceName) >= 0 {
   273  			logger.Debugf("selected %q as first address in space %q", addr.Value, addr.SpaceName)
   274  			return addr, true
   275  		}
   276  	}
   277  
   278  	if len(spaceNames) == 0 {
   279  		logger.Warningf("no spaces to select addresses from")
   280  	} else {
   281  		logger.Warningf("no addresses found in spaces %s", spaceNames)
   282  	}
   283  	return Address{}, false
   284  }
   285  
   286  // SelectHostsPortBySpaces picks the first HostPort from the given slice that has
   287  // the given space name associated.
   288  func SelectHostsPortBySpaces(hps []HostPort, spaceNames ...SpaceName) ([]HostPort, bool) {
   289  	if len(spaceNames) == 0 {
   290  		logger.Warningf("host ports not filtered - no spaces given.")
   291  		return hps, false
   292  	}
   293  
   294  	var selectedHostPorts []HostPort
   295  	for _, hp := range hps {
   296  		if spaceNameList(spaceNames).IndexOf(hp.SpaceName) >= 0 {
   297  			logger.Debugf("selected %q as a hostPort in space %q", hp.Value, hp.SpaceName)
   298  			selectedHostPorts = append(selectedHostPorts, hp)
   299  		}
   300  	}
   301  
   302  	if len(selectedHostPorts) > 0 {
   303  		return selectedHostPorts, true
   304  	}
   305  
   306  	logger.Warningf("no hostPorts found in spaces %s", spaceNames)
   307  	return hps, false
   308  }
   309  
   310  // SelectControllerAddress returns the most suitable address to use as a Juju
   311  // Controller (API/state server) endpoint given the list of addresses.
   312  // The second return value is false when no address can be returned.
   313  // When machineLocal is true both ScopeCloudLocal and ScopeMachineLocal
   314  // addresses are considered during the selection, otherwise just ScopeCloudLocal are.
   315  func SelectControllerAddress(addresses []Address, machineLocal bool) (Address, bool) {
   316  	internalAddress, ok := SelectInternalAddress(addresses, machineLocal)
   317  	logger.Debugf(
   318  		"selected %q as controller address, using scope %q",
   319  		internalAddress.Value, internalAddress.Scope,
   320  	)
   321  	return internalAddress, ok
   322  }
   323  
   324  // SelectMongoHostPorts returns the most suitable HostPort (as string) to
   325  // use as a Juju Controller (API/state server) endpoint given the list of
   326  // hostPorts. It first tries to find the first HostPort bound to the
   327  // spaces provided, then, if that fails, uses the older selection method based on scope.
   328  // When machineLocal is true and an address can't be selected by space both
   329  // ScopeCloudLocal and ScopeMachineLocal addresses are considered during the
   330  // selection, otherwise just ScopeCloudLocal are.
   331  func SelectMongoHostPortsBySpaces(hostPorts []HostPort, spaces []SpaceName) ([]string, bool) {
   332  	filteredHostPorts, ok := SelectHostsPortBySpaces(hostPorts, spaces...)
   333  	if ok {
   334  		logger.Debugf(
   335  			"selected %q as controller host:port, using spaces %q",
   336  			filteredHostPorts, spaces,
   337  		)
   338  	}
   339  	return HostPortsToStrings(filteredHostPorts), ok
   340  }
   341  
   342  func SelectMongoHostPortsByScope(hostPorts []HostPort, machineLocal bool) []string {
   343  	// Fallback to using the legacy and error-prone approach using scope
   344  	// selection instead.
   345  	internalHP := SelectInternalHostPort(hostPorts, machineLocal)
   346  	logger.Debugf(
   347  		"selected %q as controller host:port, using scope selection",
   348  		internalHP,
   349  	)
   350  	return []string{internalHP}
   351  }
   352  
   353  // SelectPublicAddress picks one address from a slice that would be
   354  // appropriate to display as a publicly accessible endpoint. If there
   355  // are no suitable addresses, then ok is false (and an empty address is
   356  // returned). If a suitable address is then ok is true.
   357  func SelectPublicAddress(addresses []Address) (Address, bool) {
   358  	index := bestAddressIndex(len(addresses), PreferIPv6(), func(i int) Address {
   359  		return addresses[i]
   360  	}, publicMatch)
   361  	if index < 0 {
   362  		return Address{}, false
   363  	}
   364  	return addresses[index], true
   365  }
   366  
   367  // SelectPublicHostPort picks one HostPort from a slice that would be
   368  // appropriate to display as a publicly accessible endpoint. If there
   369  // are no suitable candidates, the empty string is returned.
   370  func SelectPublicHostPort(hps []HostPort) string {
   371  	index := bestAddressIndex(len(hps), PreferIPv6(), func(i int) Address {
   372  		return hps[i].Address
   373  	}, publicMatch)
   374  	if index < 0 {
   375  		return ""
   376  	}
   377  	return hps[index].NetAddr()
   378  }
   379  
   380  // SelectInternalAddress picks one address from a slice that can be
   381  // used as an endpoint for juju internal communication. If there are
   382  // are no suitable addresses, then ok is false (and an empty address is
   383  // returned). If a suitable address was found then ok is true.
   384  func SelectInternalAddress(addresses []Address, machineLocal bool) (Address, bool) {
   385  	index := bestAddressIndex(len(addresses), PreferIPv6(), func(i int) Address {
   386  		return addresses[i]
   387  	}, internalAddressMatcher(machineLocal))
   388  	if index < 0 {
   389  		return Address{}, false
   390  	}
   391  	return addresses[index], true
   392  }
   393  
   394  // SelectInternalHostPort picks one HostPort from a slice that can be
   395  // used as an endpoint for juju internal communication and returns it
   396  // in its NetAddr form. If there are no suitable addresses, the empty
   397  // string is returned.
   398  func SelectInternalHostPort(hps []HostPort, machineLocal bool) string {
   399  	index := bestAddressIndex(len(hps), PreferIPv6(), func(i int) Address {
   400  		return hps[i].Address
   401  	}, internalAddressMatcher(machineLocal))
   402  	if index < 0 {
   403  		return ""
   404  	}
   405  	return hps[index].NetAddr()
   406  }
   407  
   408  // SelectInternalHostPorts picks the best matching HostPorts from a
   409  // slice that can be used as an endpoint for juju internal
   410  // communication and returns them in NetAddr form. If there are no
   411  // suitable addresses, an empty slice is returned.
   412  func SelectInternalHostPorts(hps []HostPort, machineLocal bool) []string {
   413  	indexes := bestAddressIndexes(len(hps), PreferIPv6(), func(i int) Address {
   414  		return hps[i].Address
   415  	}, internalAddressMatcher(machineLocal))
   416  
   417  	out := make([]string, 0, len(indexes))
   418  	for _, index := range indexes {
   419  		out = append(out, hps[index].NetAddr())
   420  	}
   421  	return out
   422  }
   423  
   424  // PrioritizeInternalHostPorts orders the provided addresses by best
   425  // match for use as an endpoint for juju internal communication and
   426  // returns them in NetAddr form. If there are no suitable addresses
   427  // then an empty slice is returned.
   428  func PrioritizeInternalHostPorts(hps []HostPort, machineLocal bool) []string {
   429  	indexes := prioritizedAddressIndexes(len(hps), PreferIPv6(), func(i int) Address {
   430  		return hps[i].Address
   431  	}, internalAddressMatcher(machineLocal))
   432  
   433  	out := make([]string, 0, len(indexes))
   434  	for _, index := range indexes {
   435  		out = append(out, hps[index].NetAddr())
   436  	}
   437  	return out
   438  }
   439  
   440  func publicMatch(addr Address, preferIPv6 bool) scopeMatch {
   441  	switch addr.Scope {
   442  	case ScopePublic:
   443  		return mayPreferIPv6(addr, exactScope, preferIPv6)
   444  	case ScopeCloudLocal, ScopeUnknown:
   445  		return mayPreferIPv6(addr, fallbackScope, preferIPv6)
   446  	}
   447  	return invalidScope
   448  }
   449  
   450  // mayPreferIPv6 returns mismatchedTypeExactScope or
   451  // mismatchedTypeFallbackScope (depending on originalScope) if addr's
   452  // type is IPv4, and preferIPv6 is true. When preferIPv6 is false, or
   453  // addr's type is IPv6 and preferIPv6 is true, returns the
   454  // originalScope unchanged.
   455  func mayPreferIPv6(addr Address, originalScope scopeMatch, preferIPv6 bool) scopeMatch {
   456  	if preferIPv6 && addr.Type != IPv6Address {
   457  		switch originalScope {
   458  		case exactScope:
   459  			return mismatchedTypeExactScope
   460  		case fallbackScope:
   461  			return mismatchedTypeFallbackScope
   462  		}
   463  		return invalidScope
   464  	}
   465  	return originalScope
   466  }
   467  
   468  func internalAddressMatcher(machineLocal bool) func(Address, bool) scopeMatch {
   469  	if machineLocal {
   470  		return cloudOrMachineLocalMatch
   471  	}
   472  	return cloudLocalMatch
   473  }
   474  
   475  func cloudLocalMatch(addr Address, preferIPv6 bool) scopeMatch {
   476  	switch addr.Scope {
   477  	case ScopeCloudLocal:
   478  		return mayPreferIPv6(addr, exactScope, preferIPv6)
   479  	case ScopePublic, ScopeUnknown:
   480  		return mayPreferIPv6(addr, fallbackScope, preferIPv6)
   481  	}
   482  	return invalidScope
   483  }
   484  
   485  func cloudOrMachineLocalMatch(addr Address, preferIPv6 bool) scopeMatch {
   486  	if addr.Scope == ScopeMachineLocal {
   487  		return mayPreferIPv6(addr, exactScope, preferIPv6)
   488  	}
   489  	return cloudLocalMatch(addr, preferIPv6)
   490  }
   491  
   492  type scopeMatch int
   493  
   494  const (
   495  	invalidScope scopeMatch = iota
   496  	exactScope
   497  	fallbackScope
   498  	mismatchedTypeExactScope
   499  	mismatchedTypeFallbackScope
   500  )
   501  
   502  // bestAddressIndexes returns the indexes of the addresses with the
   503  // best matching scope (according to the match func). An empty slice
   504  // is returned if there were no suitable addresses.
   505  func bestAddressIndex(numAddr int, preferIPv6 bool, getAddr func(i int) Address, match func(addr Address, preferIPv6 bool) scopeMatch) int {
   506  	indexes := bestAddressIndexes(numAddr, preferIPv6, getAddr, match)
   507  	if len(indexes) > 0 {
   508  		return indexes[0]
   509  	}
   510  	return -1
   511  }
   512  
   513  // bestAddressIndexes returns the indexes of the addresses with the
   514  // best matching scope and type (according to the match func). An
   515  // empty slice is returned if there were no suitable addresses.
   516  func bestAddressIndexes(numAddr int, preferIPv6 bool, getAddr func(i int) Address, match func(addr Address, preferIPv6 bool) scopeMatch) []int {
   517  	// Categorise addresses by scope and type matching quality.
   518  	matches := filterAndCollateAddressIndexes(numAddr, preferIPv6, getAddr, match)
   519  
   520  	// Retrieve the indexes of the addresses with the best scope and type match.
   521  	allowedMatchTypes := []scopeMatch{exactScope, fallbackScope}
   522  	if preferIPv6 {
   523  		allowedMatchTypes = append(allowedMatchTypes, mismatchedTypeExactScope, mismatchedTypeFallbackScope)
   524  	}
   525  	for _, matchType := range allowedMatchTypes {
   526  		indexes, ok := matches[matchType]
   527  		if ok && len(indexes) > 0 {
   528  			return indexes
   529  		}
   530  	}
   531  	return []int{}
   532  }
   533  
   534  func prioritizedAddressIndexes(numAddr int, preferIPv6 bool, getAddr func(i int) Address, match func(addr Address, preferIPv6 bool) scopeMatch) []int {
   535  	// Categorise addresses by scope and type matching quality.
   536  	matches := filterAndCollateAddressIndexes(numAddr, preferIPv6, getAddr, match)
   537  
   538  	// Retrieve the indexes of the addresses with the best scope and type match.
   539  	allowedMatchTypes := []scopeMatch{exactScope, fallbackScope}
   540  	if preferIPv6 {
   541  		allowedMatchTypes = append(allowedMatchTypes, mismatchedTypeExactScope, mismatchedTypeFallbackScope)
   542  	}
   543  	var prioritized []int
   544  	for _, matchType := range allowedMatchTypes {
   545  		indexes, ok := matches[matchType]
   546  		if ok && len(indexes) > 0 {
   547  			prioritized = append(prioritized, indexes...)
   548  		}
   549  	}
   550  	return prioritized
   551  }
   552  
   553  func filterAndCollateAddressIndexes(numAddr int, preferIPv6 bool, getAddr func(i int) Address, match func(addr Address, preferIPv6 bool) scopeMatch) map[scopeMatch][]int {
   554  	// Categorise addresses by scope and type matching quality.
   555  	matches := make(map[scopeMatch][]int)
   556  	for i := 0; i < numAddr; i++ {
   557  		matchType := match(getAddr(i), preferIPv6)
   558  		switch matchType {
   559  		case exactScope, fallbackScope, mismatchedTypeExactScope, mismatchedTypeFallbackScope:
   560  			matches[matchType] = append(matches[matchType], i)
   561  		}
   562  	}
   563  	return matches
   564  }
   565  
   566  // sortOrder calculates the "weight" of the address when sorting,
   567  // taking into account the preferIPv6 flag:
   568  // - public IPs first;
   569  // - hostnames after that, but "localhost" will be last if present;
   570  // - cloud-local next;
   571  // - machine-local next;
   572  // - link-local next;
   573  // - non-hostnames with unknown scope last.
   574  //
   575  // When preferIPv6 flag and the address type do not match, the order
   576  // is incremented to put non-preferred addresses after preferred.
   577  func (a Address) sortOrder(preferIPv6 bool) int {
   578  	order := 0xFF
   579  	switch a.Scope {
   580  	case ScopePublic:
   581  		order = 0x00
   582  	case ScopeCloudLocal:
   583  		order = 0x20
   584  	case ScopeMachineLocal:
   585  		order = 0x40
   586  	case ScopeLinkLocal:
   587  		order = 0x80
   588  	}
   589  	switch a.Type {
   590  	case HostName:
   591  		order = 0x10
   592  		if a.Value == "localhost" {
   593  			order++
   594  		}
   595  	case IPv4Address:
   596  		if preferIPv6 {
   597  			order++
   598  		}
   599  	case IPv6Address:
   600  		if !preferIPv6 {
   601  			order++
   602  		}
   603  	}
   604  	return order
   605  }
   606  
   607  type addressesPreferringIPv4Slice []Address
   608  
   609  func (a addressesPreferringIPv4Slice) Len() int      { return len(a) }
   610  func (a addressesPreferringIPv4Slice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
   611  func (a addressesPreferringIPv4Slice) Less(i, j int) bool {
   612  	addr1 := a[i]
   613  	addr2 := a[j]
   614  	order1 := addr1.sortOrder(false)
   615  	order2 := addr2.sortOrder(false)
   616  	if order1 == order2 {
   617  		return addr1.Value < addr2.Value
   618  	}
   619  	return order1 < order2
   620  }
   621  
   622  type addressesPreferringIPv6Slice struct {
   623  	addressesPreferringIPv4Slice
   624  }
   625  
   626  func (a addressesPreferringIPv6Slice) Less(i, j int) bool {
   627  	addr1 := a.addressesPreferringIPv4Slice[i]
   628  	addr2 := a.addressesPreferringIPv4Slice[j]
   629  	order1 := addr1.sortOrder(true)
   630  	order2 := addr2.sortOrder(true)
   631  	if order1 == order2 {
   632  		return addr1.Value < addr2.Value
   633  	}
   634  	return order1 < order2
   635  }
   636  
   637  // SortAddresses sorts the given Address slice according to the
   638  // sortOrder of each address and the preferIpv6 flag. See
   639  // Address.sortOrder() for more info.
   640  func SortAddresses(addrs []Address, preferIPv6 bool) {
   641  	if preferIPv6 {
   642  		sort.Sort(addressesPreferringIPv6Slice{addressesPreferringIPv4Slice(addrs)})
   643  	} else {
   644  		sort.Sort(addressesPreferringIPv4Slice(addrs))
   645  	}
   646  }
   647  
   648  // DecimalToIPv4 converts a decimal to the dotted quad IP address format.
   649  func DecimalToIPv4(addr uint32) net.IP {
   650  	bytes := make([]byte, 4)
   651  	binary.BigEndian.PutUint32(bytes, addr)
   652  	return net.IP(bytes)
   653  }
   654  
   655  // IPv4ToDecimal converts a dotted quad IP address to its decimal equivalent.
   656  func IPv4ToDecimal(ipv4Addr net.IP) (uint32, error) {
   657  	ip := ipv4Addr.To4()
   658  	if ip == nil {
   659  		return 0, errors.Errorf("%q is not a valid IPv4 address", ipv4Addr.String())
   660  	}
   661  	return binary.BigEndian.Uint32([]byte(ip)), nil
   662  }
   663  
   664  // ResolvableHostnames returns the set of all DNS resolvable names
   665  // from addrs. Note that 'localhost' is always considered resolvable
   666  // because it can be used both as an IPv4 or IPv6 endpoint (e.g., in
   667  // IPv6-only networks).
   668  func ResolvableHostnames(addrs []Address) []Address {
   669  	resolveableAddrs := make([]Address, 0, len(addrs))
   670  	for _, addr := range addrs {
   671  		if addr.Value == "localhost" || net.ParseIP(addr.Value) != nil {
   672  			resolveableAddrs = append(resolveableAddrs, addr)
   673  			continue
   674  		}
   675  		_, err := netLookupIP(addr.Value)
   676  		if err != nil {
   677  			logger.Infof("removing unresolvable address %q: %v", addr.Value, err)
   678  			continue
   679  		}
   680  		resolveableAddrs = append(resolveableAddrs, addr)
   681  	}
   682  	return resolveableAddrs
   683  }
   684  
   685  // MergedAddresses provides a single list of addresses without duplicates
   686  // suitable for returning as an address list for a machine.
   687  // TODO (cherylj) Add explicit unit tests - tracked with bug #1544158
   688  func MergedAddresses(machineAddresses, providerAddresses []Address) []Address {
   689  	merged := make([]Address, 0, len(providerAddresses)+len(machineAddresses))
   690  	providerValues := set.NewStrings()
   691  	for _, address := range providerAddresses {
   692  		// Older versions of Juju may have stored an empty address so ignore it here.
   693  		if address.Value == "" || providerValues.Contains(address.Value) {
   694  			continue
   695  		}
   696  		providerValues.Add(address.Value)
   697  		merged = append(merged, address)
   698  	}
   699  	for _, address := range machineAddresses {
   700  		if !providerValues.Contains(address.Value) {
   701  			merged = append(merged, address)
   702  		}
   703  	}
   704  	return merged
   705  }