384 lines
11 KiB
Go
384 lines
11 KiB
Go
|
// Copyright (c) 2013-2016 The btcsuite developers
|
||
|
// Use of this source code is governed by an ISC
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
package common
|
||
|
|
||
|
import (
|
||
|
"encoding/binary"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"math"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
// MaxVarIntPayload is the maximum payload size for a variable length integer.
|
||
|
MaxVarIntPayload = 9
|
||
|
|
||
|
// binaryFreeListMaxItems is the number of buffers to keep in the free
|
||
|
// list to use for binary serialization and deserialization.
|
||
|
binaryFreeListMaxItems = 1024
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// littleEndian is a convenience variable since binary.LittleEndian is
|
||
|
// quite long.
|
||
|
littleEndian = binary.LittleEndian
|
||
|
)
|
||
|
|
||
|
// binaryFreeList defines a concurrent safe free list of byte slices (up to the
|
||
|
// maximum number defined by the binaryFreeListMaxItems constant) that have a
|
||
|
// cap of 8 (thus it supports up to a uint64). It is used to provide temporary
|
||
|
// buffers for serializing and deserializing primitive numbers to and from their
|
||
|
// binary encoding in order to greatly reduce the number of allocations
|
||
|
// required.
|
||
|
//
|
||
|
// For convenience, functions are provided for each of the primitive unsigned
|
||
|
// integers that automatically obtain a buffer from the free list, perform the
|
||
|
// necessary binary conversion, read from or write to the given io.Reader or
|
||
|
// io.Writer, and return the buffer to the free list.
|
||
|
type binaryFreeList chan []byte
|
||
|
|
||
|
// Borrow returns a byte slice from the free list with a length of 8. A new
|
||
|
// buffer is allocated if there are not any available on the free list.
|
||
|
func (l binaryFreeList) Borrow() []byte {
|
||
|
var buf []byte
|
||
|
select {
|
||
|
case buf = <-l:
|
||
|
default:
|
||
|
buf = make([]byte, 8)
|
||
|
}
|
||
|
return buf[:8]
|
||
|
}
|
||
|
|
||
|
// Return puts the provided byte slice back on the free list. The buffer MUST
|
||
|
// have been obtained via the Borrow function and therefore have a cap of 8.
|
||
|
func (l binaryFreeList) Return(buf []byte) {
|
||
|
select {
|
||
|
case l <- buf:
|
||
|
default:
|
||
|
// Let it go to the garbage collector.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Uint8 reads a single byte from the provided reader using a buffer from the
|
||
|
// free list and returns it as a uint8.
|
||
|
func (l binaryFreeList) Uint8(r io.Reader) (uint8, error) {
|
||
|
buf := l.Borrow()[:1]
|
||
|
if _, err := io.ReadFull(r, buf); err != nil {
|
||
|
l.Return(buf)
|
||
|
return 0, err
|
||
|
}
|
||
|
rv := buf[0]
|
||
|
l.Return(buf)
|
||
|
return rv, nil
|
||
|
}
|
||
|
|
||
|
// Uint16 reads two bytes from the provided reader using a buffer from the
|
||
|
// free list, converts it to a number using the provided byte order, and returns
|
||
|
// the resulting uint16.
|
||
|
func (l binaryFreeList) Uint16(r io.Reader, byteOrder binary.ByteOrder) (uint16, error) {
|
||
|
buf := l.Borrow()[:2]
|
||
|
if _, err := io.ReadFull(r, buf); err != nil {
|
||
|
l.Return(buf)
|
||
|
return 0, err
|
||
|
}
|
||
|
rv := byteOrder.Uint16(buf)
|
||
|
l.Return(buf)
|
||
|
return rv, nil
|
||
|
}
|
||
|
|
||
|
// Uint32 reads four bytes from the provided reader using a buffer from the
|
||
|
// free list, converts it to a number using the provided byte order, and returns
|
||
|
// the resulting uint32.
|
||
|
func (l binaryFreeList) Uint32(r io.Reader, byteOrder binary.ByteOrder) (uint32, error) {
|
||
|
buf := l.Borrow()[:4]
|
||
|
if _, err := io.ReadFull(r, buf); err != nil {
|
||
|
l.Return(buf)
|
||
|
return 0, err
|
||
|
}
|
||
|
rv := byteOrder.Uint32(buf)
|
||
|
l.Return(buf)
|
||
|
return rv, nil
|
||
|
}
|
||
|
|
||
|
// Uint64 reads eight bytes from the provided reader using a buffer from the
|
||
|
// free list, converts it to a number using the provided byte order, and returns
|
||
|
// the resulting uint64.
|
||
|
func (l binaryFreeList) Uint64(r io.Reader, byteOrder binary.ByteOrder) (uint64, error) {
|
||
|
buf := l.Borrow()[:8]
|
||
|
if _, err := io.ReadFull(r, buf); err != nil {
|
||
|
l.Return(buf)
|
||
|
return 0, err
|
||
|
}
|
||
|
rv := byteOrder.Uint64(buf)
|
||
|
l.Return(buf)
|
||
|
return rv, nil
|
||
|
}
|
||
|
|
||
|
// PutUint8 copies the provided uint8 into a buffer from the free list and
|
||
|
// writes the resulting byte to the given writer.
|
||
|
func (l binaryFreeList) PutUint8(w io.Writer, val uint8) error {
|
||
|
buf := l.Borrow()[:1]
|
||
|
buf[0] = val
|
||
|
_, err := w.Write(buf)
|
||
|
l.Return(buf)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// PutUint16 serializes the provided uint16 using the given byte order into a
|
||
|
// buffer from the free list and writes the resulting two bytes to the given
|
||
|
// writer.
|
||
|
func (l binaryFreeList) PutUint16(w io.Writer, byteOrder binary.ByteOrder, val uint16) error {
|
||
|
buf := l.Borrow()[:2]
|
||
|
byteOrder.PutUint16(buf, val)
|
||
|
_, err := w.Write(buf)
|
||
|
l.Return(buf)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// PutUint32 serializes the provided uint32 using the given byte order into a
|
||
|
// buffer from the free list and writes the resulting four bytes to the given
|
||
|
// writer.
|
||
|
func (l binaryFreeList) PutUint32(w io.Writer, byteOrder binary.ByteOrder, val uint32) error {
|
||
|
buf := l.Borrow()[:4]
|
||
|
byteOrder.PutUint32(buf, val)
|
||
|
_, err := w.Write(buf)
|
||
|
l.Return(buf)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// PutUint64 serializes the provided uint64 using the given byte order into a
|
||
|
// buffer from the free list and writes the resulting eight bytes to the given
|
||
|
// writer.
|
||
|
func (l binaryFreeList) PutUint64(w io.Writer, byteOrder binary.ByteOrder, val uint64) error {
|
||
|
buf := l.Borrow()[:8]
|
||
|
byteOrder.PutUint64(buf, val)
|
||
|
_, err := w.Write(buf)
|
||
|
l.Return(buf)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// binarySerializer provides a free list of buffers to use for serializing and
|
||
|
// deserializing primitive integer values to and from io.Readers and io.Writers.
|
||
|
var binarySerializer binaryFreeList = make(chan []byte, binaryFreeListMaxItems)
|
||
|
|
||
|
// errNonCanonicalVarInt is the common format string used for non-canonically
|
||
|
// encoded variable length integer errors.
|
||
|
var errNonCanonicalVarInt = "non-canonical varint %x - discriminant %x must " +
|
||
|
"encode a value greater than %x"
|
||
|
|
||
|
// ReadVarInt reads a variable length integer from r and returns it as a uint64.
|
||
|
func ReadVarInt(r io.Reader, pver uint32) (uint64, error) {
|
||
|
discriminant, err := binarySerializer.Uint8(r)
|
||
|
if err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
|
||
|
var rv uint64
|
||
|
switch discriminant {
|
||
|
case 0xff:
|
||
|
sv, err := binarySerializer.Uint64(r, littleEndian)
|
||
|
if err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
rv = sv
|
||
|
|
||
|
// The encoding is not canonical if the value could have been
|
||
|
// encoded using fewer bytes.
|
||
|
min := uint64(0x100000000)
|
||
|
if rv < min {
|
||
|
return 0, messageError("ReadVarInt", fmt.Sprintf(
|
||
|
errNonCanonicalVarInt, rv, discriminant, min))
|
||
|
}
|
||
|
|
||
|
case 0xfe:
|
||
|
sv, err := binarySerializer.Uint32(r, littleEndian)
|
||
|
if err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
rv = uint64(sv)
|
||
|
|
||
|
// The encoding is not canonical if the value could have been
|
||
|
// encoded using fewer bytes.
|
||
|
min := uint64(0x10000)
|
||
|
if rv < min {
|
||
|
return 0, messageError("ReadVarInt", fmt.Sprintf(
|
||
|
errNonCanonicalVarInt, rv, discriminant, min))
|
||
|
}
|
||
|
|
||
|
case 0xfd:
|
||
|
sv, err := binarySerializer.Uint16(r, littleEndian)
|
||
|
if err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
rv = uint64(sv)
|
||
|
|
||
|
// The encoding is not canonical if the value could have been
|
||
|
// encoded using fewer bytes.
|
||
|
min := uint64(0xfd)
|
||
|
if rv < min {
|
||
|
return 0, messageError("ReadVarInt", fmt.Sprintf(
|
||
|
errNonCanonicalVarInt, rv, discriminant, min))
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
rv = uint64(discriminant)
|
||
|
}
|
||
|
|
||
|
return rv, nil
|
||
|
}
|
||
|
|
||
|
// WriteVarInt serializes val to w using a variable number of bytes depending
|
||
|
// on its value.
|
||
|
func WriteVarInt(w io.Writer, pver uint32, val uint64) error {
|
||
|
if val < 0xfd {
|
||
|
return binarySerializer.PutUint8(w, uint8(val))
|
||
|
}
|
||
|
|
||
|
if val <= math.MaxUint16 {
|
||
|
err := binarySerializer.PutUint8(w, 0xfd)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return binarySerializer.PutUint16(w, littleEndian, uint16(val))
|
||
|
}
|
||
|
|
||
|
if val <= math.MaxUint32 {
|
||
|
err := binarySerializer.PutUint8(w, 0xfe)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return binarySerializer.PutUint32(w, littleEndian, uint32(val))
|
||
|
}
|
||
|
|
||
|
err := binarySerializer.PutUint8(w, 0xff)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return binarySerializer.PutUint64(w, littleEndian, val)
|
||
|
}
|
||
|
|
||
|
// VarIntSerializeSize returns the number of bytes it would take to serialize
|
||
|
// val as a variable length integer.
|
||
|
func VarIntSerializeSize(val uint64) int {
|
||
|
// The value is small enough to be represented by itself, so it's
|
||
|
// just 1 byte.
|
||
|
if val < 0xfd {
|
||
|
return 1
|
||
|
}
|
||
|
|
||
|
// Discriminant 1 byte plus 2 bytes for the uint16.
|
||
|
if val <= math.MaxUint16 {
|
||
|
return 3
|
||
|
}
|
||
|
|
||
|
// Discriminant 1 byte plus 4 bytes for the uint32.
|
||
|
if val <= math.MaxUint32 {
|
||
|
return 5
|
||
|
}
|
||
|
|
||
|
// Discriminant 1 byte plus 8 bytes for the uint64.
|
||
|
return 9
|
||
|
}
|
||
|
|
||
|
// ReadVarString reads a variable length string from r and returns it as a Go
|
||
|
// string. A variable length string is encoded as a variable length integer
|
||
|
// containing the length of the string followed by the bytes that represent the
|
||
|
// string itself.
|
||
|
func ReadVarString(r io.Reader, pver uint32) (string, error) {
|
||
|
count, err := ReadVarInt(r, pver)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
buf := make([]byte, count)
|
||
|
_, err = io.ReadFull(r, buf)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
return string(buf), nil
|
||
|
}
|
||
|
|
||
|
// WriteVarString serializes str to w as a variable length integer containing
|
||
|
// the length of the string followed by the bytes that represent the string
|
||
|
// itself.
|
||
|
func WriteVarString(w io.Writer, pver uint32, str string) error {
|
||
|
err := WriteVarInt(w, pver, uint64(len(str)))
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
_, err = w.Write([]byte(str))
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// ReadVarBytes reads a variable length byte array. A byte array is encoded
|
||
|
// as a varInt containing the length of the array followed by the bytes
|
||
|
// themselves. An error is returned if the length is greater than the
|
||
|
// passed maxAllowed parameter which helps protect against memory exhaustion
|
||
|
// attacks and forced panics through malformed messages. The fieldName
|
||
|
// parameter is only used for the error message so it provides more context in
|
||
|
// the error.
|
||
|
func ReadVarBytes(r io.Reader, pver uint32, maxAllowed uint32,
|
||
|
fieldName string) ([]byte, error) {
|
||
|
|
||
|
count, err := ReadVarInt(r, pver)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
// Prevent byte array larger than the max message size. It would
|
||
|
// be possible to cause memory exhaustion and panics without a sane
|
||
|
// upper bound on this count.
|
||
|
if count > uint64(maxAllowed) {
|
||
|
str := fmt.Sprintf("%s is larger than the max allowed size "+
|
||
|
"[count %d, max %d]", fieldName, count, maxAllowed)
|
||
|
return nil, messageError("ReadVarBytes", str)
|
||
|
}
|
||
|
|
||
|
b := make([]byte, count)
|
||
|
_, err = io.ReadFull(r, b)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return b, nil
|
||
|
}
|
||
|
|
||
|
// WriteVarBytes serializes a variable length byte array to w as a varInt
|
||
|
// containing the number of bytes, followed by the bytes themselves.
|
||
|
func WriteVarBytes(w io.Writer, pver uint32, bytes []byte) error {
|
||
|
slen := uint64(len(bytes))
|
||
|
err := WriteVarInt(w, pver, slen)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
_, err = w.Write(bytes)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// MessageError describes an issue with a message.
|
||
|
// An example of some potential issues are messages from the wrong bitcoin
|
||
|
// network, invalid commands, mismatched checksums, and exceeding max payloads.
|
||
|
//
|
||
|
// This provides a mechanism for the caller to type assert the error to
|
||
|
// differentiate between general io errors such as io.EOF and issues that
|
||
|
// resulted from malformed messages.
|
||
|
type MessageError struct {
|
||
|
Func string // Function name
|
||
|
Description string // Human readable description of the issue
|
||
|
}
|
||
|
|
||
|
// Error satisfies the error interface and prints human-readable errors.
|
||
|
func (e *MessageError) Error() string {
|
||
|
if e.Func != "" {
|
||
|
return fmt.Sprintf("%v: %v", e.Func, e.Description)
|
||
|
}
|
||
|
return e.Description
|
||
|
}
|
||
|
|
||
|
// messageError creates an error for the given function and description.
|
||
|
func messageError(f string, desc string) *MessageError {
|
||
|
return &MessageError{Func: f, Description: desc}
|
||
|
}
|