Update xdr; handle marshalling errors

This commit is contained in:
Jakob Borg 2014-10-21 08:40:05 +02:00
parent 1e915a2903
commit f2adfde1a8
16 changed files with 781 additions and 170 deletions

4
Godeps/Godeps.json generated
View File

@ -1,6 +1,6 @@
{
"ImportPath": "github.com/syncthing/syncthing",
"GoVersion": "go1.3.1",
"GoVersion": "go1.3.3",
"Packages": [
"./cmd/..."
],
@ -44,7 +44,7 @@
},
{
"ImportPath": "github.com/calmh/xdr",
"Rev": "a597b63b87d6140f79084c8aab214b4d533833a1"
"Rev": "ec3d404f43731551258977b38dd72cf557d00398"
},
{
"ImportPath": "github.com/juju/ratelimit",

View File

@ -33,11 +33,15 @@ var s = XDRBenchStruct{
S0: "Hello World! String one.",
S1: "Hello World! String two.",
}
var e = s.MarshalXDR()
var e []byte
func init() {
e, _ = s.MarshalXDR()
}
func BenchmarkThisMarshal(b *testing.B) {
for i := 0; i < b.N; i++ {
res = s.MarshalXDR()
res, _ = s.MarshalXDR()
}
}

View File

@ -72,15 +72,23 @@ func (o XDRBenchStruct) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o XDRBenchStruct) MarshalXDR() []byte {
func (o XDRBenchStruct) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o XDRBenchStruct) AppendXDR(bs []byte) []byte {
func (o XDRBenchStruct) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o XDRBenchStruct) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o XDRBenchStruct) encodeXDR(xw *xdr.Writer) (int, error) {
@ -88,13 +96,13 @@ func (o XDRBenchStruct) encodeXDR(xw *xdr.Writer) (int, error) {
xw.WriteUint32(o.I2)
xw.WriteUint16(o.I3)
xw.WriteUint8(o.I4)
if len(o.Bs0) > 128 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.Bs0); l > 128 {
return xw.Tot(), xdr.ElementSizeExceeded("Bs0", l, 128)
}
xw.WriteBytes(o.Bs0)
xw.WriteBytes(o.Bs1)
if len(o.S0) > 128 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.S0); l > 128 {
return xw.Tot(), xdr.ElementSizeExceeded("S0", l, 128)
}
xw.WriteString(o.S0)
xw.WriteString(o.S1)
@ -150,15 +158,23 @@ func (o repeatReader) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o repeatReader) MarshalXDR() []byte {
func (o repeatReader) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o repeatReader) AppendXDR(bs []byte) []byte {
func (o repeatReader) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o repeatReader) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o repeatReader) encodeXDR(xw *xdr.Writer) (int, error) {

View File

@ -53,15 +53,23 @@ func (o {{.TypeName}}) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}//+n
func (o {{.TypeName}}) MarshalXDR() []byte {
func (o {{.TypeName}}) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}//+n
func (o {{.TypeName}}) AppendXDR(bs []byte) []byte {
func (o {{.TypeName}}) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}//+n
func (o {{.TypeName}}) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}//+n
func (o {{.TypeName}}) encodeXDR(xw *xdr.Writer) (int, error) {
@ -71,8 +79,8 @@ func (o {{.TypeName}}) encodeXDR(xw *xdr.Writer) (int, error) {
xw.Write{{$fieldInfo.Encoder}}({{$fieldInfo.Convert}}(o.{{$fieldInfo.Name}}))
{{else if $fieldInfo.IsBasic}}
{{if ge $fieldInfo.Max 1}}
if len(o.{{$fieldInfo.Name}}) > {{$fieldInfo.Max}} {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.{{$fieldInfo.Name}}); l > {{$fieldInfo.Max}} {
return xw.Tot(), xdr.ElementSizeExceeded("{{$fieldInfo.Name}}", l, {{$fieldInfo.Max}})
}
{{end}}
xw.Write{{$fieldInfo.Encoder}}(o.{{$fieldInfo.Name}})
@ -84,8 +92,8 @@ func (o {{.TypeName}}) encodeXDR(xw *xdr.Writer) (int, error) {
{{end}}
{{else}}
{{if ge $fieldInfo.Max 1}}
if len(o.{{$fieldInfo.Name}}) > {{$fieldInfo.Max}} {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.{{$fieldInfo.Name}}); l > {{$fieldInfo.Max}} {
return xw.Tot(), xdr.ElementSizeExceeded("{{$fieldInfo.Name}}", l, {{$fieldInfo.Max}})
}
{{end}}
xw.WriteUint32(uint32(len(o.{{$fieldInfo.Name}})))
@ -135,7 +143,7 @@ func (o *{{.TypeName}}) decodeXDR(xr *xdr.Reader) error {
_{{$fieldInfo.Name}}Size := int(xr.ReadUint32())
{{if ge $fieldInfo.Max 1}}
if _{{$fieldInfo.Name}}Size > {{$fieldInfo.Max}} {
return xdr.ErrElementSizeExceeded
return xdr.ElementSizeExceeded("{{$fieldInfo.Name}}", _{{$fieldInfo.Name}}Size, {{$fieldInfo.Max}})
}
{{end}}
o.{{$fieldInfo.Name}} = make([]{{$fieldInfo.FieldType}}, _{{$fieldInfo.Name}}Size)

View File

@ -24,9 +24,10 @@ type TestStruct struct {
UI32 uint32
I64 int64
UI64 uint64
BS []byte
S string
BS []byte // max:1024
S string // max:1024
C Opaque
SS []string // max:1024
}
type Opaque [32]byte
@ -49,9 +50,12 @@ func (Opaque) Generate(rand *rand.Rand, size int) reflect.Value {
func TestEncDec(t *testing.T) {
fn := func(t0 TestStruct) bool {
bs := t0.MarshalXDR()
bs, err := t0.MarshalXDR()
if err != nil {
t.Fatal(err)
}
var t1 TestStruct
err := t1.UnmarshalXDR(bs)
err = t1.UnmarshalXDR(bs)
if err != nil {
t.Fatal(err)
}

View File

@ -54,6 +54,14 @@ TestStruct Structure:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opaque |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Number of SS |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length of SS |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/ /
\ SS (variable length) \
/ /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
struct TestStruct {
@ -66,9 +74,10 @@ struct TestStruct {
unsigned int UI32;
hyper I64;
unsigned hyper UI64;
opaque BS<>;
string S<>;
opaque BS<1024>;
string S<1024>;
Opaque C;
string SS<1024>;
}
*/
@ -78,15 +87,23 @@ func (o TestStruct) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o TestStruct) MarshalXDR() []byte {
func (o TestStruct) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o TestStruct) AppendXDR(bs []byte) []byte {
func (o TestStruct) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o TestStruct) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o TestStruct) encodeXDR(xw *xdr.Writer) (int, error) {
@ -99,12 +116,25 @@ func (o TestStruct) encodeXDR(xw *xdr.Writer) (int, error) {
xw.WriteUint32(o.UI32)
xw.WriteUint64(uint64(o.I64))
xw.WriteUint64(o.UI64)
if l := len(o.BS); l > 1024 {
return xw.Tot(), xdr.ElementSizeExceeded("BS", l, 1024)
}
xw.WriteBytes(o.BS)
if l := len(o.S); l > 1024 {
return xw.Tot(), xdr.ElementSizeExceeded("S", l, 1024)
}
xw.WriteString(o.S)
_, err := o.C.encodeXDR(xw)
if err != nil {
return xw.Tot(), err
}
if l := len(o.SS); l > 1024 {
return xw.Tot(), xdr.ElementSizeExceeded("SS", l, 1024)
}
xw.WriteUint32(uint32(len(o.SS)))
for i := range o.SS {
xw.WriteString(o.SS[i])
}
return xw.Tot(), xw.Error()
}
@ -129,8 +159,16 @@ func (o *TestStruct) decodeXDR(xr *xdr.Reader) error {
o.UI32 = xr.ReadUint32()
o.I64 = int64(xr.ReadUint64())
o.UI64 = xr.ReadUint64()
o.BS = xr.ReadBytes()
o.S = xr.ReadString()
o.BS = xr.ReadBytesMax(1024)
o.S = xr.ReadStringMax(1024)
(&o.C).decodeXDR(xr)
_SSSize := int(xr.ReadUint32())
if _SSSize > 1024 {
return xdr.ElementSizeExceeded("SS", _SSSize, 1024)
}
o.SS = make([]string, _SSSize)
for i := range o.SS {
o.SS[i] = xr.ReadString()
}
return xr.Error()
}

View File

@ -5,14 +5,12 @@
package xdr
import (
"errors"
"fmt"
"io"
"reflect"
"unsafe"
)
var ErrElementSizeExceeded = errors.New("element size exceeded")
type Reader struct {
r io.Reader
err error
@ -71,7 +69,7 @@ func (r *Reader) ReadBytesMaxInto(max int, dst []byte) []byte {
return nil
}
if max > 0 && l > max {
r.err = ErrElementSizeExceeded
r.err = ElementSizeExceeded("bytes field", l, max)
return nil
}
@ -162,3 +160,7 @@ func (r *Reader) Error() error {
}
return XDRError{"read", r.err}
}
func ElementSizeExceeded(field string, size, limit int) error {
return fmt.Errorf("%s exceeds size limit; %d > %d", field, size, limit)
}

View File

@ -5,6 +5,7 @@ package xdr
import (
"bytes"
"strings"
"testing"
"testing/quick"
)
@ -60,7 +61,7 @@ func TestReadBytesMaxInto(t *testing.T) {
if read := len(bs); read != tot {
t.Errorf("Incorrect read bytes, wrote=%d, buf=%d, max=%d, read=%d", tot, tot+diff, max, read)
}
} else if r.err != ErrElementSizeExceeded {
} else if !strings.Contains(r.err.Error(), "exceeds size") {
t.Errorf("Unexpected non-ErrElementSizeExceeded error for wrote=%d, max=%d: %v", tot, max, r.err)
}
}
@ -84,7 +85,7 @@ func TestReadStringMax(t *testing.T) {
if read != tot {
t.Errorf("Incorrect read bytes, wrote=%d, max=%d, read=%d", tot, max, read)
}
} else if r.err != ErrElementSizeExceeded {
} else if !strings.Contains(r.err.Error(), "exceeds size") {
t.Errorf("Unexpected non-ErrElementSizeExceeded error for wrote=%d, max=%d, read=%d: %v", tot, max, read, r.err)
}
}

View File

@ -203,7 +203,7 @@ func (d *Discoverer) announcementPkt() []byte {
Magic: AnnouncementMagic,
This: Device{d.myID[:], addrs},
}
return pkt.MarshalXDR()
return pkt.MustMarshalXDR()
}
func (d *Discoverer) sendLocalAnnouncements() {
@ -213,7 +213,7 @@ func (d *Discoverer) sendLocalAnnouncements() {
Magic: AnnouncementMagic,
This: Device{d.myID[:], addrs},
}
msg := pkt.MarshalXDR()
msg := pkt.MustMarshalXDR()
for {
if d.multicastBeacon != nil {
@ -253,7 +253,7 @@ func (d *Discoverer) sendExternalAnnouncements() {
Magic: AnnouncementMagic,
This: Device{d.myID[:], []Address{{Port: d.extPort}}},
}
buf = pkt.MarshalXDR()
buf = pkt.MustMarshalXDR()
} else {
buf = d.announcementPkt()
}
@ -425,7 +425,7 @@ func (d *Discoverer) externalLookup(device protocol.DeviceID) []string {
return nil
}
buf := Query{QueryMagic, device[:]}.MarshalXDR()
buf := Query{QueryMagic, device[:]}.MustMarshalXDR()
_, err = conn.Write(buf)
if err != nil {
if debug {

View File

@ -40,21 +40,29 @@ func (o Query) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o Query) MarshalXDR() []byte {
func (o Query) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o Query) AppendXDR(bs []byte) []byte {
func (o Query) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o Query) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o Query) encodeXDR(xw *xdr.Writer) (int, error) {
xw.WriteUint32(o.Magic)
if len(o.DeviceID) > 32 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.DeviceID); l > 32 {
return xw.Tot(), xdr.ElementSizeExceeded("DeviceID", l, 32)
}
xw.WriteBytes(o.DeviceID)
return xw.Tot(), xw.Error()
@ -109,15 +117,23 @@ func (o Announce) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o Announce) MarshalXDR() []byte {
func (o Announce) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o Announce) AppendXDR(bs []byte) []byte {
func (o Announce) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o Announce) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o Announce) encodeXDR(xw *xdr.Writer) (int, error) {
@ -126,8 +142,8 @@ func (o Announce) encodeXDR(xw *xdr.Writer) (int, error) {
if err != nil {
return xw.Tot(), err
}
if len(o.Extra) > 16 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.Extra); l > 16 {
return xw.Tot(), xdr.ElementSizeExceeded("Extra", l, 16)
}
xw.WriteUint32(uint32(len(o.Extra)))
for i := range o.Extra {
@ -155,7 +171,7 @@ func (o *Announce) decodeXDR(xr *xdr.Reader) error {
(&o.This).decodeXDR(xr)
_ExtraSize := int(xr.ReadUint32())
if _ExtraSize > 16 {
return xdr.ErrElementSizeExceeded
return xdr.ElementSizeExceeded("Extra", _ExtraSize, 16)
}
o.Extra = make([]Device, _ExtraSize)
for i := range o.Extra {
@ -197,24 +213,32 @@ func (o Device) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o Device) MarshalXDR() []byte {
func (o Device) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o Device) AppendXDR(bs []byte) []byte {
func (o Device) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o Device) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o Device) encodeXDR(xw *xdr.Writer) (int, error) {
if len(o.ID) > 32 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.ID); l > 32 {
return xw.Tot(), xdr.ElementSizeExceeded("ID", l, 32)
}
xw.WriteBytes(o.ID)
if len(o.Addresses) > 16 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.Addresses); l > 16 {
return xw.Tot(), xdr.ElementSizeExceeded("Addresses", l, 16)
}
xw.WriteUint32(uint32(len(o.Addresses)))
for i := range o.Addresses {
@ -241,7 +265,7 @@ func (o *Device) decodeXDR(xr *xdr.Reader) error {
o.ID = xr.ReadBytesMax(32)
_AddressesSize := int(xr.ReadUint32())
if _AddressesSize > 16 {
return xdr.ErrElementSizeExceeded
return xdr.ElementSizeExceeded("Addresses", _AddressesSize, 16)
}
o.Addresses = make([]Address, _AddressesSize)
for i := range o.Addresses {
@ -279,20 +303,28 @@ func (o Address) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o Address) MarshalXDR() []byte {
func (o Address) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o Address) AppendXDR(bs []byte) []byte {
func (o Address) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o Address) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o Address) encodeXDR(xw *xdr.Writer) (int, error) {
if len(o.IP) > 16 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.IP); l > 16 {
return xw.Tot(), xdr.ElementSizeExceeded("IP", l, 16)
}
xw.WriteBytes(o.IP)
xw.WriteUint16(o.Port)

View File

@ -289,7 +289,8 @@ func ldbReplaceWithDelete(db *leveldb.DB, folder, device []byte, fs []protocol.F
Flags: tf.Flags | protocol.FlagDeleted,
Modified: tf.Modified,
}
batch.Put(dbi.Key(), f.MarshalXDR())
bs, _ := f.MarshalXDR()
batch.Put(dbi.Key(), bs)
ldbUpdateGlobal(db, batch, folder, device, deviceKeyName(dbi.Key()), f.Version)
return ts
}
@ -362,7 +363,7 @@ func ldbInsert(batch dbWriter, folder, device []byte, file protocol.FileInfo) ui
name := []byte(file.Name)
nk := deviceKey(folder, device, name)
batch.Put(nk, file.MarshalXDR())
batch.Put(nk, file.MustMarshalXDR())
return file.LocalVersion
}
@ -416,7 +417,7 @@ func ldbUpdateGlobal(db dbReader, batch dbWriter, folder, device, file []byte, v
fl.versions = append(fl.versions, nv)
done:
batch.Put(gk, fl.MarshalXDR())
batch.Put(gk, fl.MustMarshalXDR())
return true
}
@ -453,7 +454,7 @@ func ldbRemoveFromGlobal(db dbReader, batch dbWriter, folder, device, file []byt
if len(fl.versions) == 0 {
batch.Delete(gk)
} else {
batch.Put(gk, fl.MarshalXDR())
batch.Put(gk, fl.MustMarshalXDR())
}
}

View File

@ -42,15 +42,23 @@ func (o fileVersion) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o fileVersion) MarshalXDR() []byte {
func (o fileVersion) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o fileVersion) AppendXDR(bs []byte) []byte {
func (o fileVersion) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o fileVersion) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o fileVersion) encodeXDR(xw *xdr.Writer) (int, error) {
@ -102,15 +110,23 @@ func (o versionList) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o versionList) MarshalXDR() []byte {
func (o versionList) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o versionList) AppendXDR(bs []byte) []byte {
func (o versionList) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o versionList) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o versionList) encodeXDR(xw *xdr.Writer) (int, error) {

View File

@ -44,20 +44,28 @@ func (o IndexMessage) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o IndexMessage) MarshalXDR() []byte {
func (o IndexMessage) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o IndexMessage) AppendXDR(bs []byte) []byte {
func (o IndexMessage) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o IndexMessage) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o IndexMessage) encodeXDR(xw *xdr.Writer) (int, error) {
if len(o.Folder) > 64 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.Folder); l > 64 {
return xw.Tot(), xdr.ElementSizeExceeded("Folder", l, 64)
}
xw.WriteString(o.Folder)
xw.WriteUint32(uint32(len(o.Files)))
@ -142,20 +150,28 @@ func (o FileInfo) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o FileInfo) MarshalXDR() []byte {
func (o FileInfo) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o FileInfo) AppendXDR(bs []byte) []byte {
func (o FileInfo) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o FileInfo) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o FileInfo) encodeXDR(xw *xdr.Writer) (int, error) {
if len(o.Name) > 8192 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.Name); l > 8192 {
return xw.Tot(), xdr.ElementSizeExceeded("Name", l, 8192)
}
xw.WriteString(o.Name)
xw.WriteUint32(o.Flags)
@ -244,20 +260,28 @@ func (o FileInfoTruncated) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o FileInfoTruncated) MarshalXDR() []byte {
func (o FileInfoTruncated) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o FileInfoTruncated) AppendXDR(bs []byte) []byte {
func (o FileInfoTruncated) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o FileInfoTruncated) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o FileInfoTruncated) encodeXDR(xw *xdr.Writer) (int, error) {
if len(o.Name) > 8192 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.Name); l > 8192 {
return xw.Tot(), xdr.ElementSizeExceeded("Name", l, 8192)
}
xw.WriteString(o.Name)
xw.WriteUint32(o.Flags)
@ -318,21 +342,29 @@ func (o BlockInfo) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o BlockInfo) MarshalXDR() []byte {
func (o BlockInfo) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o BlockInfo) AppendXDR(bs []byte) []byte {
func (o BlockInfo) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o BlockInfo) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o BlockInfo) encodeXDR(xw *xdr.Writer) (int, error) {
xw.WriteUint32(o.Size)
if len(o.Hash) > 64 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.Hash); l > 64 {
return xw.Tot(), xdr.ElementSizeExceeded("Hash", l, 64)
}
xw.WriteBytes(o.Hash)
return xw.Tot(), xw.Error()
@ -396,24 +428,32 @@ func (o RequestMessage) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o RequestMessage) MarshalXDR() []byte {
func (o RequestMessage) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o RequestMessage) AppendXDR(bs []byte) []byte {
func (o RequestMessage) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o RequestMessage) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o RequestMessage) encodeXDR(xw *xdr.Writer) (int, error) {
if len(o.Folder) > 64 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.Folder); l > 64 {
return xw.Tot(), xdr.ElementSizeExceeded("Folder", l, 64)
}
xw.WriteString(o.Folder)
if len(o.Name) > 8192 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.Name); l > 8192 {
return xw.Tot(), xdr.ElementSizeExceeded("Name", l, 8192)
}
xw.WriteString(o.Name)
xw.WriteUint64(o.Offset)
@ -466,15 +506,23 @@ func (o ResponseMessage) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o ResponseMessage) MarshalXDR() []byte {
func (o ResponseMessage) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o ResponseMessage) AppendXDR(bs []byte) []byte {
func (o ResponseMessage) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o ResponseMessage) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o ResponseMessage) encodeXDR(xw *xdr.Writer) (int, error) {
@ -545,28 +593,36 @@ func (o ClusterConfigMessage) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o ClusterConfigMessage) MarshalXDR() []byte {
func (o ClusterConfigMessage) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o ClusterConfigMessage) AppendXDR(bs []byte) []byte {
func (o ClusterConfigMessage) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o ClusterConfigMessage) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o ClusterConfigMessage) encodeXDR(xw *xdr.Writer) (int, error) {
if len(o.ClientName) > 64 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.ClientName); l > 64 {
return xw.Tot(), xdr.ElementSizeExceeded("ClientName", l, 64)
}
xw.WriteString(o.ClientName)
if len(o.ClientVersion) > 64 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.ClientVersion); l > 64 {
return xw.Tot(), xdr.ElementSizeExceeded("ClientVersion", l, 64)
}
xw.WriteString(o.ClientVersion)
if len(o.Folders) > 64 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.Folders); l > 64 {
return xw.Tot(), xdr.ElementSizeExceeded("Folders", l, 64)
}
xw.WriteUint32(uint32(len(o.Folders)))
for i := range o.Folders {
@ -575,8 +631,8 @@ func (o ClusterConfigMessage) encodeXDR(xw *xdr.Writer) (int, error) {
return xw.Tot(), err
}
}
if len(o.Options) > 64 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.Options); l > 64 {
return xw.Tot(), xdr.ElementSizeExceeded("Options", l, 64)
}
xw.WriteUint32(uint32(len(o.Options)))
for i := range o.Options {
@ -604,7 +660,7 @@ func (o *ClusterConfigMessage) decodeXDR(xr *xdr.Reader) error {
o.ClientVersion = xr.ReadStringMax(64)
_FoldersSize := int(xr.ReadUint32())
if _FoldersSize > 64 {
return xdr.ErrElementSizeExceeded
return xdr.ElementSizeExceeded("Folders", _FoldersSize, 64)
}
o.Folders = make([]Folder, _FoldersSize)
for i := range o.Folders {
@ -612,7 +668,7 @@ func (o *ClusterConfigMessage) decodeXDR(xr *xdr.Reader) error {
}
_OptionsSize := int(xr.ReadUint32())
if _OptionsSize > 64 {
return xdr.ErrElementSizeExceeded
return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
}
o.Options = make([]Option, _OptionsSize)
for i := range o.Options {
@ -654,20 +710,28 @@ func (o Folder) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o Folder) MarshalXDR() []byte {
func (o Folder) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o Folder) AppendXDR(bs []byte) []byte {
func (o Folder) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o Folder) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o Folder) encodeXDR(xw *xdr.Writer) (int, error) {
if len(o.ID) > 64 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.ID); l > 64 {
return xw.Tot(), xdr.ElementSizeExceeded("ID", l, 64)
}
xw.WriteString(o.ID)
xw.WriteUint32(uint32(len(o.Devices)))
@ -735,20 +799,28 @@ func (o Device) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o Device) MarshalXDR() []byte {
func (o Device) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o Device) AppendXDR(bs []byte) []byte {
func (o Device) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o Device) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o Device) encodeXDR(xw *xdr.Writer) (int, error) {
if len(o.ID) > 32 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.ID); l > 32 {
return xw.Tot(), xdr.ElementSizeExceeded("ID", l, 32)
}
xw.WriteBytes(o.ID)
xw.WriteUint32(o.Flags)
@ -807,24 +879,32 @@ func (o Option) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o Option) MarshalXDR() []byte {
func (o Option) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o Option) AppendXDR(bs []byte) []byte {
func (o Option) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o Option) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o Option) encodeXDR(xw *xdr.Writer) (int, error) {
if len(o.Key) > 64 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.Key); l > 64 {
return xw.Tot(), xdr.ElementSizeExceeded("Key", l, 64)
}
xw.WriteString(o.Key)
if len(o.Value) > 1024 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.Value); l > 1024 {
return xw.Tot(), xdr.ElementSizeExceeded("Value", l, 1024)
}
xw.WriteString(o.Value)
return xw.Tot(), xw.Error()
@ -873,20 +953,28 @@ func (o CloseMessage) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o CloseMessage) MarshalXDR() []byte {
func (o CloseMessage) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o CloseMessage) AppendXDR(bs []byte) []byte {
func (o CloseMessage) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o CloseMessage) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o CloseMessage) encodeXDR(xw *xdr.Writer) (int, error) {
if len(o.Reason) > 1024 {
return xw.Tot(), xdr.ErrElementSizeExceeded
if l := len(o.Reason); l > 1024 {
return xw.Tot(), xdr.ElementSizeExceeded("Reason", l, 1024)
}
xw.WriteString(o.Reason)
return xw.Tot(), xw.Error()
@ -927,15 +1015,23 @@ func (o EmptyMessage) EncodeXDR(w io.Writer) (int, error) {
return o.encodeXDR(xw)
}
func (o EmptyMessage) MarshalXDR() []byte {
func (o EmptyMessage) MarshalXDR() ([]byte, error) {
return o.AppendXDR(make([]byte, 0, 128))
}
func (o EmptyMessage) AppendXDR(bs []byte) []byte {
func (o EmptyMessage) MustMarshalXDR() []byte {
bs, err := o.MarshalXDR()
if err != nil {
panic(err)
}
return bs
}
func (o EmptyMessage) AppendXDR(bs []byte) ([]byte, error) {
var aw = xdr.AppendWriter(bs)
var xw = xdr.NewWriter(&aw)
o.encodeXDR(xw)
return []byte(aw)
_, err := o.encodeXDR(xw)
return []byte(aw), err
}
func (o EmptyMessage) encodeXDR(xw *xdr.Writer) (int, error) {

View File

@ -126,7 +126,7 @@ type hdrMsg struct {
}
type encodable interface {
AppendXDR([]byte) []byte
AppendXDR([]byte) ([]byte, error)
}
const (
@ -483,7 +483,11 @@ func (c *rawConnection) writerLoop() {
case hm := <-c.outbox:
if hm.msg != nil {
// Uncompressed message in uncBuf
uncBuf = hm.msg.AppendXDR(uncBuf[:0])
uncBuf, err = hm.msg.AppendXDR(uncBuf[:0])
if err != nil {
c.close(err)
return
}
if len(uncBuf) >= c.compressionThreshold {
// Use compression for large messages

View File

@ -25,6 +25,7 @@ import (
"io/ioutil"
"os"
"reflect"
"strings"
"testing"
"testing/quick"
@ -369,7 +370,7 @@ func testMarshal(t *testing.T, prefix string, m1, m2 message) bool {
}
_, err := m1.EncodeXDR(&buf)
if err == xdr.ErrElementSizeExceeded {
if err != nil && strings.Contains(err.Error(), "exceeds size") {
return true
}
if err != nil {

View File

@ -1,8 +1,105 @@
<configuration version="6">
<folder id="default" path="s2" ro="false" rescanIntervalS="15" ignorePerms="false">
<device id="AF2HXQA-DOKKIMI-PKOG4RE-E25UTJ7-PSGQ7T5-WEY7YT5-SG6N7W5-NNA4BQM"></device>
<device id="AND7GW6-DZN66F2-TKYJSTC-ACI7MYT-75X4T63-SE5S4MQ-GBSDHDL-4CLHJA6"></device>
<device id="ATFGYHM-ZTA5Z7B-NYPY4OK-VJWDD6Y-SLCDKED-L2VDESD-TUBLMLH-CHAJEAI"></device>
<device id="AUI2JXT-PXK3PMB-YATDUNI-RIGBTKQ-VBUJLUH-JLWZEEZ-3UV5X2G-AWKOVQH"></device>
<device id="AZCVBDG-6GATBA3-XX7HOH3-LY5R3L2-SPCQOIT-KFVLUCC-GQ3KJTJ-ZSP3RAQ"></device>
<device id="A4W7JO6-IM2ZWBX-AYYLCEP-NFTJKQV-2QRGZJ4-QGSP3HI-R7UHNYF-GAKDLAS"></device>
<device id="BDZ662J-Q5IXRE3-CEPHNIM-V5D76GA-U26VZRI-UHTDERX-UABQEQ3-CKE5CQD"></device>
<device id="BP6X6FF-2SEA3QR-RW2Y55O-B4X7LR6-5EXGA6D-KM725CC-CIDD7ZZ-7VNKXAT"></device>
<device id="CDUKUIK-56DOHM3-M7VHCL6-5HGTZHB-QULOOBK-3DK6QXC-4DDDXKS-H5OOZAQ"></device>
<device id="CJRFL4X-PBD3H6K-2GCPPYS-JCZ73GF-SN63HMU-WGYMZQZ-W2AWEDG-AZ623QB"></device>
<device id="DDGR3XN-CKDGXS4-VR6G2T3-GQJVO7E-XDZGWUJ-733J3G7-NMGQFS5-ETFWKA6"></device>
<device id="DFAWRIU-3MSLR65-L7DZE53-UNR6KEX-FTJZJ2B-NVYEPQH-ZYDYU6D-ROAKFAJ"></device>
<device id="DMFDJGV-UDOMXZU-FKKYXCT-BEGE7K7-OVAFMQW-NBLQ46C-J6VORML-27T3SA2"></device>
<device id="DM4ZS3J-WF2WJGP-YNPTRRZ-MKUEDUC-7N5Q6EX-IHLR7DM-VHMP3FE-KXGHDAG"></device>
<device id="DPJWHYQ-S7N7D4C-P4CY65M-ZJ6MFOD-OR5CF7N-2TYSQ6T-IZQDK67-NYDMCQR"></device>
<device id="DYVI4HB-DO3WULH-YC4CVUV-KOHXVYH-MSRQH5O-P4JX5T4-WCVDJK2-QPWZPQQ"></device>
<device id="EAELU3R-WFMEOHV-VUF3ZQU-T6QLRUQ-S7HFGGG-447PNIB-Z32VNM6-3XRG2QM"></device>
<device id="EHYW5K4-WLONYO4-LVJFY7T-7Y6W7EA-N3O5ADS-NPXYFBA-TOSC3X2-J4BYNAV"></device>
<device id="E3KQXYD-ST7GNHR-EQVEBUK-I7X4NFR-J6JEGSL-XSU3236-PXDEOYF-SIGVJQD"></device>
<device id="FDN276E-J7AQ32B-Y4CBY3Q-TWCQ2RV-QQTQ5NJ-NLLFCGR-UOXSYQN-VLHSHAK"></device>
<device id="FOUTNQ4-M3X4ZL6-VPG3ZD6-3EFUKTT-XVNS6N6-G2E56OA-LYBDCXY-S52OLQJ"></device>
<device id="F3CJABZ-VOJPUJJ-A6V6J2O-PQF32IV-5VZY45B-ASPFXOE-OMZOBYX-IIVGJQR"></device>
<device id="F722I3J-JNESQSZ-NWHMLX7-OX5YQPY-Q4P7AKS-RAVRYFO-LUFHTBF-MV5ZEAW"></device>
<device id="GRCZC6E-PRZ5EPJ-5XZK2WL-K7PBC4D-EU7J5K3-YHME4GD-AKHMR3U-HTGE3AJ"></device>
<device id="GTZORAO-4U2BMRT-NJ7VGJV-DEFE7X4-GK7GOKP-56NADQB-DKRY64G-GK4JSAN"></device>
<device id="HHKXJNI-2FOFGMU-KT55UTA-J7CDGBX-KE3RMHK-NDVQYCB-DPUAFTN-C62LTAJ"></device>
<device id="HOA63TU-7NASFTP-7JKRNDD-CEBCBTL-3UB6GEH-LE3TBF6-UHBJUX2-B53JYQO"></device>
<device id="H4IJPSS-SGRGAHF-4HVWJXK-AQ2EV4C-EMWQWG4-63ORWGX-CTBONEL-3IO2VA2"></device>
<device id="IEDANYN-UEK237R-Z6J75BD-3SK2RPX-66FJ2F2-T347HN3-KYY2PW5-47ZAIAE"></device>
<device id="IHFBE2Q-VJKGLJN-PYEX3SL-ORFUM6B-GUDQVUN-32TZFZO-GMYDUFI-VBICSA3"></device>
<device id="ITFMT23-LPJ4LN3-Y6HQUYG-GO47ZU7-PO6SUJ6-7UO2JNG-SKI5CTG-WJCK2AP"></device>
<device id="IWWUWMQ-3KMUGCT-JJII2HN-J6NN6OW-43AM5TT-MHJXRCR-D7MJVWE-HLAD4QV"></device>
<device id="IW5I76B-NKCHGJ5-HAQNOBW-LRHKYSH-54VEQQC-HQAXHVC-DX4ME3O-C7XGUAQ"></device>
<device id="I2VEKOT-P63JCHG-LKHJAOJ-XL4VNH2-Y7WQGAW-JKQQQQY-QHIOB77-Q7WLYAW"></device>
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU"></device>
<device id="JMFJCXB-GZDE4BN-OCJE3VF-65GYZNU-AIVJRET-3J6HMRQ-AUQIGJO-FKNHMQU"></device>
<device id="JWPMLRX-U5Q3LSU-LZJCM2Z-GIZLQO2-UBSZKUO-INZXYTZ-7E6PDK2-PPDQWA4"></device>
<device id="KBBKAQ6-VINPVTG-LXSDXXF-T67N5DU-ONRJI3P-WIAZBY7-UCMAJJ4-274ZBA5"></device>
<device id="KNOUKFP-JEJTAZO-FUI7KMO-4PQJOMB-CX255W3-U3SHR2I-LJ4LBHL-AY6BCA5"></device>
<device id="KOUXI7Y-6PWCH2V-552D2Y4-6D3HQJZ-CEKC5IY-XVUV2KC-452GVYG-AJN4TQB"></device>
<device id="K3Z2ESL-SKLK2UF-CLA2RLL-DWQVXMU-7PQQ5MV-2BWWSCI-MAOF7YR-277YSAL"></device>
<device id="LW7D6SQ-3UIMICX-EEPGTNA-P6NNR2M-35WLLB5-QKW5KWV-3PGTWA3-QM65DA4"></device>
<device id="LYYJOWF-WZWZ3Z6-O4JRBOM-44LPK33-YDB5EFZ-CTX2FNI-C5BNMOH-ZBREKAK"></device>
<device id="L5GRUQX-3FPGQG6-CWJ3CQN-QJO7BFV-FDXYBXG-AKAZ465-3D37XCR-SHPHDQS"></device>
<device id="L7L37HS-3STS6WA-G76FKTT-FAIFH4G-46EORMF-6PA5JFZ-RW4QJDM-SDSBIAM"></device>
<device id="MNLYDAL-BTM5JJL-PYOD2C6-MWKXW5N-I7KGYQJ-TAJ2NQE-K2MI7C5-RIM7CA7"></device>
<device id="NBO5E2S-OIKGPUN-AXQSTWG-YNSZTQD-YSQ3JGC-BAPD72J-5BUYGBC-4U75XQK"></device>
<device id="NWKOEM4-T62TE6V-H4X75L2-GX75Q3C-XCA3OS7-X7MHSVV-IOS5V3U-6Y4IAAZ"></device>
<device id="NZKXQ5V-GXMQEY4-77E6DUN-UJK5O3O-633AEIQ-2ZB6FJL-ZPT3AFY-IFMTVAS"></device>
<device id="OYRDGZZ-BBBYTOG-PPOEKNF-RFOU5YJ-LOJXGDC-PZFHUMW-EHKAEGM-ZI5O6QX"></device>
<device id="O5BUAKV-5PXZUBI-TGCAIHE-646RUCT-5KSKVSY-NMVEZNC-XWIDH4M-N4FWJQK"></device>
<device id="PR7FK7P-O57IUUQ-L7NVOBM-BJ6B6HV-A4Q2ZM7-3ZB7YPB-2CEI225-VNE27QZ"></device>
<device id="P74TJEI-WJADRBZ-J5ZWR3D-WNGTH7F-QNTKVBW-QJSYCKN-VSXWFFY-BFZXZQH"></device>
<device id="QLQ4VDH-DCRQOCW-45ZU3NH-KGVNZ4K-RY5VH5M-E54B35V-3GDPECV-FATMQQN"></device>
<device id="QTSMMK3-7LDPTPP-NFQJBKI-MXPNRLC-RBJ46YH-EUOIHQX-X5OBHY5-DSEHQAB"></device>
<device id="RNEPSQ3-XBYBJX2-DC63XIJ-KOV6OWX-3RBQ245-SDO5NPG-7DNYUIJ-V3BVBAR"></device>
<device id="RSPOEAQ-WEOFVAO-7F5OHVV-FPEK2XA-KG23ACJ-ISANINA-EVNGPFM-6GUGUAY"></device>
<device id="SMFBW3X-ZG7GEVQ-Q5EY2HV-QTTJ2AK-CMKRW7K-4ZYN5KF-D5LHXBA-J2WJPQA"></device>
<device id="SRC4EBU-57YZZF2-U72JRON-LWMXERT-NL3R4YY-7T3H6FI-VPK7KMQ-Q7LGBQE"></device>
<device id="SVUEDXK-GOM6UEO-BK725GI-4R4GRA2-SL6EJQ7-2TQZDF4-IQEM34P-P32HLAN"></device>
<device id="S257JIU-2LMK7R6-J4EPHQE-DHBSA2V-5S45UV5-NQNI6G4-UUPXTWK-5L645AB"></device>
<device id="S475AJS-UF2H3WA-ZKSFH7J-YJC6Q4P-L6O7NDM-Q7LJKVE-CQ2DTVS-N6TY6QJ"></device>
<device id="TE3RAAB-2F65ZF2-MHDELWZ-YJHF72E-5S7HGWF-NJ6CKZK-3LJHQ72-YGOPOAR"></device>
<device id="TJEEOEJ-ADKANZG-T3RYCWO-FYJW3GQ-TX7FDLE-I3X3QLX-TQ37HW4-DQQZEAK"></device>
<device id="TN64K4J-D7S33RD-55TMUBA-D2VKVRW-FAPQRGY-2VXAMMZ-F2RBYC7-DJAXFAI"></device>
<device id="TP4ORAM-C2K6L3Z-3SFLGQF-NILNVZQ-3VSHEGT-LLKQU7K-BV66VQZ-TP65EAS"></device>
<device id="T345SQH-CZNXG3Q-WVMQDHY-S524RKJ-C3AP767-QTYGY3X-GASFDCK-LURRWAC"></device>
<device id="UMMN7QY-46JHZ4X-UJ5TWWV-EU6OKDA-4GM76QK-UUJJLPH-27H6P22-XQALWQ6"></device>
<device id="UZPBG42-GSUBWXD-ALOS27M-AQAWWWJ-XP5DA46-3GWEZ4U-I5I2JAZ-5VPHYQI"></device>
<device id="VU764LI-72YK4KV-APLQAXJ-4Q46MTT-FCQFB33-JTCH4E5-G7OJRFI-YGPJYAJ"></device>
<device id="WDGE4EI-CO3MSS6-JM5ASRP-YHBCKBN-EWAT5KQ-GGQQTZE-OSDTGH2-P2BE3AA"></device>
<device id="WGURZJA-JLZYF3P-KRQDIAL-6RGUYSZ-UZEJ3DO-U6JOGTO-PYQRA7K-NOEFSQ2"></device>
<device id="WLTEXSS-RUQSDSJ-FWYVE4T-S25G37F-4XPT7GI-VLBSHCS-SLHXBEF-QSKCQAK"></device>
<device id="WNK25O3-CDDKOM6-VDJKB2C-GZ6L6GA-HI7WHXU-G2SCLLF-EEDJX6N-AYY5OQM"></device>
<device id="WTA46HO-WMIMK2N-GMNLWSQ-UOZI2O7-JVG3YY2-FWXQ3NJ-NWG5PLX-RN26AAG"></device>
<device id="XG6FGXG-CCB54FX-AVREF7W-YMR66ED-66H4QQD-KXK5K7C-DVVYKU2-GF77LQK"></device>
<device id="XSWE7HJ-AHTZGEZ-UNH6ZSK-WZHRS2C-55LQZDR-7GLSE6Z-VWEDFWT-ZS4DCAK"></device>
<device id="XXXMH6A-3J53GPW-7BVBR76-5UJNW3E-6H2ACDZ-DY46Z6T-FD7GRPE-RGIQFAE"></device>
<device id="X2J2EIR-6MCQUSZ-DZU37TI-Y5S4QUN-JPTDKHT-ANBGXU5-YRD3EGF-6VOEHQG"></device>
<device id="X3WHUR4-N5BSQ4T-YFC3A3S-KI6KXQ2-6GL66YV-G2YGVKY-LEKVIXY-M62C6A7"></device>
<device id="X4EX7JM-CQSNCZ5-H5UNVUW-RNWT4U7-VPFKPB4-P65IWZC-N74SCAM-RLLFUAT"></device>
<device id="YEKECF3-5YAC2EZ-ADYLARL-THNGWI3-ZFU6UNK-3KCFZWB-IRYFH6X-LO5ZHQC"></device>
<device id="YP66HGG-CIWZ6N2-A3T4PXT-KP47YWK-3ZVW5UX-MGTZRHL-YNB22IK-IEEWNAY"></device>
<device id="YRG374E-AB2PK5W-UKR2MOA-E43VSQF-ICO72FN-FDZDX7F-Q75UE2H-EUCIFQJ"></device>
<device id="ZCZPFDT-GHJKTPJ-22WKCWP-2UO7X33-NUD2YAK-P6MDHN6-2JVAETG-JPXXLAR"></device>
<device id="2MDVGTI-EGVOHIO-SPFMLV6-NRFP2EC-HWMLWVO-QVLLTC3-DSQPHMY-JDEIQQ5"></device>
<device id="22UBH4D-HGCP3EW-EHVJTZ4-6PB2HOB-LDPOC3X-ONY6PA6-2DFNBIX-D3X5UQT"></device>
<device id="3OQ6R42-O76KQJM-JYN7EGL-PWKZUPG-276FWG2-CP3CTV3-SM545L3-KTTH3AG"></device>
<device id="3QW353X-J52M5LW-3DUY2Z5-SG5JU7Z-6NSJFAM-74YPGMA-VHL2W3N-5PX2JQO"></device>
<device id="373HSRP-QLPNLIE-JYKZVQF-P4PKZ63-R2ZE6K3-YD442U2-JHBGBQG-WWXAHAU"></device>
<device id="4IB56JJ-IXYAHRI-6W75NTN-TEIMPRE-25NAG6F-MYGBJBG-JB33DXY-L2WINQK"></device>
<device id="4JSTGIZ-Q2UJXIG-V6QT62A-FSRBKLF-J6FWGIY-PFIAFC3-7GP7DOY-RVNGKQU"></device>
<device id="5BKVB65-I2E5UCX-DWWPLNP-IQ7VRZY-I3P42KF-ZKAF7XO-TQTCLVU-TH45GA3"></device>
<device id="5OOL3EO-A5DTJ5N-CZHPYVP-YH74NL4-DE3O7AU-ZDFSM6D-KG5IUWA-WJJXBQL"></device>
<device id="5RXIYVZ-NOEGGYD-TY4WQTH-U6FQ5IR-LJTW3P3-HFQBN6C-RC4AB5V-MO44PQV"></device>
<device id="5WGZMYP-FNTWGLG-PQKEB5V-PJ4LEPL-53SRHBR-HHSHHLD-5KFL3P2-W6MTRQY"></device>
<device id="6I5KLVE-VVRXZ55-Z3IQIZH-3FBHKTZ-6MIPAYO-V4QX6K7-7J432VD-T5TY6AU"></device>
<device id="6R2BAIE-VRYZJXA-ZKJT4ZK-BQTXA46-VBWMLNY-PINTWYG-TS4Y6GZ-7Z2KVQG"></device>
<device id="7BT3IUI-RYS757D-YJPBP6U-WPL2ZI6-CMICBLW-UDYDSVT-NG62TB5-PM2O3A6"></device>
<device id="7TVCUYX-MLZH6GF-GDLVMJ6-REPXXUD-DCLPXP2-67HS7VR-QTTABOA-4ZXJOQD"></device>
<versioning></versioning>
<lenientMtimes>false</lenientMtimes>
</folder>
@ -18,15 +115,306 @@
<versioning></versioning>
<lenientMtimes>false</lenientMtimes>
</folder>
<device id="AF2HXQA-DOKKIMI-PKOG4RE-E25UTJ7-PSGQ7T5-WEY7YT5-SG6N7W5-NNA4BQM" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="AND7GW6-DZN66F2-TKYJSTC-ACI7MYT-75X4T63-SE5S4MQ-GBSDHDL-4CLHJA6" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="ATFGYHM-ZTA5Z7B-NYPY4OK-VJWDD6Y-SLCDKED-L2VDESD-TUBLMLH-CHAJEAI" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="AUI2JXT-PXK3PMB-YATDUNI-RIGBTKQ-VBUJLUH-JLWZEEZ-3UV5X2G-AWKOVQH" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="AZCVBDG-6GATBA3-XX7HOH3-LY5R3L2-SPCQOIT-KFVLUCC-GQ3KJTJ-ZSP3RAQ" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="A4W7JO6-IM2ZWBX-AYYLCEP-NFTJKQV-2QRGZJ4-QGSP3HI-R7UHNYF-GAKDLAS" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="BDZ662J-Q5IXRE3-CEPHNIM-V5D76GA-U26VZRI-UHTDERX-UABQEQ3-CKE5CQD" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="BP6X6FF-2SEA3QR-RW2Y55O-B4X7LR6-5EXGA6D-KM725CC-CIDD7ZZ-7VNKXAT" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="CDUKUIK-56DOHM3-M7VHCL6-5HGTZHB-QULOOBK-3DK6QXC-4DDDXKS-H5OOZAQ" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="CJRFL4X-PBD3H6K-2GCPPYS-JCZ73GF-SN63HMU-WGYMZQZ-W2AWEDG-AZ623QB" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="DDGR3XN-CKDGXS4-VR6G2T3-GQJVO7E-XDZGWUJ-733J3G7-NMGQFS5-ETFWKA6" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="DFAWRIU-3MSLR65-L7DZE53-UNR6KEX-FTJZJ2B-NVYEPQH-ZYDYU6D-ROAKFAJ" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="DMFDJGV-UDOMXZU-FKKYXCT-BEGE7K7-OVAFMQW-NBLQ46C-J6VORML-27T3SA2" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="DM4ZS3J-WF2WJGP-YNPTRRZ-MKUEDUC-7N5Q6EX-IHLR7DM-VHMP3FE-KXGHDAG" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="DPJWHYQ-S7N7D4C-P4CY65M-ZJ6MFOD-OR5CF7N-2TYSQ6T-IZQDK67-NYDMCQR" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="DYVI4HB-DO3WULH-YC4CVUV-KOHXVYH-MSRQH5O-P4JX5T4-WCVDJK2-QPWZPQQ" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="EAELU3R-WFMEOHV-VUF3ZQU-T6QLRUQ-S7HFGGG-447PNIB-Z32VNM6-3XRG2QM" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="EHYW5K4-WLONYO4-LVJFY7T-7Y6W7EA-N3O5ADS-NPXYFBA-TOSC3X2-J4BYNAV" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="E3KQXYD-ST7GNHR-EQVEBUK-I7X4NFR-J6JEGSL-XSU3236-PXDEOYF-SIGVJQD" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="FDN276E-J7AQ32B-Y4CBY3Q-TWCQ2RV-QQTQ5NJ-NLLFCGR-UOXSYQN-VLHSHAK" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="FOUTNQ4-M3X4ZL6-VPG3ZD6-3EFUKTT-XVNS6N6-G2E56OA-LYBDCXY-S52OLQJ" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="F3CJABZ-VOJPUJJ-A6V6J2O-PQF32IV-5VZY45B-ASPFXOE-OMZOBYX-IIVGJQR" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="F722I3J-JNESQSZ-NWHMLX7-OX5YQPY-Q4P7AKS-RAVRYFO-LUFHTBF-MV5ZEAW" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="GRCZC6E-PRZ5EPJ-5XZK2WL-K7PBC4D-EU7J5K3-YHME4GD-AKHMR3U-HTGE3AJ" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="GTZORAO-4U2BMRT-NJ7VGJV-DEFE7X4-GK7GOKP-56NADQB-DKRY64G-GK4JSAN" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="HHKXJNI-2FOFGMU-KT55UTA-J7CDGBX-KE3RMHK-NDVQYCB-DPUAFTN-C62LTAJ" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="HOA63TU-7NASFTP-7JKRNDD-CEBCBTL-3UB6GEH-LE3TBF6-UHBJUX2-B53JYQO" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="H4IJPSS-SGRGAHF-4HVWJXK-AQ2EV4C-EMWQWG4-63ORWGX-CTBONEL-3IO2VA2" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="IEDANYN-UEK237R-Z6J75BD-3SK2RPX-66FJ2F2-T347HN3-KYY2PW5-47ZAIAE" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="IHFBE2Q-VJKGLJN-PYEX3SL-ORFUM6B-GUDQVUN-32TZFZO-GMYDUFI-VBICSA3" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="ITFMT23-LPJ4LN3-Y6HQUYG-GO47ZU7-PO6SUJ6-7UO2JNG-SKI5CTG-WJCK2AP" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="IWWUWMQ-3KMUGCT-JJII2HN-J6NN6OW-43AM5TT-MHJXRCR-D7MJVWE-HLAD4QV" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="IW5I76B-NKCHGJ5-HAQNOBW-LRHKYSH-54VEQQC-HQAXHVC-DX4ME3O-C7XGUAQ" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="I2VEKOT-P63JCHG-LKHJAOJ-XL4VNH2-Y7WQGAW-JKQQQQY-QHIOB77-Q7WLYAW" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" name="s1" compression="true" introducer="false">
<address>127.0.0.1:22001</address>
</device>
<device id="JMFJCXB-GZDE4BN-OCJE3VF-65GYZNU-AIVJRET-3J6HMRQ-AUQIGJO-FKNHMQU" name="s2" compression="true" introducer="false">
<address>127.0.0.1:22002</address>
</device>
<device id="JWPMLRX-U5Q3LSU-LZJCM2Z-GIZLQO2-UBSZKUO-INZXYTZ-7E6PDK2-PPDQWA4" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="KBBKAQ6-VINPVTG-LXSDXXF-T67N5DU-ONRJI3P-WIAZBY7-UCMAJJ4-274ZBA5" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="KNOUKFP-JEJTAZO-FUI7KMO-4PQJOMB-CX255W3-U3SHR2I-LJ4LBHL-AY6BCA5" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="KOUXI7Y-6PWCH2V-552D2Y4-6D3HQJZ-CEKC5IY-XVUV2KC-452GVYG-AJN4TQB" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="K3Z2ESL-SKLK2UF-CLA2RLL-DWQVXMU-7PQQ5MV-2BWWSCI-MAOF7YR-277YSAL" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="LW7D6SQ-3UIMICX-EEPGTNA-P6NNR2M-35WLLB5-QKW5KWV-3PGTWA3-QM65DA4" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="LYYJOWF-WZWZ3Z6-O4JRBOM-44LPK33-YDB5EFZ-CTX2FNI-C5BNMOH-ZBREKAK" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="L5GRUQX-3FPGQG6-CWJ3CQN-QJO7BFV-FDXYBXG-AKAZ465-3D37XCR-SHPHDQS" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="L7L37HS-3STS6WA-G76FKTT-FAIFH4G-46EORMF-6PA5JFZ-RW4QJDM-SDSBIAM" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="MNLYDAL-BTM5JJL-PYOD2C6-MWKXW5N-I7KGYQJ-TAJ2NQE-K2MI7C5-RIM7CA7" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="NBO5E2S-OIKGPUN-AXQSTWG-YNSZTQD-YSQ3JGC-BAPD72J-5BUYGBC-4U75XQK" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="NWKOEM4-T62TE6V-H4X75L2-GX75Q3C-XCA3OS7-X7MHSVV-IOS5V3U-6Y4IAAZ" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="NZKXQ5V-GXMQEY4-77E6DUN-UJK5O3O-633AEIQ-2ZB6FJL-ZPT3AFY-IFMTVAS" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="OYRDGZZ-BBBYTOG-PPOEKNF-RFOU5YJ-LOJXGDC-PZFHUMW-EHKAEGM-ZI5O6QX" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="O5BUAKV-5PXZUBI-TGCAIHE-646RUCT-5KSKVSY-NMVEZNC-XWIDH4M-N4FWJQK" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="PR7FK7P-O57IUUQ-L7NVOBM-BJ6B6HV-A4Q2ZM7-3ZB7YPB-2CEI225-VNE27QZ" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="P74TJEI-WJADRBZ-J5ZWR3D-WNGTH7F-QNTKVBW-QJSYCKN-VSXWFFY-BFZXZQH" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="QLQ4VDH-DCRQOCW-45ZU3NH-KGVNZ4K-RY5VH5M-E54B35V-3GDPECV-FATMQQN" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="QTSMMK3-7LDPTPP-NFQJBKI-MXPNRLC-RBJ46YH-EUOIHQX-X5OBHY5-DSEHQAB" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="RNEPSQ3-XBYBJX2-DC63XIJ-KOV6OWX-3RBQ245-SDO5NPG-7DNYUIJ-V3BVBAR" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="RSPOEAQ-WEOFVAO-7F5OHVV-FPEK2XA-KG23ACJ-ISANINA-EVNGPFM-6GUGUAY" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="SMFBW3X-ZG7GEVQ-Q5EY2HV-QTTJ2AK-CMKRW7K-4ZYN5KF-D5LHXBA-J2WJPQA" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="SRC4EBU-57YZZF2-U72JRON-LWMXERT-NL3R4YY-7T3H6FI-VPK7KMQ-Q7LGBQE" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="SVUEDXK-GOM6UEO-BK725GI-4R4GRA2-SL6EJQ7-2TQZDF4-IQEM34P-P32HLAN" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="S257JIU-2LMK7R6-J4EPHQE-DHBSA2V-5S45UV5-NQNI6G4-UUPXTWK-5L645AB" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="S475AJS-UF2H3WA-ZKSFH7J-YJC6Q4P-L6O7NDM-Q7LJKVE-CQ2DTVS-N6TY6QJ" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="TE3RAAB-2F65ZF2-MHDELWZ-YJHF72E-5S7HGWF-NJ6CKZK-3LJHQ72-YGOPOAR" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="TJEEOEJ-ADKANZG-T3RYCWO-FYJW3GQ-TX7FDLE-I3X3QLX-TQ37HW4-DQQZEAK" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="TN64K4J-D7S33RD-55TMUBA-D2VKVRW-FAPQRGY-2VXAMMZ-F2RBYC7-DJAXFAI" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="TP4ORAM-C2K6L3Z-3SFLGQF-NILNVZQ-3VSHEGT-LLKQU7K-BV66VQZ-TP65EAS" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="T345SQH-CZNXG3Q-WVMQDHY-S524RKJ-C3AP767-QTYGY3X-GASFDCK-LURRWAC" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="UMMN7QY-46JHZ4X-UJ5TWWV-EU6OKDA-4GM76QK-UUJJLPH-27H6P22-XQALWQ6" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="UZPBG42-GSUBWXD-ALOS27M-AQAWWWJ-XP5DA46-3GWEZ4U-I5I2JAZ-5VPHYQI" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="VU764LI-72YK4KV-APLQAXJ-4Q46MTT-FCQFB33-JTCH4E5-G7OJRFI-YGPJYAJ" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="WDGE4EI-CO3MSS6-JM5ASRP-YHBCKBN-EWAT5KQ-GGQQTZE-OSDTGH2-P2BE3AA" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="WGURZJA-JLZYF3P-KRQDIAL-6RGUYSZ-UZEJ3DO-U6JOGTO-PYQRA7K-NOEFSQ2" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="WLTEXSS-RUQSDSJ-FWYVE4T-S25G37F-4XPT7GI-VLBSHCS-SLHXBEF-QSKCQAK" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="WNK25O3-CDDKOM6-VDJKB2C-GZ6L6GA-HI7WHXU-G2SCLLF-EEDJX6N-AYY5OQM" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="WTA46HO-WMIMK2N-GMNLWSQ-UOZI2O7-JVG3YY2-FWXQ3NJ-NWG5PLX-RN26AAG" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="XG6FGXG-CCB54FX-AVREF7W-YMR66ED-66H4QQD-KXK5K7C-DVVYKU2-GF77LQK" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="XSWE7HJ-AHTZGEZ-UNH6ZSK-WZHRS2C-55LQZDR-7GLSE6Z-VWEDFWT-ZS4DCAK" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="XXXMH6A-3J53GPW-7BVBR76-5UJNW3E-6H2ACDZ-DY46Z6T-FD7GRPE-RGIQFAE" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="X2J2EIR-6MCQUSZ-DZU37TI-Y5S4QUN-JPTDKHT-ANBGXU5-YRD3EGF-6VOEHQG" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="X3WHUR4-N5BSQ4T-YFC3A3S-KI6KXQ2-6GL66YV-G2YGVKY-LEKVIXY-M62C6A7" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="X4EX7JM-CQSNCZ5-H5UNVUW-RNWT4U7-VPFKPB4-P65IWZC-N74SCAM-RLLFUAT" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="YEKECF3-5YAC2EZ-ADYLARL-THNGWI3-ZFU6UNK-3KCFZWB-IRYFH6X-LO5ZHQC" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="YP66HGG-CIWZ6N2-A3T4PXT-KP47YWK-3ZVW5UX-MGTZRHL-YNB22IK-IEEWNAY" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="YRG374E-AB2PK5W-UKR2MOA-E43VSQF-ICO72FN-FDZDX7F-Q75UE2H-EUCIFQJ" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="ZCZPFDT-GHJKTPJ-22WKCWP-2UO7X33-NUD2YAK-P6MDHN6-2JVAETG-JPXXLAR" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="2MDVGTI-EGVOHIO-SPFMLV6-NRFP2EC-HWMLWVO-QVLLTC3-DSQPHMY-JDEIQQ5" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="22UBH4D-HGCP3EW-EHVJTZ4-6PB2HOB-LDPOC3X-ONY6PA6-2DFNBIX-D3X5UQT" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="3OQ6R42-O76KQJM-JYN7EGL-PWKZUPG-276FWG2-CP3CTV3-SM545L3-KTTH3AG" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="3QW353X-J52M5LW-3DUY2Z5-SG5JU7Z-6NSJFAM-74YPGMA-VHL2W3N-5PX2JQO" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="373HSRP-QLPNLIE-JYKZVQF-P4PKZ63-R2ZE6K3-YD442U2-JHBGBQG-WWXAHAU" name="s3" compression="true" introducer="false">
<address>127.0.0.1:22003</address>
</device>
<device id="4IB56JJ-IXYAHRI-6W75NTN-TEIMPRE-25NAG6F-MYGBJBG-JB33DXY-L2WINQK" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="4JSTGIZ-Q2UJXIG-V6QT62A-FSRBKLF-J6FWGIY-PFIAFC3-7GP7DOY-RVNGKQU" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="5BKVB65-I2E5UCX-DWWPLNP-IQ7VRZY-I3P42KF-ZKAF7XO-TQTCLVU-TH45GA3" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="5OOL3EO-A5DTJ5N-CZHPYVP-YH74NL4-DE3O7AU-ZDFSM6D-KG5IUWA-WJJXBQL" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="5RXIYVZ-NOEGGYD-TY4WQTH-U6FQ5IR-LJTW3P3-HFQBN6C-RC4AB5V-MO44PQV" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="5WGZMYP-FNTWGLG-PQKEB5V-PJ4LEPL-53SRHBR-HHSHHLD-5KFL3P2-W6MTRQY" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="6I5KLVE-VVRXZ55-Z3IQIZH-3FBHKTZ-6MIPAYO-V4QX6K7-7J432VD-T5TY6AU" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="6R2BAIE-VRYZJXA-ZKJT4ZK-BQTXA46-VBWMLNY-PINTWYG-TS4Y6GZ-7Z2KVQG" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="7BT3IUI-RYS757D-YJPBP6U-WPL2ZI6-CMICBLW-UDYDSVT-NG62TB5-PM2O3A6" compression="false" introducer="false">
<address>dynamic</address>
</device>
<device id="7TVCUYX-MLZH6GF-GDLVMJ6-REPXXUD-DCLPXP2-67HS7VR-QTTABOA-4ZXJOQD" compression="false" introducer="false">
<address>dynamic</address>
</device>
<gui enabled="true" tls="false">
<address>127.0.0.1:8082</address>
<apikey>abc123</apikey>