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