mirror of
https://github.com/octoleo/syncthing.git
synced 2025-01-22 22:58:25 +00:00
protocol: Add "Hello" message at connection start, also for unauthed peers
This commit is contained in:
parent
1d17891286
commit
b4f9a55e6e
@ -130,14 +130,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<p>
|
<p>
|
||||||
<span translate translate-value-device="{{ device }}" translate-value-address="{{ event.data.address }}">
|
<span translate translate-value-device="{{ device }}" translate-value-address="{{ event.data.address }}" translate-value-name="{{ event.data.name }}">
|
||||||
Device {%device%} ({%address%}) wants to connect. Add new device?
|
Device "{%name%}" ({%device%} at {%address%}) wants to connect. Add new device?
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-footer clearfix">
|
<div class="panel-footer clearfix">
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<button type="button" class="btn btn-sm btn-success" ng-click="addDevice(device)">
|
<button type="button" class="btn btn-sm btn-success" ng-click="addDevice(device, event.data.name)">
|
||||||
<span class="fa fa-plus"></span> <span translate>Add Device</span>
|
<span class="fa fa-plus"></span> <span translate>Add Device</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-sm btn-danger" ng-click="ignoreRejectedDevice(device)">
|
<button type="button" class="btn btn-sm btn-danger" ng-click="ignoreRejectedDevice(device)">
|
||||||
|
@ -977,13 +977,14 @@ angular.module('syncthing.core')
|
|||||||
$('#idqr').modal('show');
|
$('#idqr').modal('show');
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.addDevice = function (deviceID) {
|
$scope.addDevice = function (deviceID, name) {
|
||||||
return $http.get(urlbase + '/system/discovery')
|
return $http.get(urlbase + '/system/discovery')
|
||||||
.success(function (registry) {
|
.success(function (registry) {
|
||||||
$scope.discovery = registry;
|
$scope.discovery = registry;
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
$scope.currentDevice = {
|
$scope.currentDevice = {
|
||||||
|
name: name,
|
||||||
deviceID: deviceID,
|
deviceID: deviceID,
|
||||||
_addressesStr: 'dynamic',
|
_addressesStr: 'dynamic',
|
||||||
compression: 'metadata',
|
compression: 'metadata',
|
||||||
|
File diff suppressed because one or more lines are too long
@ -8,6 +8,7 @@ package connections
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
@ -18,7 +19,6 @@ import (
|
|||||||
"github.com/juju/ratelimit"
|
"github.com/juju/ratelimit"
|
||||||
"github.com/syncthing/syncthing/lib/config"
|
"github.com/syncthing/syncthing/lib/config"
|
||||||
"github.com/syncthing/syncthing/lib/discover"
|
"github.com/syncthing/syncthing/lib/discover"
|
||||||
"github.com/syncthing/syncthing/lib/events"
|
|
||||||
"github.com/syncthing/syncthing/lib/model"
|
"github.com/syncthing/syncthing/lib/model"
|
||||||
"github.com/syncthing/syncthing/lib/protocol"
|
"github.com/syncthing/syncthing/lib/protocol"
|
||||||
"github.com/syncthing/syncthing/lib/relay"
|
"github.com/syncthing/syncthing/lib/relay"
|
||||||
@ -39,9 +39,11 @@ var (
|
|||||||
|
|
||||||
type Model interface {
|
type Model interface {
|
||||||
protocol.Model
|
protocol.Model
|
||||||
AddConnection(conn model.Connection)
|
AddConnection(conn model.Connection, hello protocol.HelloMessage)
|
||||||
ConnectedTo(remoteID protocol.DeviceID) bool
|
ConnectedTo(remoteID protocol.DeviceID) bool
|
||||||
IsPaused(remoteID protocol.DeviceID) bool
|
IsPaused(remoteID protocol.DeviceID) bool
|
||||||
|
OnHello(protocol.DeviceID, net.Addr, protocol.HelloMessage)
|
||||||
|
GetHello(protocol.DeviceID) protocol.HelloMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
// Service listens on TLS and dials configured unconnected devices. Successful
|
// Service listens on TLS and dials configured unconnected devices. Successful
|
||||||
@ -144,14 +146,14 @@ func NewConnectionService(cfg *config.Wrapper, myID protocol.DeviceID, mdl Model
|
|||||||
func (s *Service) handle() {
|
func (s *Service) handle() {
|
||||||
next:
|
next:
|
||||||
for c := range s.conns {
|
for c := range s.conns {
|
||||||
cs := c.Conn.ConnectionState()
|
cs := c.ConnectionState()
|
||||||
|
|
||||||
// We should have negotiated the next level protocol "bep/1.0" as part
|
// We should have negotiated the next level protocol "bep/1.0" as part
|
||||||
// of the TLS handshake. Unfortunately this can't be a hard error,
|
// of the TLS handshake. Unfortunately this can't be a hard error,
|
||||||
// because there are implementations out there that don't support
|
// because there are implementations out there that don't support
|
||||||
// protocol negotiation (iOS for one...).
|
// protocol negotiation (iOS for one...).
|
||||||
if !cs.NegotiatedProtocolIsMutual || cs.NegotiatedProtocol != s.bepProtocolName {
|
if !cs.NegotiatedProtocolIsMutual || cs.NegotiatedProtocol != s.bepProtocolName {
|
||||||
l.Infof("Peer %s did not negotiate bep/1.0", c.Conn.RemoteAddr())
|
l.Infof("Peer %s did not negotiate bep/1.0", c.RemoteAddr())
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should have received exactly one certificate from the other
|
// We should have received exactly one certificate from the other
|
||||||
@ -159,8 +161,8 @@ next:
|
|||||||
// connection.
|
// connection.
|
||||||
certs := cs.PeerCertificates
|
certs := cs.PeerCertificates
|
||||||
if cl := len(certs); cl != 1 {
|
if cl := len(certs); cl != 1 {
|
||||||
l.Infof("Got peer certificate list of length %d != 1 from %s; protocol error", cl, c.Conn.RemoteAddr())
|
l.Infof("Got peer certificate list of length %d != 1 from %s; protocol error", cl, c.RemoteAddr())
|
||||||
c.Conn.Close()
|
c.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
remoteCert := certs[0]
|
remoteCert := certs[0]
|
||||||
@ -171,10 +173,19 @@ next:
|
|||||||
// clients between the same NAT gateway, and global discovery.
|
// clients between the same NAT gateway, and global discovery.
|
||||||
if remoteID == s.myID {
|
if remoteID == s.myID {
|
||||||
l.Infof("Connected to myself (%s) - should not happen", remoteID)
|
l.Infof("Connected to myself (%s) - should not happen", remoteID)
|
||||||
c.Conn.Close()
|
c.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hello, err := exchangeHello(c, s.model.GetHello(remoteID))
|
||||||
|
if err != nil {
|
||||||
|
l.Infof("Failed to exchange Hello messages with %s (%s): %s", remoteID, c.RemoteAddr(), err)
|
||||||
|
c.Close()
|
||||||
|
continue next
|
||||||
|
}
|
||||||
|
|
||||||
|
s.model.OnHello(remoteID, c.RemoteAddr(), hello)
|
||||||
|
|
||||||
// If we have a relay connection, and the new incoming connection is
|
// If we have a relay connection, and the new incoming connection is
|
||||||
// not a relay connection, we should drop that, and prefer the this one.
|
// not a relay connection, we should drop that, and prefer the this one.
|
||||||
s.mut.RLock()
|
s.mut.RLock()
|
||||||
@ -191,11 +202,11 @@ next:
|
|||||||
// in parallel we don't want to do that or we end up with no
|
// in parallel we don't want to do that or we end up with no
|
||||||
// connections still established...
|
// connections still established...
|
||||||
l.Infof("Connected to already connected device (%s)", remoteID)
|
l.Infof("Connected to already connected device (%s)", remoteID)
|
||||||
c.Conn.Close()
|
c.Close()
|
||||||
continue
|
continue
|
||||||
} else if s.model.IsPaused(remoteID) {
|
} else if s.model.IsPaused(remoteID) {
|
||||||
l.Infof("Connection from paused device (%s)", remoteID)
|
l.Infof("Connection from paused device (%s)", remoteID)
|
||||||
c.Conn.Close()
|
c.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,15 +224,15 @@ next:
|
|||||||
// Incorrect certificate name is something the user most
|
// Incorrect certificate name is something the user most
|
||||||
// likely wants to know about, since it's an advanced
|
// likely wants to know about, since it's an advanced
|
||||||
// config. Warn instead of Info.
|
// config. Warn instead of Info.
|
||||||
l.Warnf("Bad certificate from %s (%v): %v", remoteID, c.Conn.RemoteAddr(), err)
|
l.Warnf("Bad certificate from %s (%v): %v", remoteID, c.RemoteAddr(), err)
|
||||||
c.Conn.Close()
|
c.Close()
|
||||||
continue next
|
continue next
|
||||||
}
|
}
|
||||||
|
|
||||||
// If rate limiting is set, and based on the address we should
|
// If rate limiting is set, and based on the address we should
|
||||||
// limit the connection, then we wrap it in a limiter.
|
// limit the connection, then we wrap it in a limiter.
|
||||||
|
|
||||||
limit := s.shouldLimit(c.Conn.RemoteAddr())
|
limit := s.shouldLimit(c.RemoteAddr())
|
||||||
|
|
||||||
wr := io.Writer(c.Conn)
|
wr := io.Writer(c.Conn)
|
||||||
if limit && s.writeRateLimit != nil {
|
if limit && s.writeRateLimit != nil {
|
||||||
@ -233,17 +244,17 @@ next:
|
|||||||
rd = NewReadLimiter(c.Conn, s.readRateLimit)
|
rd = NewReadLimiter(c.Conn, s.readRateLimit)
|
||||||
}
|
}
|
||||||
|
|
||||||
name := fmt.Sprintf("%s-%s (%s)", c.Conn.LocalAddr(), c.Conn.RemoteAddr(), c.Type)
|
name := fmt.Sprintf("%s-%s (%s)", c.LocalAddr(), c.RemoteAddr(), c.Type)
|
||||||
protoConn := protocol.NewConnection(remoteID, rd, wr, s.model, name, deviceCfg.Compression)
|
protoConn := protocol.NewConnection(remoteID, rd, wr, s.model, name, deviceCfg.Compression)
|
||||||
|
|
||||||
l.Infof("Established secure connection to %s at %s", remoteID, name)
|
l.Infof("Established secure connection to %s at %s", remoteID, name)
|
||||||
l.Debugf("cipher suite: %04X in lan: %t", c.Conn.ConnectionState().CipherSuite, !limit)
|
l.Debugf("cipher suite: %04X in lan: %t", c.ConnectionState().CipherSuite, !limit)
|
||||||
|
|
||||||
s.model.AddConnection(model.Connection{
|
s.model.AddConnection(model.Connection{
|
||||||
c.Conn,
|
c,
|
||||||
protoConn,
|
protoConn,
|
||||||
c.Type,
|
c.Type,
|
||||||
})
|
}, hello)
|
||||||
s.mut.Lock()
|
s.mut.Lock()
|
||||||
s.connType[remoteID] = c.Type
|
s.connType[remoteID] = c.Type
|
||||||
s.mut.Unlock()
|
s.mut.Unlock()
|
||||||
@ -251,15 +262,8 @@ next:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !s.cfg.IgnoredDevice(remoteID) {
|
l.Infof("Connection from %s (%s) with ignored device ID %s", c.RemoteAddr(), c.Type, remoteID)
|
||||||
events.Default.Log(events.DeviceRejected, map[string]string{
|
c.Close()
|
||||||
"device": remoteID.String(),
|
|
||||||
"address": c.Conn.RemoteAddr().String(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Infof("Connection from %s (%s) with ignored device ID %s", c.Conn.RemoteAddr(), c.Type, remoteID)
|
|
||||||
c.Conn.Close()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,6 +572,54 @@ func isPublicIPv6(ip net.IP) bool {
|
|||||||
return ip.IsGlobalUnicast()
|
return ip.IsGlobalUnicast()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func exchangeHello(c net.Conn, h protocol.HelloMessage) (protocol.HelloMessage, error) {
|
||||||
|
if err := c.SetDeadline(time.Now().Add(2 * time.Second)); err != nil {
|
||||||
|
return protocol.HelloMessage{}, err
|
||||||
|
}
|
||||||
|
defer c.SetDeadline(time.Time{})
|
||||||
|
|
||||||
|
header := make([]byte, 8)
|
||||||
|
msg := h.MustMarshalXDR()
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint32(header[:4], protocol.HelloMessageMagic)
|
||||||
|
binary.BigEndian.PutUint32(header[4:], uint32(len(msg)))
|
||||||
|
|
||||||
|
if _, err := c.Write(header); err != nil {
|
||||||
|
return protocol.HelloMessage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := c.Write(msg); err != nil {
|
||||||
|
return protocol.HelloMessage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := io.ReadFull(c, header); err != nil {
|
||||||
|
return protocol.HelloMessage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if binary.BigEndian.Uint32(header[:4]) != protocol.HelloMessageMagic {
|
||||||
|
return protocol.HelloMessage{}, fmt.Errorf("incorrect magic")
|
||||||
|
}
|
||||||
|
|
||||||
|
msgSize := binary.BigEndian.Uint32(header[4:])
|
||||||
|
if msgSize > 1024 {
|
||||||
|
return protocol.HelloMessage{}, fmt.Errorf("hello message too big")
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, msgSize)
|
||||||
|
|
||||||
|
var hello protocol.HelloMessage
|
||||||
|
|
||||||
|
if _, err := io.ReadFull(c, buf); err != nil {
|
||||||
|
return protocol.HelloMessage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := hello.UnmarshalXDR(buf); err != nil {
|
||||||
|
return protocol.HelloMessage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return hello, nil
|
||||||
|
}
|
||||||
|
|
||||||
// serviceFunc wraps a function to create a suture.Service without stop
|
// serviceFunc wraps a function to create a suture.Service without stop
|
||||||
// functionality.
|
// functionality.
|
||||||
type serviceFunc func()
|
type serviceFunc func()
|
||||||
|
@ -87,10 +87,10 @@ type Model struct {
|
|||||||
folderStatRefs map[string]*stats.FolderStatisticsReference // folder -> statsRef
|
folderStatRefs map[string]*stats.FolderStatisticsReference // folder -> statsRef
|
||||||
fmut sync.RWMutex // protects the above
|
fmut sync.RWMutex // protects the above
|
||||||
|
|
||||||
conn map[protocol.DeviceID]Connection
|
conn map[protocol.DeviceID]Connection
|
||||||
deviceVer map[protocol.DeviceID]string
|
helloMessages map[protocol.DeviceID]protocol.HelloMessage
|
||||||
devicePaused map[protocol.DeviceID]bool
|
devicePaused map[protocol.DeviceID]bool
|
||||||
pmut sync.RWMutex // protects the above
|
pmut sync.RWMutex // protects the above
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -128,7 +128,7 @@ func NewModel(cfg *config.Wrapper, id protocol.DeviceID, deviceName, clientName,
|
|||||||
folderRunnerTokens: make(map[string][]suture.ServiceToken),
|
folderRunnerTokens: make(map[string][]suture.ServiceToken),
|
||||||
folderStatRefs: make(map[string]*stats.FolderStatisticsReference),
|
folderStatRefs: make(map[string]*stats.FolderStatisticsReference),
|
||||||
conn: make(map[protocol.DeviceID]Connection),
|
conn: make(map[protocol.DeviceID]Connection),
|
||||||
deviceVer: make(map[protocol.DeviceID]string),
|
helloMessages: make(map[protocol.DeviceID]protocol.HelloMessage),
|
||||||
devicePaused: make(map[protocol.DeviceID]bool),
|
devicePaused: make(map[protocol.DeviceID]bool),
|
||||||
|
|
||||||
fmut: sync.NewRWMutex(),
|
fmut: sync.NewRWMutex(),
|
||||||
@ -315,8 +315,13 @@ func (m *Model) ConnectionStats() map[string]interface{} {
|
|||||||
devs := m.cfg.Devices()
|
devs := m.cfg.Devices()
|
||||||
conns := make(map[string]ConnectionInfo, len(devs))
|
conns := make(map[string]ConnectionInfo, len(devs))
|
||||||
for device := range devs {
|
for device := range devs {
|
||||||
|
hello := m.helloMessages[device]
|
||||||
|
versionString := hello.ClientVersion
|
||||||
|
if hello.ClientName != "syncthing" {
|
||||||
|
versionString = hello.ClientName + " " + hello.ClientVersion
|
||||||
|
}
|
||||||
ci := ConnectionInfo{
|
ci := ConnectionInfo{
|
||||||
ClientVersion: m.deviceVer[device],
|
ClientVersion: versionString,
|
||||||
Paused: m.devicePaused[device],
|
Paused: m.devicePaused[device],
|
||||||
}
|
}
|
||||||
if conn, ok := m.conn[device]; ok {
|
if conn, ok := m.conn[device]; ok {
|
||||||
@ -606,30 +611,6 @@ func (m *Model) folderSharedWithUnlocked(folder string, deviceID protocol.Device
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterConfigMessage) {
|
func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterConfigMessage) {
|
||||||
m.pmut.Lock()
|
|
||||||
if cm.ClientName == "syncthing" {
|
|
||||||
m.deviceVer[deviceID] = cm.ClientVersion
|
|
||||||
} else {
|
|
||||||
m.deviceVer[deviceID] = cm.ClientName + " " + cm.ClientVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
event := map[string]string{
|
|
||||||
"id": deviceID.String(),
|
|
||||||
"deviceName": cm.DeviceName,
|
|
||||||
"clientName": cm.ClientName,
|
|
||||||
"clientVersion": cm.ClientVersion,
|
|
||||||
}
|
|
||||||
|
|
||||||
if conn, ok := m.conn[deviceID]; ok {
|
|
||||||
event["type"] = conn.Type.String()
|
|
||||||
addr := conn.RemoteAddr()
|
|
||||||
if addr != nil {
|
|
||||||
event["addr"] = addr.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m.pmut.Unlock()
|
|
||||||
|
|
||||||
// Check the peer device's announced folders against our own. Emits events
|
// Check the peer device's announced folders against our own. Emits events
|
||||||
// for folders that we don't expect (unknown or not shared).
|
// for folders that we don't expect (unknown or not shared).
|
||||||
|
|
||||||
@ -661,19 +642,8 @@ nextFolder:
|
|||||||
}
|
}
|
||||||
m.fmut.Unlock()
|
m.fmut.Unlock()
|
||||||
|
|
||||||
events.Default.Log(events.DeviceConnected, event)
|
|
||||||
|
|
||||||
l.Infof(`Device %s client is "%s %s" named "%s"`, deviceID, cm.ClientName, cm.ClientVersion, cm.DeviceName)
|
|
||||||
|
|
||||||
var changed bool
|
var changed bool
|
||||||
|
|
||||||
device, ok := m.cfg.Devices()[deviceID]
|
|
||||||
if ok && device.Name == "" {
|
|
||||||
device.Name = cm.DeviceName
|
|
||||||
m.cfg.SetDevice(device)
|
|
||||||
changed = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.cfg.Devices()[deviceID].Introducer {
|
if m.cfg.Devices()[deviceID].Introducer {
|
||||||
// This device is an introducer. Go through the announced lists of folders
|
// This device is an introducer. Go through the announced lists of folders
|
||||||
// and devices and add what we are missing.
|
// and devices and add what we are missing.
|
||||||
@ -773,7 +743,7 @@ func (m *Model) Close(device protocol.DeviceID, err error) {
|
|||||||
closeRawConn(conn)
|
closeRawConn(conn)
|
||||||
}
|
}
|
||||||
delete(m.conn, device)
|
delete(m.conn, device)
|
||||||
delete(m.deviceVer, device)
|
delete(m.helloMessages, device)
|
||||||
m.pmut.Unlock()
|
m.pmut.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -978,10 +948,41 @@ func (m *Model) SetIgnores(folder string, content []string) error {
|
|||||||
return m.ScanFolder(folder)
|
return m.ScanFolder(folder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnHello is called when an device connects to us.
|
||||||
|
// This allows us to extract some information from the Hello message
|
||||||
|
// and add it to a list of known devices ahead of any checks.
|
||||||
|
func (m *Model) OnHello(remoteID protocol.DeviceID, addr net.Addr, hello protocol.HelloMessage) {
|
||||||
|
for deviceID := range m.cfg.Devices() {
|
||||||
|
if deviceID == remoteID {
|
||||||
|
// Existing device, we will get the hello message in AddConnection
|
||||||
|
// hence do not persist any state here, as the connection might
|
||||||
|
// get killed before AddConnection
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !m.cfg.IgnoredDevice(remoteID) {
|
||||||
|
events.Default.Log(events.DeviceRejected, map[string]string{
|
||||||
|
"name": hello.DeviceName,
|
||||||
|
"device": remoteID.String(),
|
||||||
|
"address": addr.String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHello is called when we are about to connect to some remote device.
|
||||||
|
func (m *Model) GetHello(protocol.DeviceID) protocol.HelloMessage {
|
||||||
|
return protocol.HelloMessage{
|
||||||
|
DeviceName: m.deviceName,
|
||||||
|
ClientName: m.clientName,
|
||||||
|
ClientVersion: m.clientVersion,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// AddConnection adds a new peer connection to the model. An initial index will
|
// AddConnection adds a new peer connection to the model. An initial index will
|
||||||
// be sent to the connected peer, thereafter index updates whenever the local
|
// be sent to the connected peer, thereafter index updates whenever the local
|
||||||
// folder changes.
|
// folder changes.
|
||||||
func (m *Model) AddConnection(conn Connection) {
|
func (m *Model) AddConnection(conn Connection, hello protocol.HelloMessage) {
|
||||||
deviceID := conn.ID()
|
deviceID := conn.ID()
|
||||||
|
|
||||||
m.pmut.Lock()
|
m.pmut.Lock()
|
||||||
@ -990,6 +991,32 @@ func (m *Model) AddConnection(conn Connection) {
|
|||||||
}
|
}
|
||||||
m.conn[deviceID] = conn
|
m.conn[deviceID] = conn
|
||||||
|
|
||||||
|
m.helloMessages[deviceID] = hello
|
||||||
|
|
||||||
|
event := map[string]string{
|
||||||
|
"id": deviceID.String(),
|
||||||
|
"deviceName": hello.DeviceName,
|
||||||
|
"clientName": hello.ClientName,
|
||||||
|
"clientVersion": hello.ClientVersion,
|
||||||
|
"type": conn.Type.String(),
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := conn.RemoteAddr()
|
||||||
|
if addr != nil {
|
||||||
|
event["addr"] = addr.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
events.Default.Log(events.DeviceConnected, event)
|
||||||
|
|
||||||
|
l.Infof(`Device %s client is "%s %s" named "%s"`, deviceID, hello.ClientName, hello.ClientVersion, hello.DeviceName)
|
||||||
|
|
||||||
|
device, ok := m.cfg.Devices()[deviceID]
|
||||||
|
if ok && device.Name == "" {
|
||||||
|
device.Name = hello.DeviceName
|
||||||
|
m.cfg.SetDevice(device)
|
||||||
|
m.cfg.Save()
|
||||||
|
}
|
||||||
|
|
||||||
conn.Start()
|
conn.Start()
|
||||||
|
|
||||||
cm := m.generateClusterConfig(deviceID)
|
cm := m.generateClusterConfig(deviceID)
|
||||||
@ -1510,11 +1537,7 @@ func (m *Model) numHashers(folder string) int {
|
|||||||
// generateClusterConfig returns a ClusterConfigMessage that is correct for
|
// generateClusterConfig returns a ClusterConfigMessage that is correct for
|
||||||
// the given peer device
|
// the given peer device
|
||||||
func (m *Model) generateClusterConfig(device protocol.DeviceID) protocol.ClusterConfigMessage {
|
func (m *Model) generateClusterConfig(device protocol.DeviceID) protocol.ClusterConfigMessage {
|
||||||
message := protocol.ClusterConfigMessage{
|
var message protocol.ClusterConfigMessage
|
||||||
DeviceName: m.deviceName,
|
|
||||||
ClientName: m.clientName,
|
|
||||||
ClientVersion: m.clientVersion,
|
|
||||||
}
|
|
||||||
|
|
||||||
m.fmut.RLock()
|
m.fmut.RLock()
|
||||||
for _, folder := range m.deviceFolders[device] {
|
for _, folder := range m.deviceFolders[device] {
|
||||||
|
@ -279,7 +279,7 @@ func BenchmarkRequest(b *testing.B) {
|
|||||||
&net.TCPConn{},
|
&net.TCPConn{},
|
||||||
fc,
|
fc,
|
||||||
ConnectionTypeDirectAccept,
|
ConnectionTypeDirectAccept,
|
||||||
})
|
}, protocol.HelloMessage{})
|
||||||
m.Index(device1, "default", files, 0, nil)
|
m.Index(device1, "default", files, 0, nil)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
@ -295,11 +295,10 @@ func BenchmarkRequest(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDeviceRename(t *testing.T) {
|
func TestDeviceRename(t *testing.T) {
|
||||||
ccm := protocol.ClusterConfigMessage{
|
hello := protocol.HelloMessage{
|
||||||
ClientName: "syncthing",
|
ClientName: "syncthing",
|
||||||
ClientVersion: "v0.9.4",
|
ClientVersion: "v0.9.4",
|
||||||
}
|
}
|
||||||
|
|
||||||
defer os.Remove("tmpconfig.xml")
|
defer os.Remove("tmpconfig.xml")
|
||||||
|
|
||||||
rawCfg := config.New(device1)
|
rawCfg := config.New(device1)
|
||||||
@ -313,35 +312,39 @@ func TestDeviceRename(t *testing.T) {
|
|||||||
db := db.OpenMemory()
|
db := db.OpenMemory()
|
||||||
m := NewModel(cfg, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil)
|
m := NewModel(cfg, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil)
|
||||||
|
|
||||||
fc := FakeConnection{
|
if cfg.Devices()[device1].Name != "" {
|
||||||
id: device1,
|
t.Errorf("Device already has a name")
|
||||||
requestData: []byte("some data to return"),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m.AddConnection(Connection{
|
conn := Connection{
|
||||||
&net.TCPConn{},
|
&net.TCPConn{},
|
||||||
fc,
|
FakeConnection{
|
||||||
|
id: device1,
|
||||||
|
requestData: []byte("some data to return"),
|
||||||
|
},
|
||||||
ConnectionTypeDirectAccept,
|
ConnectionTypeDirectAccept,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
m.AddConnection(conn, hello)
|
||||||
|
|
||||||
m.ServeBackground()
|
m.ServeBackground()
|
||||||
|
|
||||||
if cfg.Devices()[device1].Name != "" {
|
if cfg.Devices()[device1].Name != "" {
|
||||||
t.Errorf("Device already has a name")
|
t.Errorf("Device already has a name")
|
||||||
}
|
}
|
||||||
|
|
||||||
m.ClusterConfig(device1, ccm)
|
m.Close(device1, protocol.ErrTimeout)
|
||||||
if cfg.Devices()[device1].Name != "" {
|
hello.DeviceName = "tester"
|
||||||
t.Errorf("Device already has a name")
|
m.AddConnection(conn, hello)
|
||||||
}
|
|
||||||
|
|
||||||
ccm.DeviceName = "tester"
|
|
||||||
m.ClusterConfig(device1, ccm)
|
|
||||||
if cfg.Devices()[device1].Name != "tester" {
|
if cfg.Devices()[device1].Name != "tester" {
|
||||||
t.Errorf("Device did not get a name")
|
t.Errorf("Device did not get a name")
|
||||||
}
|
}
|
||||||
|
|
||||||
ccm.DeviceName = "tester2"
|
m.Close(device1, protocol.ErrTimeout)
|
||||||
m.ClusterConfig(device1, ccm)
|
hello.DeviceName = "tester2"
|
||||||
|
m.AddConnection(conn, hello)
|
||||||
|
|
||||||
if cfg.Devices()[device1].Name != "tester" {
|
if cfg.Devices()[device1].Name != "tester" {
|
||||||
t.Errorf("Device name got overwritten")
|
t.Errorf("Device name got overwritten")
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,16 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
var sha256OfEmptyBlock = sha256.Sum256(make([]byte, BlockSize))
|
var (
|
||||||
|
sha256OfEmptyBlock = sha256.Sum256(make([]byte, BlockSize))
|
||||||
|
HelloMessageMagic uint32 = 0x9F79BC40
|
||||||
|
)
|
||||||
|
|
||||||
|
type HelloMessage struct {
|
||||||
|
DeviceName string // max:64
|
||||||
|
ClientName string // max:64
|
||||||
|
ClientVersion string // max:64
|
||||||
|
}
|
||||||
|
|
||||||
type IndexMessage struct {
|
type IndexMessage struct {
|
||||||
Folder string // max:256
|
Folder string // max:256
|
||||||
@ -125,11 +134,8 @@ type ResponseMessage struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ClusterConfigMessage struct {
|
type ClusterConfigMessage struct {
|
||||||
DeviceName string // max:64
|
Folders []Folder // max:1000000
|
||||||
ClientName string // max:64
|
Options []Option // max:64
|
||||||
ClientVersion string // max:64
|
|
||||||
Folders []Folder // max:1000000
|
|
||||||
Options []Option // max:64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *ClusterConfigMessage) GetOption(key string) string {
|
func (o *ClusterConfigMessage) GetOption(key string) string {
|
||||||
|
@ -10,6 +10,82 @@ import (
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
|
HelloMessage Structure:
|
||||||
|
|
||||||
|
0 1 2 3
|
||||||
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
/ /
|
||||||
|
\ Device Name (length + padded data) \
|
||||||
|
/ /
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
/ /
|
||||||
|
\ Client Name (length + padded data) \
|
||||||
|
/ /
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
/ /
|
||||||
|
\ Client Version (length + padded data) \
|
||||||
|
/ /
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
|
|
||||||
|
struct HelloMessage {
|
||||||
|
string DeviceName<64>;
|
||||||
|
string ClientName<64>;
|
||||||
|
string ClientVersion<64>;
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (o HelloMessage) XDRSize() int {
|
||||||
|
return 4 + len(o.DeviceName) + xdr.Padding(len(o.DeviceName)) +
|
||||||
|
4 + len(o.ClientName) + xdr.Padding(len(o.ClientName)) +
|
||||||
|
4 + len(o.ClientVersion) + xdr.Padding(len(o.ClientVersion))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o HelloMessage) MarshalXDR() ([]byte, error) {
|
||||||
|
buf := make([]byte, o.XDRSize())
|
||||||
|
m := &xdr.Marshaller{Data: buf}
|
||||||
|
return buf, o.MarshalXDRInto(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o HelloMessage) MustMarshalXDR() []byte {
|
||||||
|
bs, err := o.MarshalXDR()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return bs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o HelloMessage) MarshalXDRInto(m *xdr.Marshaller) error {
|
||||||
|
if l := len(o.DeviceName); l > 64 {
|
||||||
|
return xdr.ElementSizeExceeded("DeviceName", l, 64)
|
||||||
|
}
|
||||||
|
m.MarshalString(o.DeviceName)
|
||||||
|
if l := len(o.ClientName); l > 64 {
|
||||||
|
return xdr.ElementSizeExceeded("ClientName", l, 64)
|
||||||
|
}
|
||||||
|
m.MarshalString(o.ClientName)
|
||||||
|
if l := len(o.ClientVersion); l > 64 {
|
||||||
|
return xdr.ElementSizeExceeded("ClientVersion", l, 64)
|
||||||
|
}
|
||||||
|
m.MarshalString(o.ClientVersion)
|
||||||
|
return m.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *HelloMessage) UnmarshalXDR(bs []byte) error {
|
||||||
|
u := &xdr.Unmarshaller{Data: bs}
|
||||||
|
return o.UnmarshalXDRFrom(u)
|
||||||
|
}
|
||||||
|
func (o *HelloMessage) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
|
||||||
|
o.DeviceName = u.UnmarshalStringMax(64)
|
||||||
|
o.ClientName = u.UnmarshalStringMax(64)
|
||||||
|
o.ClientVersion = u.UnmarshalStringMax(64)
|
||||||
|
return u.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
IndexMessage Structure:
|
IndexMessage Structure:
|
||||||
|
|
||||||
0 1 2 3
|
0 1 2 3
|
||||||
@ -506,18 +582,6 @@ ClusterConfigMessage Structure:
|
|||||||
0 1 2 3
|
0 1 2 3
|
||||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
/ /
|
|
||||||
\ Device Name (length + padded data) \
|
|
||||||
/ /
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
/ /
|
|
||||||
\ Client Name (length + padded data) \
|
|
||||||
/ /
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
/ /
|
|
||||||
\ Client Version (length + padded data) \
|
|
||||||
/ /
|
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
||||||
| Number of Folders |
|
| Number of Folders |
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
/ /
|
/ /
|
||||||
@ -533,9 +597,6 @@ ClusterConfigMessage Structure:
|
|||||||
|
|
||||||
|
|
||||||
struct ClusterConfigMessage {
|
struct ClusterConfigMessage {
|
||||||
string DeviceName<64>;
|
|
||||||
string ClientName<64>;
|
|
||||||
string ClientVersion<64>;
|
|
||||||
Folder Folders<1000000>;
|
Folder Folders<1000000>;
|
||||||
Option Options<64>;
|
Option Options<64>;
|
||||||
}
|
}
|
||||||
@ -543,10 +604,7 @@ struct ClusterConfigMessage {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
func (o ClusterConfigMessage) XDRSize() int {
|
func (o ClusterConfigMessage) XDRSize() int {
|
||||||
return 4 + len(o.DeviceName) + xdr.Padding(len(o.DeviceName)) +
|
return 4 + xdr.SizeOfSlice(o.Folders) +
|
||||||
4 + len(o.ClientName) + xdr.Padding(len(o.ClientName)) +
|
|
||||||
4 + len(o.ClientVersion) + xdr.Padding(len(o.ClientVersion)) +
|
|
||||||
4 + xdr.SizeOfSlice(o.Folders) +
|
|
||||||
4 + xdr.SizeOfSlice(o.Options)
|
4 + xdr.SizeOfSlice(o.Options)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,18 +623,6 @@ func (o ClusterConfigMessage) MustMarshalXDR() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o ClusterConfigMessage) MarshalXDRInto(m *xdr.Marshaller) error {
|
func (o ClusterConfigMessage) MarshalXDRInto(m *xdr.Marshaller) error {
|
||||||
if l := len(o.DeviceName); l > 64 {
|
|
||||||
return xdr.ElementSizeExceeded("DeviceName", l, 64)
|
|
||||||
}
|
|
||||||
m.MarshalString(o.DeviceName)
|
|
||||||
if l := len(o.ClientName); l > 64 {
|
|
||||||
return xdr.ElementSizeExceeded("ClientName", l, 64)
|
|
||||||
}
|
|
||||||
m.MarshalString(o.ClientName)
|
|
||||||
if l := len(o.ClientVersion); l > 64 {
|
|
||||||
return xdr.ElementSizeExceeded("ClientVersion", l, 64)
|
|
||||||
}
|
|
||||||
m.MarshalString(o.ClientVersion)
|
|
||||||
if l := len(o.Folders); l > 1000000 {
|
if l := len(o.Folders); l > 1000000 {
|
||||||
return xdr.ElementSizeExceeded("Folders", l, 1000000)
|
return xdr.ElementSizeExceeded("Folders", l, 1000000)
|
||||||
}
|
}
|
||||||
@ -603,9 +649,6 @@ func (o *ClusterConfigMessage) UnmarshalXDR(bs []byte) error {
|
|||||||
return o.UnmarshalXDRFrom(u)
|
return o.UnmarshalXDRFrom(u)
|
||||||
}
|
}
|
||||||
func (o *ClusterConfigMessage) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
|
func (o *ClusterConfigMessage) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
|
||||||
o.DeviceName = u.UnmarshalStringMax(64)
|
|
||||||
o.ClientName = u.UnmarshalStringMax(64)
|
|
||||||
o.ClientVersion = u.UnmarshalStringMax(64)
|
|
||||||
_FoldersSize := int(u.UnmarshalUint32())
|
_FoldersSize := int(u.UnmarshalUint32())
|
||||||
if _FoldersSize < 0 {
|
if _FoldersSize < 0 {
|
||||||
return xdr.ElementSizeExceeded("Folders", _FoldersSize, 1000000)
|
return xdr.ElementSizeExceeded("Folders", _FoldersSize, 1000000)
|
||||||
|
@ -189,12 +189,12 @@ func TestClose(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestElementSizeExceededNested(t *testing.T) {
|
func TestElementSizeExceededNested(t *testing.T) {
|
||||||
m := ClusterConfigMessage{
|
m := HelloMessage{
|
||||||
ClientName: "longstringlongstringlongstringinglongstringlongstringlonlongstringlongstringlon",
|
ClientName: "longstringlongstringlongstringinglongstringlongstringlonlongstringlongstringlon",
|
||||||
}
|
}
|
||||||
_, err := m.MarshalXDR()
|
_, err := m.MarshalXDR()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("ID length %d > max 64, but no error", len(m.Folders[0].ID))
|
t.Errorf("ID length %d > max 64, but no error", len(m.ClientName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user