1 package wire
2 3 import (
4 "crypto/rand"
5 "encoding/binary"
6 "fmt"
7 "io"
8 "math"
9 "time"
10 11 "github.com/p9c/p9/pkg/chainhash"
12 )
13 14 const (
15 // MaxVarIntPayload is the maximum payload size for a variable length integer.
16 MaxVarIntPayload = 9
17 // binaryFreeListMaxItems is the number of buffers to keep in the free list to
18 // use for binary serialization and deserialization.
19 binaryFreeListMaxItems = 16384
20 )
21 22 var (
23 // littleEndian is a convenience variable since binary.LittleEndian is quite
24 // long.
25 littleEndian = binary.LittleEndian
26 // bigEndian is a convenience variable since binary.BigEndian is quite long.
27 bigEndian = binary.BigEndian
28 )
29 30 // binaryFreeList defines a concurrent safe free list of byte slices (up to the
31 // maximum number defined by the binaryFreeListMaxItems constant) that have a
32 // cap of 8 (thus it supports up to a uint64). It is used to provide temporary
33 // buffers for deserializing primitive numbers to and from their binary encoding
34 // in order to greatly reduce the number of allocations required. For
35 // convenience, functions are provided for each of the primitive unsigned
36 // integers that automatically obtain a buffer from the free list, perform the
37 // necessary binary conversion, read from or write to the given io.Reader or
38 // io.Writer, and return the buffer to the free list.
39 type binaryFreeList chan []byte
40 41 // Borrow returns a byte slice from the free list with a length of 8. A new buffer is allocated if there are not any available on the free list.
42 func (l binaryFreeList) Borrow() []byte {
43 var buf []byte
44 select {
45 case buf = <-l:
46 default:
47 buf = make([]byte, 8)
48 }
49 return buf[:8]
50 }
51 52 // Return puts the provided byte slice back on the free list. The buffer MUST have been obtained via the Borrow function
53 // and therefore have a cap of 8.
54 func (l binaryFreeList) Return(buf []byte) {
55 select {
56 case l <- buf:
57 default:
58 // Let it go to the garbage collector.
59 }
60 }
61 62 // Uint8 reads a single byte from the provided reader using a buffer from the free list and returns it as a uint8.
63 func (l binaryFreeList) Uint8(r io.Reader) (rv uint8, e error) {
64 buf := l.Borrow()[:1]
65 if _, e = io.ReadFull(r, buf); E.Chk(e) {
66 l.Return(buf)
67 return 0, e
68 }
69 rv = buf[0]
70 l.Return(buf)
71 return rv, nil
72 }
73 74 // Uint16 reads two bytes from the provided reader using a buffer from the free
75 // list, converts it to a number using the provided byte order, and returns the
76 // resulting uint16.
77 func (l binaryFreeList) Uint16(r io.Reader, byteOrder binary.ByteOrder) (rv uint16, e error) {
78 buf := l.Borrow()[:2]
79 if _, e = io.ReadFull(r, buf); E.Chk(e) {
80 l.Return(buf)
81 return
82 }
83 rv = byteOrder.Uint16(buf)
84 l.Return(buf)
85 return rv, nil
86 }
87 88 // Uint32 reads four bytes from the provided reader using a buffer from the free
89 // list, converts it to a number using the provided byte order, and returns the
90 // resulting uint32.
91 func (l binaryFreeList) Uint32(r io.Reader, byteOrder binary.ByteOrder) (rv uint32, e error) {
92 buf := l.Borrow()[:4]
93 if _, e = io.ReadFull(r, buf); E.Chk(e) {
94 l.Return(buf)
95 return 0, e
96 }
97 rv = byteOrder.Uint32(buf)
98 l.Return(buf)
99 return rv, nil
100 }
101 102 // Uint64 reads eight bytes from the provided reader using a buffer from the
103 // free list, converts it to a number using the provided byte order, and returns
104 // the resulting uint64.
105 func (l binaryFreeList) Uint64(r io.Reader, byteOrder binary.ByteOrder) (rv uint64, e error) {
106 buf := l.Borrow()[:8]
107 if _, e = io.ReadFull(r, buf); E.Chk(e) {
108 l.Return(buf)
109 return
110 }
111 rv = byteOrder.Uint64(buf)
112 l.Return(buf)
113 return rv, nil
114 }
115 116 // PutUint8 copies the provided uint8 into a buffer from the free list and writes the resulting byte to the given
117 // writer.
118 func (l binaryFreeList) PutUint8(w io.Writer, val uint8) (e error) {
119 buf := l.Borrow()[:1]
120 buf[0] = val
121 _, e = w.Write(buf)
122 l.Return(buf)
123 return e
124 }
125 126 // PutUint16 serializes the provided uint16 using the given byte order into a buffer from the free list and writes the
127 // resulting two bytes to the given writer.
128 func (l binaryFreeList) PutUint16(w io.Writer, byteOrder binary.ByteOrder, val uint16) (e error) {
129 buf := l.Borrow()[:2]
130 byteOrder.PutUint16(buf, val)
131 _, e = w.Write(buf)
132 l.Return(buf)
133 return
134 }
135 136 // PutUint32 serializes the provided uint32 using the given byte order into a buffer from the free list and writes the
137 // resulting four bytes to the given writer.
138 func (l binaryFreeList) PutUint32(w io.Writer, byteOrder binary.ByteOrder, val uint32) (e error) {
139 buf := l.Borrow()[:4]
140 byteOrder.PutUint32(buf, val)
141 _, e = w.Write(buf)
142 l.Return(buf)
143 return
144 }
145 146 // PutUint64 serializes the provided uint64 using the given byte order into a buffer from the free list and writes the
147 // resulting eight bytes to the given writer.
148 func (l binaryFreeList) PutUint64(w io.Writer, byteOrder binary.ByteOrder, val uint64) (e error) {
149 buf := l.Borrow()[:8]
150 byteOrder.PutUint64(buf, val)
151 _, e = w.Write(buf)
152 l.Return(buf)
153 return
154 }
155 156 // binarySerializer provides a free list of buffers to use for serializing and deserializing primitive integer values to
157 // and from io.Readers and io.Writers.
158 var binarySerializer binaryFreeList = make(chan []byte, binaryFreeListMaxItems)
159 160 // errNonCanonicalVarInt is the common format string used for non-canonically encoded variable length integer errors.
161 var errNonCanonicalVarInt = "non-canonical varint %x - discriminant %x must " +
162 "encode a value greater than %x"
163 164 // uint32Time represents a unix timestamp encoded with a uint32. It is used as a way to signal the readElement function
165 // how to decode a timestamp into a Go time.Time since it is otherwise ambiguous.
166 type uint32Time time.Time
167 168 // int64Time represents a unix timestamp encoded with an int64. It is used as a way to signal the readElement function
169 // how to decode a timestamp into a Go time.Time since it is otherwise ambiguous.
170 type int64Time time.Time
171 172 // readElement reads the next sequence of bytes from r using little endian depending on the concrete type of element
173 // pointed to.
174 func readElement(r io.Reader, element interface{}) (e error) {
175 // Attempt to read the element based on the concrete type via fast type assertions first.
176 switch l := element.(type) {
177 case *int32:
178 var rv uint32
179 if rv, e = binarySerializer.Uint32(r, littleEndian); E.Chk(e) {
180 return
181 }
182 *l = int32(rv)
183 return
184 case *uint32:
185 var rv uint32
186 if rv, e = binarySerializer.Uint32(r, littleEndian); E.Chk(e) {
187 return
188 }
189 *l = rv
190 return
191 case *int64:
192 var rv uint64
193 if rv, e = binarySerializer.Uint64(r, littleEndian); E.Chk(e) {
194 return
195 }
196 *l = int64(rv)
197 return
198 case *uint64:
199 var rv uint64
200 if rv, e = binarySerializer.Uint64(r, littleEndian); E.Chk(e) {
201 return
202 }
203 *l = rv
204 return
205 case *bool:
206 var rv uint8
207 if rv, e = binarySerializer.Uint8(r); E.Chk(e) {
208 return
209 }
210 if rv == 0x00 {
211 *l = false
212 } else {
213 *l = true
214 }
215 return nil
216 // Unix timestamp encoded as a uint32.
217 case *uint32Time:
218 var rv uint32
219 if rv, e = binarySerializer.Uint32(r, binary.LittleEndian); E.Chk(e) {
220 return
221 }
222 *l = uint32Time(time.Unix(int64(rv), 0))
223 return
224 // Unix timestamp encoded as an int64.
225 case *int64Time:
226 var rv uint64
227 if rv, e = binarySerializer.Uint64(r, binary.LittleEndian); E.Chk(e) {
228 return
229 }
230 *l = int64Time(time.Unix(int64(rv), 0))
231 return
232 // Message header checksum.
233 case *[4]byte:
234 if _, e = io.ReadFull(r, l[:]); E.Chk(e) {
235 }
236 return
237 // Message header command.
238 case *[CommandSize]uint8:
239 if _, e = io.ReadFull(r, l[:]); E.Chk(e) {
240 }
241 return
242 // IP address.
243 case *[16]byte:
244 if _, e = io.ReadFull(r, l[:]); E.Chk(e) {
245 }
246 return
247 case *chainhash.Hash:
248 if _, e = io.ReadFull(r, l[:]); E.Chk(e) {
249 return
250 }
251 return nil
252 case *ServiceFlag:
253 var rv uint64
254 if rv, e = binarySerializer.Uint64(r, littleEndian); E.Chk(e) {
255 return
256 }
257 *l = ServiceFlag(rv)
258 return
259 case *InvType:
260 var rv uint32
261 if rv, e = binarySerializer.Uint32(r, littleEndian); E.Chk(e) {
262 return
263 }
264 *l = InvType(rv)
265 return
266 case *BitcoinNet:
267 var rv uint32
268 if rv, e = binarySerializer.Uint32(r, littleEndian); E.Chk(e) {
269 return
270 }
271 *l = BitcoinNet(rv)
272 return
273 case *BloomUpdateType:
274 var rv uint8
275 if rv, e = binarySerializer.Uint8(r); E.Chk(e) {
276 return
277 }
278 *l = BloomUpdateType(rv)
279 return
280 case *RejectCode:
281 var rv uint8
282 if rv, e = binarySerializer.Uint8(r); E.Chk(e) {
283 return
284 }
285 *l = RejectCode(rv)
286 return
287 }
288 // Fall back to the slower binary.Read if a fast path was not available above.
289 return binary.Read(r, littleEndian, element)
290 }
291 292 // readElements reads multiple items from r. It is equivalent to multiple calls to readElement.
293 func readElements(r io.Reader, elements ...interface{}) (e error) {
294 for _, element := range elements {
295 if e = readElement(r, element); E.Chk(e) {
296 return
297 }
298 }
299 return
300 }
301 302 // writeElement writes the little endian representation of element to w.
303 func writeElement(w io.Writer, element interface{}) (e error) {
304 // Attempt to write the element based on the concrete type via fast type assertions first.
305 switch el := element.(type) {
306 case int32:
307 if e = binarySerializer.PutUint32(w, littleEndian, uint32(el)); E.Chk(e) {
308 return
309 }
310 return
311 case uint32:
312 if e = binarySerializer.PutUint32(w, littleEndian, el); E.Chk(e) {
313 }
314 return
315 case int64:
316 if e = binarySerializer.PutUint64(w, littleEndian, uint64(el)); E.Chk(e) {
317 }
318 return
319 case uint64:
320 if e = binarySerializer.PutUint64(w, littleEndian, el); E.Chk(e) {
321 }
322 return
323 case bool:
324 if el {
325 e = binarySerializer.PutUint8(w, 0x01)
326 } else {
327 e = binarySerializer.PutUint8(w, 0x00)
328 }
329 if E.Chk(e) {
330 return
331 }
332 return nil
333 // Message header checksum.
334 case [4]byte:
335 if _, e = w.Write(el[:]); E.Chk(e) {
336 return
337 }
338 return
339 // Message header command.
340 case [CommandSize]uint8:
341 if _, e = w.Write(el[:]); E.Chk(e) {
342 }
343 return
344 // IP address.
345 case [16]byte:
346 if _, e = w.Write(el[:]); E.Chk(e) {
347 }
348 return
349 case *chainhash.Hash:
350 if _, e = w.Write(el[:]); E.Chk(e) {
351 }
352 return
353 case ServiceFlag:
354 if e = binarySerializer.PutUint64(w, littleEndian, uint64(el)); E.Chk(e) {
355 }
356 return
357 case InvType:
358 if e = binarySerializer.PutUint32(w, littleEndian, uint32(el)); E.Chk(e) {
359 }
360 return
361 case BitcoinNet:
362 if e = binarySerializer.PutUint32(w, littleEndian, uint32(el)); E.Chk(e) {
363 }
364 return
365 case BloomUpdateType:
366 if e = binarySerializer.PutUint8(w, uint8(el)); E.Chk(e) {
367 }
368 return
369 case RejectCode:
370 if e = binarySerializer.PutUint8(w, uint8(el)); E.Chk(e) {
371 return
372 }
373 return
374 }
375 // Fall back to the slower binary.Write if a fast path was not available above.
376 return binary.Write(w, littleEndian, element)
377 }
378 379 // writeElements writes multiple items to w. It is equivalent to multiple calls to writeElement.
380 func writeElements(w io.Writer, elements ...interface{}) (e error) {
381 for _, element := range elements {
382 if e = writeElement(w, element); E.Chk(e) {
383 return
384 }
385 }
386 return
387 }
388 389 // ReadVarInt reads a variable length integer from r and returns it as a uint64.
390 func ReadVarInt(r io.Reader, pver uint32) (rv uint64, e error) {
391 var discriminant uint8
392 if discriminant, e = binarySerializer.Uint8(r); E.Chk(e) {
393 return
394 }
395 switch discriminant {
396 case 0xff:
397 var sv uint64
398 if sv, e = binarySerializer.Uint64(r, littleEndian); E.Chk(e) {
399 return
400 }
401 rv = sv
402 // The encoding is not canonical if the value could have been encoded using fewer bytes.
403 min := uint64(0x100000000)
404 if rv < min {
405 return 0, messageError(
406 "ReadVarInt", fmt.Sprintf(
407 errNonCanonicalVarInt, rv, discriminant, min,
408 ),
409 )
410 }
411 case 0xfe:
412 var sv uint32
413 if sv, e = binarySerializer.Uint32(r, littleEndian); E.Chk(e) {
414 return
415 }
416 rv = uint64(sv)
417 // The encoding is not canonical if the value could have been encoded using fewer bytes.
418 min := uint64(0x10000)
419 if rv < min {
420 return 0, messageError(
421 "ReadVarInt", fmt.Sprintf(
422 errNonCanonicalVarInt, rv, discriminant, min,
423 ),
424 )
425 }
426 case 0xfd:
427 var sv uint16
428 if sv, e = binarySerializer.Uint16(r, littleEndian); E.Chk(e) {
429 return
430 }
431 rv = uint64(sv)
432 // The encoding is not canonical if the value could have been encoded using fewer bytes.
433 min := uint64(0xfd)
434 if rv < min {
435 return 0, messageError(
436 "ReadVarInt", fmt.Sprintf(
437 errNonCanonicalVarInt, rv, discriminant, min,
438 ),
439 )
440 }
441 default:
442 rv = uint64(discriminant)
443 }
444 return
445 }
446 447 // WriteVarInt serializes val to w using a variable number of bytes depending on its value.
448 func WriteVarInt(w io.Writer, pver uint32, val uint64) (e error) {
449 if val < 0xfd {
450 return binarySerializer.PutUint8(w, uint8(val))
451 }
452 if val <= math.MaxUint16 {
453 if e = binarySerializer.PutUint8(w, 0xfd); E.Chk(e) {
454 return
455 }
456 return binarySerializer.PutUint16(w, littleEndian, uint16(val))
457 }
458 if val <= math.MaxUint32 {
459 if e = binarySerializer.PutUint8(w, 0xfe); E.Chk(e) {
460 return
461 }
462 return binarySerializer.PutUint32(w, littleEndian, uint32(val))
463 }
464 if e = binarySerializer.PutUint8(w, 0xff); E.Chk(e) {
465 return
466 }
467 return binarySerializer.PutUint64(w, littleEndian, val)
468 }
469 470 // VarIntSerializeSize returns the number of bytes it would take to serialize val as a variable length integer.
471 func VarIntSerializeSize(val uint64) int {
472 // The value is small enough to be represented by itself, so it's just 1 byte.
473 if val < 0xfd {
474 return 1
475 }
476 // Discriminant 1 byte plus 2 bytes for the uint16.
477 if val <= math.MaxUint16 {
478 return 3
479 }
480 // Discriminant 1 byte plus 4 bytes for the uint32.
481 if val <= math.MaxUint32 {
482 return 5
483 }
484 // Discriminant 1 byte plus 8 bytes for the uint64.
485 return 9
486 }
487 488 // ReadVarString reads a variable length string from r and returns it as a Go string. A variable length string is
489 // encoded as a variable length integer containing the length of the string followed by the bytes that represent the
490 // string itself. An error is returned if the length is greater than the maximum block payload size since it helps
491 // protect against memory exhaustion attacks and forced panics through malformed messages.
492 func ReadVarString(r io.Reader, pver uint32) (s string, e error) {
493 var count uint64
494 if count, e = ReadVarInt(r, pver); E.Chk(e) {
495 return
496 }
497 // Prevent variable length strings that are larger than the maximum message size. It would be possible to cause
498 // memory exhaustion and panics without a sane upper bound on this count.
499 if count > MaxMessagePayload {
500 str := fmt.Sprintf(
501 "variable length string is too long "+
502 "[count %d, max %d]", count, MaxMessagePayload,
503 )
504 return "", messageError("ReadVarString", str)
505 }
506 buf := make([]byte, count)
507 if _, e = io.ReadFull(r, buf); E.Chk(e) {
508 return
509 }
510 return string(buf), e
511 }
512 513 // WriteVarString serializes str to w as a variable length integer containing the length of the string followed by the
514 // bytes that represent the string itself.
515 func WriteVarString(w io.Writer, pver uint32, str string) (e error) {
516 if e = WriteVarInt(w, pver, uint64(len(str))); E.Chk(e) {
517 return
518 }
519 _, e = w.Write([]byte(str))
520 return
521 }
522 523 // ReadVarBytes reads a variable length byte array. A byte array is encoded as a varInt containing the length of the
524 // array followed by the bytes themselves. An error is returned if the length is greater than the passed maxAllowed
525 // parameter which helps protect against memory exhaustion attacks and forced panics through malformed messages. The
526 // fieldName parameter is only used for the error message so it provides more context in the error.
527 func ReadVarBytes(r io.Reader, pver uint32, maxAllowed uint32, fieldName string) (b []byte, e error) {
528 var count uint64
529 if count, e = ReadVarInt(r, pver); E.Chk(e) {
530 return
531 }
532 // Prevent byte array larger than the max message size. It would be possible to cause memory exhaustion and panics
533 // without a sane upper bound on this count.
534 if count > uint64(maxAllowed) {
535 str := fmt.Sprintf(
536 "%s is larger than the max allowed size "+
537 "[count %d, max %d]", fieldName, count, maxAllowed,
538 )
539 e = messageError("ReadVarBytes", str)
540 return
541 }
542 b = make([]byte, count)
543 if _, e = io.ReadFull(r, b); E.Chk(e) {
544 }
545 return
546 }
547 548 // WriteVarBytes serializes a variable length byte array to w as a varInt containing the number of bytes, followed by
549 // the bytes themselves.
550 func WriteVarBytes(w io.Writer, pver uint32, bytes []byte) (e error) {
551 slen := uint64(len(bytes))
552 if e = WriteVarInt(w, pver, slen); E.Chk(e) {
553 return
554 }
555 _, e = w.Write(bytes)
556 return
557 }
558 559 // randomUint64 returns a cryptographically random uint64 value. This unexported version takes a reader primarily to
560 // ensure the error paths can be properly tested by passing a fake reader in the tests.
561 func randomUint64(r io.Reader) (rv uint64, e error) {
562 if rv, e = binarySerializer.Uint64(r, bigEndian); E.Chk(e) {
563 }
564 return
565 }
566 567 // RandomUint64 returns a cryptographically random uint64 value.
568 func RandomUint64() (rv uint64, e error) {
569 return randomUint64(rand.Reader)
570 }
571