bep_hello.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. // Copyright (C) 2016 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package protocol
  7. import (
  8. "encoding/binary"
  9. "errors"
  10. "io"
  11. "google.golang.org/protobuf/proto"
  12. "github.com/syncthing/syncthing/internal/gen/bep"
  13. )
  14. const (
  15. HelloMessageMagic uint32 = 0x2EA7D90B
  16. Version13HelloMagic uint32 = 0x9F79BC40 // old
  17. )
  18. var (
  19. // ErrTooOldVersion is returned by ExchangeHello when the other side
  20. // speaks an older, incompatible version of the protocol.
  21. ErrTooOldVersion = errors.New("the remote device speaks an older version of the protocol not compatible with this version")
  22. // ErrUnknownMagic is returned by ExchangeHello when the other side
  23. // speaks something entirely unknown.
  24. ErrUnknownMagic = errors.New("the remote device speaks an unknown (newer?) version of the protocol")
  25. )
  26. type Hello struct {
  27. DeviceName string
  28. ClientName string
  29. ClientVersion string
  30. NumConnections int
  31. Timestamp int64
  32. }
  33. func (h *Hello) toWire() *bep.Hello {
  34. return &bep.Hello{
  35. DeviceName: h.DeviceName,
  36. ClientName: h.ClientName,
  37. ClientVersion: h.ClientVersion,
  38. NumConnections: int32(h.NumConnections),
  39. Timestamp: h.Timestamp,
  40. }
  41. }
  42. func helloFromWire(w *bep.Hello) Hello {
  43. return Hello{
  44. DeviceName: w.DeviceName,
  45. ClientName: w.ClientName,
  46. ClientVersion: w.ClientVersion,
  47. NumConnections: int(w.NumConnections),
  48. Timestamp: w.Timestamp,
  49. }
  50. }
  51. func (Hello) Magic() uint32 {
  52. return HelloMessageMagic
  53. }
  54. func ExchangeHello(c io.ReadWriter, h Hello) (Hello, error) {
  55. if h.Timestamp == 0 {
  56. panic("bug: missing timestamp in outgoing hello")
  57. }
  58. if err := writeHello(c, h); err != nil {
  59. return Hello{}, err
  60. }
  61. return readHello(c)
  62. }
  63. // IsVersionMismatch returns true if the error is a reliable indication of a
  64. // version mismatch that we might want to alert the user about.
  65. func IsVersionMismatch(err error) bool {
  66. return errors.Is(err, ErrTooOldVersion) || errors.Is(err, ErrUnknownMagic)
  67. }
  68. func readHello(c io.Reader) (Hello, error) {
  69. header := make([]byte, 4)
  70. if _, err := io.ReadFull(c, header); err != nil {
  71. return Hello{}, err
  72. }
  73. switch binary.BigEndian.Uint32(header) {
  74. case HelloMessageMagic:
  75. // This is a v0.14 Hello message in proto format
  76. if _, err := io.ReadFull(c, header[:2]); err != nil {
  77. return Hello{}, err
  78. }
  79. msgSize := binary.BigEndian.Uint16(header[:2])
  80. if msgSize > 32767 {
  81. return Hello{}, errors.New("hello message too big")
  82. }
  83. buf := make([]byte, msgSize)
  84. if _, err := io.ReadFull(c, buf); err != nil {
  85. return Hello{}, err
  86. }
  87. var wh bep.Hello
  88. if err := proto.Unmarshal(buf, &wh); err != nil {
  89. return Hello{}, err
  90. }
  91. return helloFromWire(&wh), nil
  92. case 0x00010001, 0x00010000, Version13HelloMagic:
  93. // This is the first word of an older cluster config message or an
  94. // old magic number. (Version 0, message ID 1, message type 0,
  95. // compression enabled or disabled)
  96. return Hello{}, ErrTooOldVersion
  97. }
  98. return Hello{}, ErrUnknownMagic
  99. }
  100. func writeHello(c io.Writer, h Hello) error {
  101. msg, err := proto.Marshal(h.toWire())
  102. if err != nil {
  103. return err
  104. }
  105. if len(msg) > 32767 {
  106. // The header length must be a positive signed int16
  107. panic("bug: attempting to serialize too large hello message")
  108. }
  109. header := make([]byte, 6, 6+len(msg))
  110. binary.BigEndian.PutUint32(header[:4], h.Magic())
  111. binary.BigEndian.PutUint16(header[4:], uint16(len(msg)))
  112. _, err = c.Write(append(header, msg...))
  113. return err
  114. }