hello.go 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. // Copyright (C) 2016 The Protocol Authors.
  2. package protocol
  3. import (
  4. "encoding/binary"
  5. "errors"
  6. "io"
  7. )
  8. var (
  9. // ErrTooOldVersion is returned by ExchangeHello when the other side
  10. // speaks an older, incompatible version of the protocol.
  11. ErrTooOldVersion = errors.New("the remote device speaks an older version of the protocol not compatible with this version")
  12. // ErrUnknownMagic is returned by ExchangeHello when the other side
  13. // speaks something entirely unknown.
  14. ErrUnknownMagic = errors.New("the remote device speaks an unknown (newer?) version of the protocol")
  15. )
  16. func ExchangeHello(c io.ReadWriter, h Hello) (Hello, error) {
  17. if h.Timestamp == 0 {
  18. panic("bug: missing timestamp in outgoing hello")
  19. }
  20. if err := writeHello(c, h); err != nil {
  21. return Hello{}, err
  22. }
  23. return readHello(c)
  24. }
  25. // IsVersionMismatch returns true if the error is a reliable indication of a
  26. // version mismatch that we might want to alert the user about.
  27. func IsVersionMismatch(err error) bool {
  28. switch err {
  29. case ErrTooOldVersion, ErrUnknownMagic:
  30. return true
  31. default:
  32. return false
  33. }
  34. }
  35. func readHello(c io.Reader) (Hello, error) {
  36. header := make([]byte, 4)
  37. if _, err := io.ReadFull(c, header); err != nil {
  38. return Hello{}, err
  39. }
  40. switch binary.BigEndian.Uint32(header) {
  41. case HelloMessageMagic:
  42. // This is a v0.14 Hello message in proto format
  43. if _, err := io.ReadFull(c, header[:2]); err != nil {
  44. return Hello{}, err
  45. }
  46. msgSize := binary.BigEndian.Uint16(header[:2])
  47. if msgSize > 32767 {
  48. return Hello{}, errors.New("hello message too big")
  49. }
  50. buf := make([]byte, msgSize)
  51. if _, err := io.ReadFull(c, buf); err != nil {
  52. return Hello{}, err
  53. }
  54. var hello Hello
  55. if err := hello.Unmarshal(buf); err != nil {
  56. return Hello{}, err
  57. }
  58. return Hello(hello), nil
  59. case 0x00010001, 0x00010000, Version13HelloMagic:
  60. // This is the first word of an older cluster config message or an
  61. // old magic number. (Version 0, message ID 1, message type 0,
  62. // compression enabled or disabled)
  63. return Hello{}, ErrTooOldVersion
  64. }
  65. return Hello{}, ErrUnknownMagic
  66. }
  67. func writeHello(c io.Writer, h Hello) error {
  68. msg, err := h.Marshal()
  69. if err != nil {
  70. return err
  71. }
  72. if len(msg) > 32767 {
  73. // The header length must be a positive signed int16
  74. panic("bug: attempting to serialize too large hello message")
  75. }
  76. header := make([]byte, 6, 6+len(msg))
  77. binary.BigEndian.PutUint32(header[:4], h.Magic())
  78. binary.BigEndian.PutUint16(header[4:], uint16(len(msg)))
  79. _, err = c.Write(append(header, msg...))
  80. return err
  81. }