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