github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/wire/message.go (about)

     1  // Copyright (c) 2013-2016 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package wire
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"io"
    11  	"unicode/utf8"
    12  
    13  	"github.com/mit-dci/lit/btcutil/chaincfg/chainhash"
    14  )
    15  
    16  // MessageHeaderSize is the number of bytes in a bitcoin message header.
    17  // Bitcoin network (magic) 4 bytes + command 12 bytes + payload length 4 bytes +
    18  // checksum 4 bytes.
    19  const MessageHeaderSize = 24
    20  
    21  // CommandSize is the fixed size of all commands in the common bitcoin message
    22  // header.  Shorter commands must be zero padded.
    23  const CommandSize = 12
    24  
    25  // MaxMessagePayload is the maximum bytes a message can be regardless of other
    26  // individual limits imposed by messages themselves.
    27  const MaxMessagePayload = (1024 * 1024 * 32) // 32MB
    28  
    29  // Commands used in bitcoin message headers which describe the type of message.
    30  const (
    31  	CmdVersion     = "version"
    32  	CmdVerAck      = "verack"
    33  	CmdGetAddr     = "getaddr"
    34  	CmdAddr        = "addr"
    35  	CmdGetBlocks   = "getblocks"
    36  	CmdInv         = "inv"
    37  	CmdGetData     = "getdata"
    38  	CmdNotFound    = "notfound"
    39  	CmdBlock       = "block"
    40  	CmdTx          = "tx"
    41  	CmdGetHeaders  = "getheaders"
    42  	CmdHeaders     = "headers"
    43  	CmdPing        = "ping"
    44  	CmdPong        = "pong"
    45  	CmdAlert       = "alert"
    46  	CmdMemPool     = "mempool"
    47  	CmdFilterAdd   = "filteradd"
    48  	CmdFilterClear = "filterclear"
    49  	CmdFilterLoad  = "filterload"
    50  	CmdMerkleBlock = "merkleblock"
    51  	CmdReject      = "reject"
    52  	CmdSendHeaders = "sendheaders"
    53  )
    54  
    55  // WireEncoding represents the wire message encoding format to be used.
    56  type MessageEncoding uint32
    57  
    58  const (
    59  	// BaseEncoding encodes all messages in the default format specified
    60  	// for the Bitcoin wire protocol.
    61  	BaseEncoding MessageEncoding = 1 << iota
    62  
    63  	// WitnessEncoding encodes all messages other than transaction messages
    64  	// using the default Bitcoin wire protocol specification. For transaction
    65  	// messages, the new encoding format detailed in BIP0144 will be used.
    66  	WitnessEncoding
    67  )
    68  
    69  // LatestEncoding is the most recently specified encoding for the Bitcoin wire
    70  // protocol.
    71  var LatestEncoding = WitnessEncoding
    72  
    73  // Message is an interface that describes a bitcoin message.  A type that
    74  // implements Message has complete control over the representation of its data
    75  // and may therefore contain additional or fewer fields than those which
    76  // are used directly in the protocol encoded message.
    77  type Message interface {
    78  	BtcDecode(io.Reader, uint32, MessageEncoding) error
    79  	BtcEncode(io.Writer, uint32, MessageEncoding) error
    80  	Command() string
    81  	MaxPayloadLength(uint32) uint32
    82  }
    83  
    84  // makeEmptyMessage creates a message of the appropriate concrete type based
    85  // on the command.
    86  func makeEmptyMessage(command string) (Message, error) {
    87  	var msg Message
    88  	switch command {
    89  	case CmdVersion:
    90  		msg = &MsgVersion{}
    91  
    92  	case CmdVerAck:
    93  		msg = &MsgVerAck{}
    94  
    95  	case CmdGetAddr:
    96  		msg = &MsgGetAddr{}
    97  
    98  	case CmdAddr:
    99  		msg = &MsgAddr{}
   100  
   101  	case CmdGetBlocks:
   102  		msg = &MsgGetBlocks{}
   103  
   104  	case CmdBlock:
   105  		msg = &MsgBlock{}
   106  
   107  	case CmdInv:
   108  		msg = &MsgInv{}
   109  
   110  	case CmdGetData:
   111  		msg = &MsgGetData{}
   112  
   113  	case CmdNotFound:
   114  		msg = &MsgNotFound{}
   115  
   116  	case CmdTx:
   117  		msg = &MsgTx{}
   118  
   119  	case CmdPing:
   120  		msg = &MsgPing{}
   121  
   122  	case CmdPong:
   123  		msg = &MsgPong{}
   124  
   125  	case CmdGetHeaders:
   126  		msg = &MsgGetHeaders{}
   127  
   128  	case CmdHeaders:
   129  		msg = &MsgHeaders{}
   130  
   131  	case CmdAlert:
   132  		msg = &MsgAlert{}
   133  
   134  	case CmdMemPool:
   135  		msg = &MsgMemPool{}
   136  
   137  	case CmdFilterAdd:
   138  		msg = &MsgFilterAdd{}
   139  
   140  	case CmdFilterClear:
   141  		msg = &MsgFilterClear{}
   142  
   143  	case CmdFilterLoad:
   144  		msg = &MsgFilterLoad{}
   145  
   146  	case CmdMerkleBlock:
   147  		msg = &MsgMerkleBlock{}
   148  
   149  	case CmdReject:
   150  		msg = &MsgReject{}
   151  
   152  	case CmdSendHeaders:
   153  		msg = &MsgSendHeaders{}
   154  
   155  	default:
   156  		return nil, fmt.Errorf("unhandled command [%s]", command)
   157  	}
   158  	return msg, nil
   159  }
   160  
   161  // messageHeader defines the header structure for all bitcoin protocol messages.
   162  type messageHeader struct {
   163  	magic    BitcoinNet // 4 bytes
   164  	command  string     // 12 bytes
   165  	length   uint32     // 4 bytes
   166  	checksum [4]byte    // 4 bytes
   167  }
   168  
   169  // readMessageHeader reads a bitcoin message header from r.
   170  func readMessageHeader(r io.Reader) (int, *messageHeader, error) {
   171  	// Since readElements doesn't return the amount of bytes read, attempt
   172  	// to read the entire header into a buffer first in case there is a
   173  	// short read so the proper amount of read bytes are known.  This works
   174  	// since the header is a fixed size.
   175  	var headerBytes [MessageHeaderSize]byte
   176  	n, err := io.ReadFull(r, headerBytes[:])
   177  	if err != nil {
   178  		return n, nil, err
   179  	}
   180  	hr := bytes.NewReader(headerBytes[:])
   181  
   182  	// Create and populate a messageHeader struct from the raw header bytes.
   183  	hdr := messageHeader{}
   184  	var command [CommandSize]byte
   185  	readElements(hr, &hdr.magic, &command, &hdr.length, &hdr.checksum)
   186  
   187  	// Strip trailing zeros from command string.
   188  	hdr.command = string(bytes.TrimRight(command[:], string(0)))
   189  
   190  	return n, &hdr, nil
   191  }
   192  
   193  // discardInput reads n bytes from reader r in chunks and discards the read
   194  // bytes.  This is used to skip payloads when various errors occur and helps
   195  // prevent rogue nodes from causing massive memory allocation through forging
   196  // header length.
   197  func discardInput(r io.Reader, n uint32) {
   198  	maxSize := uint32(10 * 1024) // 10k at a time
   199  	numReads := n / maxSize
   200  	bytesRemaining := n % maxSize
   201  	if n > 0 {
   202  		buf := make([]byte, maxSize)
   203  		for i := uint32(0); i < numReads; i++ {
   204  			io.ReadFull(r, buf)
   205  		}
   206  	}
   207  	if bytesRemaining > 0 {
   208  		buf := make([]byte, bytesRemaining)
   209  		io.ReadFull(r, buf)
   210  	}
   211  }
   212  
   213  // WriteMessageN writes a bitcoin Message to w including the necessary header
   214  // information and returns the number of bytes written.    This function is the
   215  // same as WriteMessage except it also returns the number of bytes written.
   216  func WriteMessageN(w io.Writer, msg Message, pver uint32, btcnet BitcoinNet) (int, error) {
   217  	return WriteMessageWithEncodingN(w, msg, pver, btcnet, BaseEncoding)
   218  }
   219  
   220  // WriteMessage writes a bitcoin Message to w including the necessary header
   221  // information.  This function is the same as WriteMessageN except it doesn't
   222  // doesn't return the number of bytes written.  This function is mainly provided
   223  // for backwards compatibility with the original API, but it's also useful for
   224  // callers that don't care about byte counts.
   225  func WriteMessage(w io.Writer, msg Message, pver uint32, btcnet BitcoinNet) error {
   226  	_, err := WriteMessageN(w, msg, pver, btcnet)
   227  	return err
   228  }
   229  
   230  // WriteMessageWithEncodingN writes a bitcoin Message to w including the
   231  // necessary header information and returns the number of bytes written.
   232  // This function is the same as WriteMessageN except it also allows the caller
   233  // to specify the message encoding format to be used when serializing wire
   234  // messages.
   235  func WriteMessageWithEncodingN(w io.Writer, msg Message, pver uint32,
   236  	btcnet BitcoinNet, encoding MessageEncoding) (int, error) {
   237  
   238  	totalBytes := 0
   239  
   240  	// Enforce max command size.
   241  	var command [CommandSize]byte
   242  	cmd := msg.Command()
   243  	if len(cmd) > CommandSize {
   244  		str := fmt.Sprintf("command [%s] is too long [max %v]",
   245  			cmd, CommandSize)
   246  		return totalBytes, messageError("WriteMessage", str)
   247  	}
   248  	copy(command[:], []byte(cmd))
   249  
   250  	// Encode the message payload.
   251  	var bw bytes.Buffer
   252  	err := msg.BtcEncode(&bw, pver, encoding)
   253  	if err != nil {
   254  		return totalBytes, err
   255  	}
   256  	payload := bw.Bytes()
   257  	lenp := len(payload)
   258  
   259  	// Enforce maximum overall message payload.
   260  	if lenp > MaxMessagePayload {
   261  		str := fmt.Sprintf("message payload is too large - encoded "+
   262  			"%d bytes, but maximum message payload is %d bytes",
   263  			lenp, MaxMessagePayload)
   264  		return totalBytes, messageError("WriteMessage", str)
   265  	}
   266  
   267  	// Enforce maximum message payload based on the message type.
   268  	mpl := msg.MaxPayloadLength(pver)
   269  	if uint32(lenp) > mpl {
   270  		str := fmt.Sprintf("message payload is too large - encoded "+
   271  			"%d bytes, but maximum message payload size for "+
   272  			"messages of type [%s] is %d.", lenp, cmd, mpl)
   273  		return totalBytes, messageError("WriteMessage", str)
   274  	}
   275  
   276  	// Create header for the message.
   277  	hdr := messageHeader{}
   278  	hdr.magic = btcnet
   279  	hdr.command = cmd
   280  	hdr.length = uint32(lenp)
   281  	copy(hdr.checksum[:], chainhash.DoubleHashB(payload)[0:4])
   282  
   283  	// Encode the header for the message.  This is done to a buffer
   284  	// rather than directly to the writer since writeElements doesn't
   285  	// return the number of bytes written.
   286  	hw := bytes.NewBuffer(make([]byte, 0, MessageHeaderSize))
   287  	writeElements(hw, hdr.magic, command, hdr.length, hdr.checksum)
   288  
   289  	// Write header.
   290  	n, err := w.Write(hw.Bytes())
   291  	totalBytes += n
   292  	if err != nil {
   293  		return totalBytes, err
   294  	}
   295  
   296  	// Write payload.
   297  	n, err = w.Write(payload)
   298  	totalBytes += n
   299  	if err != nil {
   300  		return totalBytes, err
   301  	}
   302  
   303  	return totalBytes, nil
   304  }
   305  
   306  // ReadMessageWithEncodingN reads, validates, and parses the next bitcoin Message
   307  // from r for the provided protocol version and bitcoin network.  It returns the
   308  // number of bytes read in addition to the parsed Message and raw bytes which
   309  // comprise the message.  This function is the same as ReadMessageN except it
   310  // allows the caller to specify which message encoding is to to consult when
   311  // decoding wire messages.
   312  func ReadMessageWithEncodingN(r io.Reader, pver uint32, btcnet BitcoinNet,
   313  	enc MessageEncoding) (int, Message, []byte, error) {
   314  
   315  	totalBytes := 0
   316  	n, hdr, err := readMessageHeader(r)
   317  	totalBytes += n
   318  	if err != nil {
   319  		return totalBytes, nil, nil, err
   320  	}
   321  
   322  	// Enforce maximum message payload.
   323  	if hdr.length > MaxMessagePayload {
   324  		str := fmt.Sprintf("message payload is too large - header "+
   325  			"indicates %d bytes, but max message payload is %d "+
   326  			"bytes.", hdr.length, MaxMessagePayload)
   327  		return totalBytes, nil, nil, messageError("ReadMessage", str)
   328  
   329  	}
   330  
   331  	// Check for messages from the wrong bitcoin network.
   332  	if hdr.magic != btcnet {
   333  		discardInput(r, hdr.length)
   334  		str := fmt.Sprintf("message from other network [%v]", hdr.magic)
   335  		return totalBytes, nil, nil, messageError("ReadMessage", str)
   336  	}
   337  
   338  	// Check for malformed commands.
   339  	command := hdr.command
   340  	if !utf8.ValidString(command) {
   341  		discardInput(r, hdr.length)
   342  		str := fmt.Sprintf("invalid command %v", []byte(command))
   343  		return totalBytes, nil, nil, messageError("ReadMessage", str)
   344  	}
   345  
   346  	// Create struct of appropriate message type based on the command.
   347  	msg, err := makeEmptyMessage(command)
   348  	if err != nil {
   349  		discardInput(r, hdr.length)
   350  		return totalBytes, nil, nil, messageError("ReadMessage",
   351  			err.Error())
   352  	}
   353  
   354  	// Check for maximum length based on the message type as a malicious client
   355  	// could otherwise create a well-formed header and set the length to max
   356  	// numbers in order to exhaust the machine's memory.
   357  	mpl := msg.MaxPayloadLength(pver)
   358  	if hdr.length > mpl {
   359  		discardInput(r, hdr.length)
   360  		str := fmt.Sprintf("payload exceeds max length - header "+
   361  			"indicates %v bytes, but max payload size for "+
   362  			"messages of type [%v] is %v.", hdr.length, command, mpl)
   363  		return totalBytes, nil, nil, messageError("ReadMessage", str)
   364  	}
   365  
   366  	// Read payload.
   367  	payload := make([]byte, hdr.length)
   368  	n, err = io.ReadFull(r, payload)
   369  	totalBytes += n
   370  	if err != nil {
   371  		return totalBytes, nil, nil, err
   372  	}
   373  
   374  	// Test checksum.
   375  	checksum := chainhash.DoubleHashB(payload)[0:4]
   376  	if !bytes.Equal(checksum[:], hdr.checksum[:]) {
   377  		str := fmt.Sprintf("payload checksum failed - header "+
   378  			"indicates %v, but actual checksum is %v.",
   379  			hdr.checksum, checksum)
   380  		return totalBytes, nil, nil, messageError("ReadMessage", str)
   381  	}
   382  
   383  	// Unmarshal message.  NOTE: This must be a *bytes.Buffer since the
   384  	// MsgVersion BtcDecode function requires it.
   385  	pr := bytes.NewBuffer(payload)
   386  	err = msg.BtcDecode(pr, pver, enc)
   387  	if err != nil {
   388  		return totalBytes, nil, nil, err
   389  	}
   390  
   391  	return totalBytes, msg, payload, nil
   392  }
   393  
   394  // ReadMessageN reads, validates, and parses the next bitcoin Message from r for
   395  // the provided protocol version and bitcoin network.  It returns the number of
   396  // bytes read in addition to the parsed Message and raw bytes which comprise the
   397  // message.  This function is the same as ReadMessage except it also returns the
   398  // number of bytes read.
   399  func ReadMessageN(r io.Reader, pver uint32, btcnet BitcoinNet) (int, Message, []byte, error) {
   400  	return ReadMessageWithEncodingN(r, pver, btcnet, BaseEncoding)
   401  }
   402  
   403  // ReadMessage reads, validates, and parses the next bitcoin Message from r for
   404  // the provided protocol version and bitcoin network.  It returns the parsed
   405  // Message and raw bytes which comprise the message.  This function only differs
   406  // from ReadMessageN in that it doesn't return the number of bytes read.  This
   407  // function is mainly provided for backwards compatibility with the original
   408  // API, but it's also useful for callers that don't care about byte counts.
   409  func ReadMessage(r io.Reader, pver uint32, btcnet BitcoinNet) (Message, []byte, error) {
   410  	_, msg, buf, err := ReadMessageN(r, pver, btcnet)
   411  	return msg, buf, err
   412  }