hello.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  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, 8)
  52. if _, err := io.ReadFull(c, header); err != nil {
  53. return HelloResult{}, err
  54. }
  55. switch binary.BigEndian.Uint32(header[:4]) {
  56. case HelloMessageMagic:
  57. // This is a v0.14 Hello message in proto format
  58. msgSize := binary.BigEndian.Uint32(header[4:])
  59. if msgSize > 1024 {
  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. res := HelloResult{
  71. DeviceName: hello.DeviceName,
  72. ClientName: hello.ClientName,
  73. ClientVersion: hello.ClientVersion,
  74. }
  75. return res, nil
  76. case Version13HelloMagic:
  77. // This is a v0.13 Hello message in XDR format
  78. msgSize := binary.BigEndian.Uint32(header[4:])
  79. if msgSize > 1024 {
  80. return HelloResult{}, fmt.Errorf("hello message too big")
  81. }
  82. buf := make([]byte, msgSize)
  83. if _, err := io.ReadFull(c, buf); err != nil {
  84. return HelloResult{}, err
  85. }
  86. var hello Version13HelloMessage
  87. if err := hello.UnmarshalXDR(buf); err != nil {
  88. return HelloResult{}, err
  89. }
  90. res := HelloResult{
  91. DeviceName: hello.DeviceName,
  92. ClientName: hello.ClientName,
  93. ClientVersion: hello.ClientVersion,
  94. }
  95. return res, ErrTooOldVersion13
  96. case 0x00010001, 0x00010000:
  97. // This is the first word of a v0.12 cluster config message.
  98. // (Version 0, message ID 1, message type 0, compression enabled or disabled)
  99. return HelloResult{}, ErrTooOldVersion12
  100. }
  101. return HelloResult{}, ErrUnknownMagic
  102. }
  103. func writeHello(c io.Writer, h HelloIntf) error {
  104. msg, err := h.Marshal()
  105. if err != nil {
  106. return err
  107. }
  108. header := make([]byte, 8)
  109. binary.BigEndian.PutUint32(header[:4], h.Magic())
  110. binary.BigEndian.PutUint32(header[4:], uint32(len(msg)))
  111. _, err = c.Write(append(header, msg...))
  112. return err
  113. }