github.com/BlockABC/godash@v0.0.0-20191112120524-f4aa3a32c566/wire/message.go (about)

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