protocol: Add "Hello" message at connection start, also for unauthed peers

This commit is contained in:
Audrius Butkevicius 2016-03-25 20:29:07 +00:00
parent 1d17891286
commit b4f9a55e6e
9 changed files with 267 additions and 139 deletions

View File

@ -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>&nbsp;<span translate>Add Device</span> <span class="fa fa-plus"></span>&nbsp;<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)">

View File

@ -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

View File

@ -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()

View File

@ -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] {

View File

@ -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")
} }

View File

@ -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 {

View File

@ -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)

View File

@ -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))
} }
} }