mirror of
https://github.com/syncthing/syncthing.git
synced 2024-11-16 18:41:59 -07:00
106 lines
2.8 KiB
Go
106 lines
2.8 KiB
Go
|
// Copyright (C) 2016 The Protocol Authors.
|
||
|
|
||
|
package protocol
|
||
|
|
||
|
import (
|
||
|
"encoding/binary"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
)
|
||
|
|
||
|
// The HelloMessage interface is implemented by the version specific hello
|
||
|
// message. It knows its magic number and how to serialize itself to a byte
|
||
|
// buffer.
|
||
|
type HelloMessage interface {
|
||
|
Magic() uint32
|
||
|
Marshal() ([]byte, error)
|
||
|
}
|
||
|
|
||
|
// The HelloResult is the non version specific interpretation of the other
|
||
|
// side's Hello message.
|
||
|
type HelloResult struct {
|
||
|
DeviceName string
|
||
|
ClientName string
|
||
|
ClientVersion string
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
// ErrTooOldVersion12 is returned by ExchangeHello when the other side
|
||
|
// speaks the older, incompatible version 0.12 of the protocol.
|
||
|
ErrTooOldVersion12 = errors.New("the remote device speaks an older version of the protocol (v0.12) not compatible with this version")
|
||
|
// ErrUnknownMagic is returned by ExchangeHellow when the other side
|
||
|
// speaks something entirely unknown.
|
||
|
ErrUnknownMagic = errors.New("the remote device speaks an unknown (newer?) version of the protocol")
|
||
|
)
|
||
|
|
||
|
func ExchangeHello(c io.ReadWriter, h HelloMessage) (HelloResult, error) {
|
||
|
if err := writeHello(c, h); err != nil {
|
||
|
return HelloResult{}, err
|
||
|
}
|
||
|
return readHello(c)
|
||
|
}
|
||
|
|
||
|
// IsVersionMismatch returns true if the error is a reliable indication of a
|
||
|
// version mismatch that we might want to alert the user about.
|
||
|
func IsVersionMismatch(err error) bool {
|
||
|
switch err {
|
||
|
case ErrTooOldVersion12, ErrUnknownMagic:
|
||
|
return true
|
||
|
default:
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func readHello(c io.Reader) (HelloResult, error) {
|
||
|
header := make([]byte, 8)
|
||
|
if _, err := io.ReadFull(c, header); err != nil {
|
||
|
return HelloResult{}, err
|
||
|
}
|
||
|
|
||
|
switch binary.BigEndian.Uint32(header[:4]) {
|
||
|
case Version13HelloMagic:
|
||
|
// This is a v0.13 Hello message in XDR format
|
||
|
msgSize := binary.BigEndian.Uint32(header[4:])
|
||
|
if msgSize > 1024 {
|
||
|
return HelloResult{}, fmt.Errorf("hello message too big")
|
||
|
}
|
||
|
buf := make([]byte, msgSize)
|
||
|
if _, err := io.ReadFull(c, buf); err != nil {
|
||
|
return HelloResult{}, err
|
||
|
}
|
||
|
|
||
|
var hello Version13HelloMessage
|
||
|
if err := hello.UnmarshalXDR(buf); err != nil {
|
||
|
return HelloResult{}, err
|
||
|
}
|
||
|
res := HelloResult{
|
||
|
DeviceName: hello.DeviceName,
|
||
|
ClientName: hello.ClientName,
|
||
|
ClientVersion: hello.ClientVersion,
|
||
|
}
|
||
|
return res, nil
|
||
|
|
||
|
case 0x00010001, 0x00010000:
|
||
|
// This is the first word of a v0.12 cluster config message.
|
||
|
// (Version 0, message ID 1, message type 0, compression enabled or disabled)
|
||
|
return HelloResult{}, ErrTooOldVersion12
|
||
|
}
|
||
|
|
||
|
return HelloResult{}, ErrUnknownMagic
|
||
|
}
|
||
|
|
||
|
func writeHello(c io.Writer, h HelloMessage) error {
|
||
|
msg, err := h.Marshal()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
header := make([]byte, 8)
|
||
|
binary.BigEndian.PutUint32(header[:4], h.Magic())
|
||
|
binary.BigEndian.PutUint32(header[4:], uint32(len(msg)))
|
||
|
|
||
|
_, err = c.Write(append(header, msg...))
|
||
|
return err
|
||
|
}
|