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 }