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 }