hello.go 2.9 KB

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