message.go raw

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