hello.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  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. // ErrTooOldVersion12 is returned by ExchangeHello when the other side
  25. // speaks the older, incompatible version 0.12 of the protocol.
  26. ErrTooOldVersion12 = errors.New("the remote device speaks an older version of the protocol (v0.12) not compatible with this version")
  27. // ErrTooOldVersion13 is returned by ExchangeHello when the other side
  28. // speaks the older, incompatible version 0.12 of the protocol.
  29. ErrTooOldVersion13 = errors.New("the remote device speaks an older version of the protocol (v0.13) not compatible with this version")
  30. // ErrUnknownMagic is returned by ExchangeHellow when the other side
  31. // speaks something entirely unknown.
  32. ErrUnknownMagic = errors.New("the remote device speaks an unknown (newer?) version of the protocol")
  33. )
  34. func ExchangeHello(c io.ReadWriter, h HelloIntf) (HelloResult, error) {
  35. if err := writeHello(c, h); err != nil {
  36. return HelloResult{}, err
  37. }
  38. return readHello(c)
  39. }
  40. // IsVersionMismatch returns true if the error is a reliable indication of a
  41. // version mismatch that we might want to alert the user about.
  42. func IsVersionMismatch(err error) bool {
  43. switch err {
  44. case ErrTooOldVersion12, ErrTooOldVersion13, ErrUnknownMagic:
  45. return true
  46. default:
  47. return false
  48. }
  49. }
  50. func readHello(c io.Reader) (HelloResult, error) {
  51. header := make([]byte, 4)
  52. if _, err := io.ReadFull(c, header); err != nil {
  53. return HelloResult{}, err
  54. }
  55. switch binary.BigEndian.Uint32(header) {
  56. case HelloMessageMagic:
  57. // This is a v0.14 Hello message in proto format
  58. if _, err := io.ReadFull(c, header[:2]); err != nil {
  59. return HelloResult{}, err
  60. }
  61. msgSize := binary.BigEndian.Uint16(header[:2])
  62. if msgSize > 32767 {
  63. return HelloResult{}, fmt.Errorf("hello message too big")
  64. }
  65. buf := make([]byte, msgSize)
  66. if _, err := io.ReadFull(c, buf); err != nil {
  67. return HelloResult{}, err
  68. }
  69. var hello Hello
  70. if err := hello.Unmarshal(buf); err != nil {
  71. return HelloResult{}, err
  72. }
  73. res := HelloResult{
  74. DeviceName: hello.DeviceName,
  75. ClientName: hello.ClientName,
  76. ClientVersion: hello.ClientVersion,
  77. }
  78. return res, nil
  79. case Version13HelloMagic:
  80. // This is a v0.13 Hello message in XDR format
  81. if _, err := io.ReadFull(c, header[:4]); err != nil {
  82. return HelloResult{}, err
  83. }
  84. msgSize := binary.BigEndian.Uint32(header[:4])
  85. if msgSize > 1024 {
  86. return HelloResult{}, fmt.Errorf("hello message too big")
  87. }
  88. buf := make([]byte, msgSize)
  89. if _, err := io.ReadFull(c, buf); err != nil {
  90. return HelloResult{}, err
  91. }
  92. var hello Version13HelloMessage
  93. if err := hello.UnmarshalXDR(buf); err != nil {
  94. return HelloResult{}, err
  95. }
  96. res := HelloResult(hello)
  97. return res, ErrTooOldVersion13
  98. case 0x00010001, 0x00010000:
  99. // This is the first word of a v0.12 cluster config message.
  100. // (Version 0, message ID 1, message type 0, compression enabled or disabled)
  101. return HelloResult{}, ErrTooOldVersion12
  102. }
  103. return HelloResult{}, ErrUnknownMagic
  104. }
  105. func writeHello(c io.Writer, h HelloIntf) error {
  106. msg, err := h.Marshal()
  107. if err != nil {
  108. return err
  109. }
  110. if len(msg) > 32767 {
  111. // The header length must be a positive signed int16
  112. panic("bug: attempting to serialize too large hello message")
  113. }
  114. header := make([]byte, 6)
  115. binary.BigEndian.PutUint32(header[:4], h.Magic())
  116. binary.BigEndian.PutUint16(header[4:], uint16(len(msg)))
  117. _, err = c.Write(append(header, msg...))
  118. return err
  119. }