decred.org/dcrdex@v1.0.5/dex/ip.go (about)

     1  // This code is available on the terms of the project LICENSE.md file,
     2  // also available online at https://blueoakcouncil.org/license/1.0.0.
     3  
     4  package dex
     5  
     6  import (
     7  	"net"
     8  	"strings"
     9  )
    10  
    11  // IPKey is a IP address byte array.
    12  type IPKey [net.IPv6len]byte
    13  
    14  // NewIPKey parses an IP address string into an IPKey. For an IPV6 address, this
    15  // drops the interface identifier, which is the second half of the address. The
    16  // first half of the address comprises 48-bits for the prefix, which is the
    17  // public topology of a network that is assigned by an ISP, and 16-bits for the
    18  // Site-Level Aggregation Identifier (SLA). However, any number of the SLA bits
    19  // may be assigned by a provider, potentially giving a single link the freedom
    20  // to use multiple subnets, thus expanding the set of addresses they may use
    21  // freely. As such, IPv6 addresses with just the interface bits masked may not
    22  // be sufficient to identify a single remote uplink.
    23  func NewIPKey(addr string) IPKey {
    24  	host, _, err := net.SplitHostPort(addr)
    25  	if err == nil && host != "" {
    26  		addr = host
    27  	} else {
    28  		// If SplitHostPort failed, IPv6 addresses may still have brackets.
    29  		addr = strings.Trim(addr, "[]")
    30  	}
    31  
    32  	ip := net.ParseIP(addr)
    33  	if ip == nil {
    34  		return IPKey{} // i.e. net.IPv6unspecified
    35  	}
    36  	// IPv4 is encoded in a net.IP of length IPv6len as
    37  	// 00:00:00:00:00:00:00:00:00:00:ff:ff:xx:xx:xx:xx
    38  	// Thus, must copy all 16 bytes for IPv4.
    39  	N := net.IPv6len
    40  	if ip.To4() == nil && !ip.Equal(net.IPv6loopback) {
    41  		// Drop the last 64 bits (interface) of non-loopback IPv6 addresses.
    42  		N = net.IPv6len / 2 // i.e. ip = ip.Mask(net.CIDRMask(64, 128))
    43  	}
    44  	var ipKey IPKey
    45  	copy(ipKey[:], ip[:N])
    46  	return ipKey
    47  }
    48  
    49  // String returns a readable IP address representation of the IPKey. This is
    50  // done by copying the bytes of the IPKey array, and invoking the
    51  // net.(IP).String method. As such it is inefficient and should not be invoked
    52  // repeatedly or in hot paths.
    53  func (ipk IPKey) String() string {
    54  	return net.IP(ipk[:]).String()
    55  }
    56  
    57  // PrefixV6 returns the first 48-bits of an IPv6 address, or nil for an IPv4
    58  // address. This may be used to identify multiple remote hosts from the same
    59  // public topology but different subnets. A heuristic may be defined to treat
    60  // hosts with the same prefix as the same host if there are many such hosts.
    61  func (ipk IPKey) PrefixV6() *IPKey {
    62  	if net.IP(ipk[:]).To4() != nil {
    63  		return nil
    64  	}
    65  	var prefix IPKey
    66  	copy(prefix[:], ipk[:6]) // 48 bit prefix
    67  	return &prefix
    68  }
    69  
    70  // IsLoopback reports whether the IPKey represents a loopback address.
    71  func (ipk IPKey) IsLoopback() bool {
    72  	return net.IP(ipk[:]).IsLoopback()
    73  }
    74  
    75  // IsUnspecified reports whether the IPKey is zero (unspecified).
    76  func (ipk IPKey) IsUnspecified() bool {
    77  	return ipk == IPKey{} // net.IP(ipk[:]).Equal(net.IPv6unspecified)
    78  }