hello.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. // Copyright (C) 2016 The Protocol Authors.
  2. package protocol
  3. import (
  4. "encoding/binary"
  5. "errors"
  6. "fmt"
  7. "io"
  8. )
  9. // The HelloMessage 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 HelloMessage 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. // 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 HelloMessage) (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 ErrTooOldVersion12, ErrUnknownMagic:
  42. return true
  43. default:
  44. return false
  45. }
  46. }
  47. func readHello(c io.Reader) (HelloResult, error) {
  48. header := make([]byte, 8)
  49. if _, err := io.ReadFull(c, header); err != nil {
  50. return HelloResult{}, err
  51. }
  52. switch binary.BigEndian.Uint32(header[:4]) {
  53. case Version13HelloMagic:
  54. // This is a v0.13 Hello message in XDR format
  55. msgSize := binary.BigEndian.Uint32(header[4:])
  56. if msgSize > 1024 {
  57. return HelloResult{}, fmt.Errorf("hello message too big")
  58. }
  59. buf := make([]byte, msgSize)
  60. if _, err := io.ReadFull(c, buf); err != nil {
  61. return HelloResult{}, err
  62. }
  63. var hello Version13HelloMessage
  64. if err := hello.UnmarshalXDR(buf); err != nil {
  65. return HelloResult{}, err
  66. }
  67. res := HelloResult{
  68. DeviceName: hello.DeviceName,
  69. ClientName: hello.ClientName,
  70. ClientVersion: hello.ClientVersion,
  71. }
  72. return res, nil
  73. case 0x00010001, 0x00010000:
  74. // This is the first word of a v0.12 cluster config message.
  75. // (Version 0, message ID 1, message type 0, compression enabled or disabled)
  76. return HelloResult{}, ErrTooOldVersion12
  77. }
  78. return HelloResult{}, ErrUnknownMagic
  79. }
  80. func writeHello(c io.Writer, h HelloMessage) error {
  81. msg, err := h.Marshal()
  82. if err != nil {
  83. return err
  84. }
  85. header := make([]byte, 8)
  86. binary.BigEndian.PutUint32(header[:4], h.Magic())
  87. binary.BigEndian.PutUint32(header[4:], uint32(len(msg)))
  88. _, err = c.Write(append(header, msg...))
  89. return err
  90. }