hello.go 2.7 KB

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