hello.go 2.9 KB

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