netaddress.go raw

   1  package btcwire
   2  
   3  import (
   4  	"encoding/binary"
   5  	"fmt"
   6  	"strconv"
   7  	"strings"
   8  )
   9  
  10  // NetAddress is a Bitcoin network address as carried by the version message:
  11  // the services offered by the peer, its IP address, and its port.
  12  //
  13  // The IP field holds 16 bytes. IPv4 addresses are stored in their
  14  // IPv4-mapped IPv6 form (::ffff:a.b.c.d), which is how they appear on the
  15  // wire. The port is big endian on the wire, unlike every other integer in the
  16  // protocol.
  17  type NetAddress struct {
  18  	// Services is the set of service bits advertised by the peer.
  19  	Services uint64
  20  
  21  	// IP is the 16-byte IP address.
  22  	IP [16]byte
  23  
  24  	// Port is the TCP port.
  25  	Port uint16
  26  }
  27  
  28  // v4MappedPrefix is the 12-byte prefix of an IPv4-mapped IPv6 address.
  29  var v4MappedPrefix = [12]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}
  30  
  31  // NewNetAddress parses host, which must be an IPv4 dotted quad or an IPv6
  32  // literal as defined by RFC 4291 (optionally enclosed in brackets), and
  33  // returns an address with the given port. IPv6 zone identifiers are not
  34  // supported, and DNS names are not resolved: this package performs no name
  35  // service work and does not import the net package.
  36  func NewNetAddress(host string, port uint16) (*NetAddress, error) {
  37  	ip, err := parseIP(host)
  38  	if err != nil {
  39  		return nil, err
  40  	}
  41  	return &NetAddress{IP: ip, Port: port}, nil
  42  }
  43  
  44  // IsZero reports whether the address holds no endpoint, that is, whether both
  45  // its IP and its port are zero.
  46  func (na *NetAddress) IsZero() bool {
  47  	if na.Port != 0 {
  48  		return false
  49  	}
  50  	return na.IP == [16]byte{}
  51  }
  52  
  53  // IPString returns the textual form of the address's IP: dotted quad for
  54  // IPv4 and IPv4-mapped addresses, and RFC 5952 IPv6 notation otherwise.
  55  func (na *NetAddress) IPString() string {
  56  	return formatIP(na.IP)
  57  }
  58  
  59  // String returns the address as host:port, with the host bracketed when it is
  60  // an IPv6 address.
  61  func (na *NetAddress) String() string {
  62  	host := formatIP(na.IP)
  63  	port := strconv.FormatUint(uint64(na.Port), 10)
  64  	if strings.IndexByte(host, ':') >= 0 {
  65  		return "[" + host + "]:" + port
  66  	}
  67  	return host + ":" + port
  68  }
  69  
  70  // encode appends the wire encoding of the address: services, IP, and big
  71  // endian port.
  72  func (na *NetAddress) encode(e *Encoder) {
  73  	e.Uint64(na.Services)
  74  	e.Write(na.IP[:])
  75  	e.Uint16BE(na.Port)
  76  }
  77  
  78  // decode replaces the address with the one decoded from d.
  79  func (na *NetAddress) decode(d *Decoder) error {
  80  	services, err := d.Uint64()
  81  	if err != nil {
  82  		return fmt.Errorf("btcwire: decoding address services: %w", err)
  83  	}
  84  	ip, err := d.Bytes(len(na.IP))
  85  	if err != nil {
  86  		return fmt.Errorf("btcwire: decoding address IP: %w", err)
  87  	}
  88  	port, err := d.Uint16BE()
  89  	if err != nil {
  90  		return fmt.Errorf("btcwire: decoding address port: %w", err)
  91  	}
  92  	na.Services = services
  93  	copy(na.IP[:], ip)
  94  	na.Port = port
  95  	return nil
  96  }
  97  
  98  // parseIP parses host, which may be an IPv4 or IPv6 literal and may be
  99  // bracketed, into its 16-byte wire representation.
 100  func parseIP(host string) ([16]byte, error) {
 101  	var ip [16]byte
 102  	if len(host) >= 2 && host[0] == '[' && host[len(host)-1] == ']' {
 103  		host = host[1 : len(host)-1]
 104  	}
 105  	if host == "" {
 106  		return ip, fmt.Errorf("%w: empty host", ErrInvalidAddress)
 107  	}
 108  	if strings.IndexByte(host, ':') >= 0 {
 109  		parsed, ok := parseIPv6(host)
 110  		if !ok {
 111  			return ip, fmt.Errorf("%w: %q is not an IPv6 address", ErrInvalidAddress, host)
 112  		}
 113  		return parsed, nil
 114  	}
 115  	v4, ok := parseIPv4(host)
 116  	if !ok {
 117  		return ip, fmt.Errorf("%w: %q is not an IPv4 address", ErrInvalidAddress, host)
 118  	}
 119  	copy(ip[:len(v4MappedPrefix)], v4MappedPrefix[:])
 120  	copy(ip[12:16], v4[:])
 121  	return ip, nil
 122  }
 123  
 124  // parseIPv4 parses a dotted quad. Leading zeros are rejected because they make
 125  // the interpretation ambiguous.
 126  func parseIPv4(s string) ([4]byte, bool) {
 127  	var out [4]byte
 128  	parts := strings.Split(s, ".")
 129  	if len(parts) != 4 {
 130  		return out, false
 131  	}
 132  	for i, part := range parts {
 133  		if len(part) == 0 || len(part) > 3 {
 134  			return out, false
 135  		}
 136  		if len(part) > 1 && part[0] == '0' {
 137  			return out, false
 138  		}
 139  		n := 0
 140  		for j := 0; j < len(part); j++ {
 141  			c := part[j]
 142  			if c < '0' || c > '9' {
 143  				return out, false
 144  			}
 145  			n = n*10 + int(c-'0')
 146  		}
 147  		if n > 255 {
 148  			return out, false
 149  		}
 150  		out[i] = byte(n)
 151  	}
 152  	return out, true
 153  }
 154  
 155  // parseIPv6 parses an IPv6 literal, including forms with an embedded IPv4
 156  // dotted quad and at most one "::" compression.
 157  func parseIPv6(s string) ([16]byte, bool) {
 158  	var ip [16]byte
 159  	if idx := strings.Index(s, "::"); idx >= 0 {
 160  		rest := s[idx+2:]
 161  		if strings.Contains(rest, "::") {
 162  			return ip, false
 163  		}
 164  		head, ok := parseIPv6Groups(s[:idx], false)
 165  		if !ok {
 166  			return ip, false
 167  		}
 168  		tail, ok := parseIPv6Groups(rest, true)
 169  		if !ok {
 170  			return ip, false
 171  		}
 172  		if len(head)+len(tail) > 7 {
 173  			return ip, false
 174  		}
 175  		for i, g := range head {
 176  			binary.BigEndian.PutUint16(ip[2*i:], g)
 177  		}
 178  		base := 8 - len(tail)
 179  		for i, g := range tail {
 180  			binary.BigEndian.PutUint16(ip[2*(base+i):], g)
 181  		}
 182  		return ip, true
 183  	}
 184  	groups, ok := parseIPv6Groups(s, true)
 185  	if !ok || len(groups) != 8 {
 186  		return ip, false
 187  	}
 188  	for i, g := range groups {
 189  		binary.BigEndian.PutUint16(ip[2*i:], g)
 190  	}
 191  	return ip, true
 192  }
 193  
 194  // parseIPv6Groups parses the colon-separated 16-bit groups of s. When
 195  // allowIPv4 is true the final group may be a dotted quad, which contributes
 196  // two 16-bit groups.
 197  func parseIPv6Groups(s string, allowIPv4 bool) ([]uint16, bool) {
 198  	if s == "" {
 199  		return nil, true
 200  	}
 201  	parts := strings.Split(s, ":")
 202  	groups := make([]uint16, 0, len(parts))
 203  	for i, part := range parts {
 204  		if part == "" {
 205  			return nil, false
 206  		}
 207  		if allowIPv4 && i == len(parts)-1 && strings.IndexByte(part, '.') >= 0 {
 208  			v4, ok := parseIPv4(part)
 209  			if !ok {
 210  				return nil, false
 211  			}
 212  			groups = append(groups,
 213  				binary.BigEndian.Uint16(v4[0:2]),
 214  				binary.BigEndian.Uint16(v4[2:4]),
 215  			)
 216  			continue
 217  		}
 218  		if len(part) > 4 {
 219  			return nil, false
 220  		}
 221  		var group uint16
 222  		for j := 0; j < len(part); j++ {
 223  			c := part[j]
 224  			var digit uint16
 225  			switch {
 226  			case c >= '0' && c <= '9':
 227  				digit = uint16(c - '0')
 228  			case c >= 'a' && c <= 'f':
 229  				digit = uint16(c-'a') + 10
 230  			case c >= 'A' && c <= 'F':
 231  				digit = uint16(c-'A') + 10
 232  			default:
 233  				return nil, false
 234  			}
 235  			group = group<<4 | digit
 236  		}
 237  		groups = append(groups, group)
 238  	}
 239  	return groups, true
 240  }
 241  
 242  // formatIP renders a 16-byte IP in its shortest standard textual form.
 243  func formatIP(ip [16]byte) string {
 244  	if v4, ok := ipv4FromMapped(ip); ok {
 245  		return strconv.Itoa(int(v4[0])) + "." + strconv.Itoa(int(v4[1])) + "." +
 246  			strconv.Itoa(int(v4[2])) + "." + strconv.Itoa(int(v4[3]))
 247  	}
 248  	var groups [8]uint16
 249  	for i := range groups {
 250  		groups[i] = binary.BigEndian.Uint16(ip[2*i:])
 251  	}
 252  	// Find the longest run of zero groups; the leftmost run wins ties. Only
 253  	// runs of two or more groups are compressed.
 254  	bestStart, bestLen := -1, 0
 255  	for i := 0; i < len(groups); {
 256  		if groups[i] != 0 {
 257  			i++
 258  			continue
 259  		}
 260  		j := i
 261  		for j < len(groups) && groups[j] == 0 {
 262  			j++
 263  		}
 264  		if j-i > bestLen {
 265  			bestStart, bestLen = i, j-i
 266  		}
 267  		i = j
 268  	}
 269  	if bestLen < 2 {
 270  		bestStart, bestLen = -1, 0
 271  	}
 272  	var b strings.Builder
 273  	wrote := false
 274  	justCompressed := false
 275  	for i := 0; i < len(groups); i++ {
 276  		if i == bestStart {
 277  			b.WriteString("::")
 278  			wrote, justCompressed = true, true
 279  			i += bestLen - 1
 280  			continue
 281  		}
 282  		if wrote && !justCompressed {
 283  			b.WriteByte(':')
 284  		}
 285  		b.WriteString(strconv.FormatUint(uint64(groups[i]), 16))
 286  		wrote, justCompressed = true, false
 287  	}
 288  	return b.String()
 289  }
 290  
 291  // ipv4FromMapped returns the dotted-quad bytes of an IPv4-mapped IPv6
 292  // address.
 293  func ipv4FromMapped(ip [16]byte) ([4]byte, bool) {
 294  	var v4 [4]byte
 295  	for i := 0; i < len(v4MappedPrefix); i++ {
 296  		if ip[i] != v4MappedPrefix[i] {
 297  			return v4, false
 298  		}
 299  	}
 300  	copy(v4[:], ip[12:16])
 301  	return v4, true
 302  }
 303