mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-09 23:00:58 +00:00
This adds the ability to have multiple concurrent connections to a single device. This is primarily useful when the network has multiple physical links for aggregated bandwidth. A single connection will never see a higher rate than a single link can give, but multiple connections are load-balanced over multiple links. It is also incidentally useful for older multi-core CPUs, where bandwidth could be limited by the TLS performance of a single CPU core -- using multiple connections achieves concurrency in the required crypto calculations... Co-authored-by: Simon Frei <freisim93@gmail.com> Co-authored-by: tomasz1986 <twilczynski@naver.com> Co-authored-by: bt90 <btom1990@googlemail.com>
This commit is contained in:
parent
38bbdebffa
commit
c6334e61aa
@ -15,6 +15,7 @@ import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/sliceutil"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/storage"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
@ -352,14 +353,7 @@ func expire(addrs []DatabaseAddress, now int64) []DatabaseAddress {
|
||||
i := 0
|
||||
for i < len(addrs) {
|
||||
if addrs[i].Expires < now {
|
||||
// This item is expired. Replace it with the last in the list
|
||||
// (noop if we are at the last item).
|
||||
addrs[i] = addrs[len(addrs)-1]
|
||||
// Wipe the last item of the list to release references to
|
||||
// strings and stuff.
|
||||
addrs[len(addrs)-1] = DatabaseAddress{}
|
||||
// Shorten the slice.
|
||||
addrs = addrs[:len(addrs)-1]
|
||||
addrs = sliceutil.RemoveAndZero(addrs, i)
|
||||
continue
|
||||
}
|
||||
i++
|
||||
|
@ -185,7 +185,7 @@ func TestFilter(t *testing.T) {
|
||||
},
|
||||
{
|
||||
a: []DatabaseAddress{{Address: "a", Expires: 5}, {Address: "b", Expires: 15}, {Address: "c", Expires: 5}, {Address: "d", Expires: 15}, {Address: "e", Expires: 5}},
|
||||
b: []DatabaseAddress{{Address: "d", Expires: 15}, {Address: "b", Expires: 15}}, // gets reordered
|
||||
b: []DatabaseAddress{{Address: "b", Expires: 15}, {Address: "d", Expires: 15}},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -871,6 +871,13 @@
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="connections[deviceCfg.deviceID].connected">
|
||||
<th><span class="fas fa-fw fa-random"></span> <span translate>Number of Connections</span></th>
|
||||
<td class="text-right">
|
||||
<span ng-if="connections[deviceCfg.deviceID].secondary.length">1 + {{connections[deviceCfg.deviceID].secondary.length | alwaysNumber}}</span>
|
||||
<span ng-if="!connections[deviceCfg.deviceID].secondary.length">1</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="deviceCfg.allowedNetworks.length > 0">
|
||||
<th><span class="fas fa-fw fa-filter"></span> <span translate>Allowed Networks</span></th>
|
||||
<td class="text-right">
|
||||
|
@ -137,11 +137,22 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row form-group">
|
||||
<div class="col-md-12">
|
||||
<div class="row">
|
||||
<div class="col-md-6" ng-class="{'has-error': deviceEditor.numConnections.$invalid && deviceEditor.numConnections.$dirty}">
|
||||
<label translate>Connection Management</label>
|
||||
<div class="row">
|
||||
<span class="col-md-8" translate>Number of Connections</span>
|
||||
<div class="col-md-4">
|
||||
<input name="numConnections" id="numConnections" class="form-control" type="number" pattern="\d+" ng-model="currentDevice.numConnections" min="0" />
|
||||
</div>
|
||||
</div>
|
||||
<p class="help-block" ng-if="!deviceEditor.numConnections.$valid && deviceEditor.numConnections.$dirty" translate>The number of connections must be a non-negative number.</p>
|
||||
<p class="help-block" ng-if="deviceEditor.numConnections.$valid || deviceEditor.numConnections.$pristine" translate>When set to more than one on both devices, Syncthing will attempt to establish multiple concurrent connections. If the values differ, the highest will be used. Set to zero to let Syncthing decide.</p>
|
||||
</div>
|
||||
<div class="col-md-6 form-group">
|
||||
<label translate>Device rate limits</label>
|
||||
<div class="row">
|
||||
<div class="col-md-6" ng-class="{'has-error': deviceEditor.maxRecvKbps.$invalid && deviceEditor.maxRecvKbps.$dirty}">
|
||||
<div class="col-md-12" ng-class="{'has-error': deviceEditor.maxRecvKbps.$invalid && deviceEditor.maxRecvKbps.$dirty}">
|
||||
<div class="row">
|
||||
<span class="col-md-8" translate>Incoming Rate Limit (KiB/s)</span>
|
||||
<div class="col-md-4">
|
||||
@ -150,7 +161,7 @@
|
||||
</div>
|
||||
<p class="help-block" ng-if="!deviceEditor.maxRecvKbps.$valid && deviceEditor.maxRecvKbps.$dirty" translate>The rate limit must be a non-negative number (0: no limit)</p>
|
||||
</div>
|
||||
<div class="col-md-6" ng-class="{'has-error': deviceEditor.maxSendKbps.$invalid && deviceEditor.maxSendKbps.$dirty}">
|
||||
<div class="col-md-12" ng-class="{'has-error': deviceEditor.maxSendKbps.$invalid && deviceEditor.maxSendKbps.$dirty}">
|
||||
<div class="row">
|
||||
<span class="col-md-8" translate>Outgoing Rate Limit (KiB/s)</span>
|
||||
<div class="col-md-4">
|
||||
@ -158,6 +169,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<p class="help-block" ng-if="!deviceEditor.maxSendKbps.$valid && deviceEditor.maxSendKbps.$dirty" translate>The rate limit must be a non-negative number (0: no limit)</p>
|
||||
<p class="help-block" ng-if="(deviceEditor.maxSendKbps.$valid || deviceEditor.maxSendKbps.$pristine) && (deviceEditor.maxRecvKbps.$valid || deviceEditor.maxRecvKbps.$pristine)">The rate limit is applied to the accumulated traffic of all connections to this device.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1230,6 +1230,14 @@ func (s *service) getSupportBundle(w http.ResponseWriter, r *http.Request) {
|
||||
promhttp.Handler().ServeHTTP(wr, &http.Request{Method: http.MethodGet})
|
||||
files = append(files, fileEntry{name: "metrics.txt", data: buf.Bytes()})
|
||||
|
||||
// Connection data as JSON
|
||||
connStats := s.model.ConnectionStats()
|
||||
if connStatsJSON, err := json.MarshalIndent(connStats, "", " "); err != nil {
|
||||
l.Warnln("Support bundle: failed to serialize connection-stats.json.txt", err)
|
||||
} else {
|
||||
files = append(files, fileEntry{name: "connection-stats.json.txt", data: connStatsJSON})
|
||||
}
|
||||
|
||||
// Heap and CPU Proofs as a pprof extension
|
||||
var heapBuffer, cpuBuffer bytes.Buffer
|
||||
filename := fmt.Sprintf("syncthing-heap-%s-%s-%s-%s.pprof", runtime.GOOS, runtime.GOARCH, build.Version, time.Now().Format("150405")) // hhmmss
|
||||
@ -1607,7 +1615,7 @@ func (s *service) getPeerCompletion(w http.ResponseWriter, _ *http.Request) {
|
||||
for _, folder := range s.cfg.Folders() {
|
||||
for _, device := range folder.DeviceIDs() {
|
||||
deviceStr := device.String()
|
||||
if _, ok := s.model.Connection(device); ok {
|
||||
if s.model.ConnectedTo(device) {
|
||||
comp, err := s.model.Completion(device, folder.ID)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
"github.com/syncthing/syncthing/lib/netutil"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/sliceutil"
|
||||
"github.com/syncthing/syncthing/lib/structutil"
|
||||
)
|
||||
|
||||
@ -564,8 +565,7 @@ func ensureNoUntrustedTrustingSharing(f *FolderConfiguration, devices []FolderDe
|
||||
}
|
||||
if devCfg := existingDevices[dev.DeviceID]; devCfg.Untrusted {
|
||||
l.Warnf("Folder %s (%s) is shared in trusted mode with untrusted device %s (%s); unsharing.", f.ID, f.Label, dev.DeviceID.Short(), devCfg.Name)
|
||||
copy(devices[i:], devices[i+1:])
|
||||
devices = devices[:len(devices)-1]
|
||||
devices = sliceutil.RemoveAndZero(devices, i)
|
||||
i--
|
||||
}
|
||||
}
|
||||
@ -601,9 +601,7 @@ func filterURLSchemePrefix(addrs []string, prefix string) []string {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(uri.Scheme, prefix) {
|
||||
// Remove this entry
|
||||
copy(addrs[i:], addrs[i+1:])
|
||||
addrs = addrs[:len(addrs)-1]
|
||||
addrs = sliceutil.RemoveAndZero(addrs, i)
|
||||
i--
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
const defaultNumConnections = 1 // number of connections to use by default; may change in the future.
|
||||
|
||||
func (cfg DeviceConfiguration) Copy() DeviceConfiguration {
|
||||
c := cfg
|
||||
c.Addresses = make([]string, len(cfg.Addresses))
|
||||
@ -49,6 +51,17 @@ func (cfg *DeviceConfiguration) prepare(sharedFolders []string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *DeviceConfiguration) NumConnections() int {
|
||||
switch {
|
||||
case cfg.RawNumConnections == 0:
|
||||
return defaultNumConnections
|
||||
case cfg.RawNumConnections < 0:
|
||||
return 1
|
||||
default:
|
||||
return cfg.RawNumConnections
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *DeviceConfiguration) IgnoredFolder(folder string) bool {
|
||||
for _, ignoredFolder := range cfg.IgnoredFolders {
|
||||
if ignoredFolder.ID == folder {
|
||||
|
@ -28,7 +28,7 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||
type DeviceConfiguration struct {
|
||||
DeviceID github_com_syncthing_syncthing_lib_protocol.DeviceID `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3,customtype=github.com/syncthing/syncthing/lib/protocol.DeviceID" json:"deviceID" xml:"id,attr" nodefault:"true"`
|
||||
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name" xml:"name,attr,omitempty"`
|
||||
Addresses []string `protobuf:"bytes,3,rep,name=addresses,proto3" json:"addresses" xml:"address,omitempty" default:"dynamic"`
|
||||
Addresses []string `protobuf:"bytes,3,rep,name=addresses,proto3" json:"addresses" xml:"address,omitempty"`
|
||||
Compression protocol.Compression `protobuf:"varint,4,opt,name=compression,proto3,enum=protocol.Compression" json:"compression" xml:"compression,attr"`
|
||||
CertName string `protobuf:"bytes,5,opt,name=cert_name,json=certName,proto3" json:"certName" xml:"certName,attr,omitempty"`
|
||||
Introducer bool `protobuf:"varint,6,opt,name=introducer,proto3" json:"introducer" xml:"introducer,attr"`
|
||||
@ -44,6 +44,7 @@ type DeviceConfiguration struct {
|
||||
MaxRequestKiB int `protobuf:"varint,16,opt,name=max_request_kib,json=maxRequestKib,proto3,casttype=int" json:"maxRequestKiB" xml:"maxRequestKiB"`
|
||||
Untrusted bool `protobuf:"varint,17,opt,name=untrusted,proto3" json:"untrusted" xml:"untrusted"`
|
||||
RemoteGUIPort int `protobuf:"varint,18,opt,name=remote_gui_port,json=remoteGuiPort,proto3,casttype=int" json:"remoteGUIPort" xml:"remoteGUIPort"`
|
||||
RawNumConnections int `protobuf:"varint,19,opt,name=num_connections,json=numConnections,proto3,casttype=int" json:"numConnections" xml:"numConnections"`
|
||||
}
|
||||
|
||||
func (m *DeviceConfiguration) Reset() { *m = DeviceConfiguration{} }
|
||||
@ -88,72 +89,74 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptor_744b782bd13071dd = []byte{
|
||||
// 1026 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x55, 0xbf, 0x6f, 0xdb, 0x46,
|
||||
0x18, 0x15, 0xeb, 0xc4, 0xb6, 0xce, 0x3f, 0x64, 0xd3, 0x88, 0xc3, 0x18, 0x88, 0x4e, 0x50, 0x35,
|
||||
0x28, 0x68, 0x22, 0x17, 0x6e, 0x27, 0xa3, 0x2d, 0x50, 0xc6, 0x68, 0x63, 0x18, 0x4d, 0x5c, 0x16,
|
||||
0x5d, 0xbc, 0xb0, 0x24, 0xef, 0xac, 0x1c, 0x2c, 0xf2, 0x58, 0xf2, 0xa8, 0x58, 0x40, 0xff, 0x80,
|
||||
0x76, 0x2b, 0x02, 0x74, 0xea, 0x92, 0xf6, 0xdf, 0xe8, 0xd0, 0xd5, 0x9b, 0x35, 0x16, 0x1d, 0x0e,
|
||||
0x88, 0xbd, 0x71, 0x29, 0xc0, 0x31, 0x53, 0x71, 0x77, 0x14, 0x45, 0xca, 0x51, 0x50, 0xa0, 0x1b,
|
||||
0xef, 0xbd, 0x77, 0xef, 0xdd, 0xf7, 0xe9, 0xbb, 0x13, 0xe8, 0x0c, 0x88, 0xbb, 0xeb, 0xd1, 0xe0,
|
||||
0x94, 0xf4, 0x77, 0x11, 0x1e, 0x12, 0x0f, 0xab, 0x45, 0x12, 0x39, 0x8c, 0xd0, 0xa0, 0x17, 0x46,
|
||||
0x94, 0x51, 0x7d, 0x51, 0x81, 0x3b, 0xdb, 0x42, 0x2d, 0x21, 0x8f, 0x0e, 0x76, 0x5d, 0x1c, 0x2a,
|
||||
0x7e, 0xe7, 0x5e, 0xc9, 0x85, 0xba, 0x31, 0x8e, 0x86, 0x18, 0xe5, 0x54, 0x1d, 0x9f, 0x33, 0xf5,
|
||||
0xd9, 0xfe, 0x67, 0x03, 0x6c, 0x1d, 0xc8, 0x8c, 0xc7, 0xe5, 0x0c, 0xfd, 0x4f, 0x0d, 0xd4, 0x55,
|
||||
0xb6, 0x4d, 0x90, 0xa1, 0xb5, 0xb4, 0xee, 0xaa, 0xf9, 0x9b, 0x76, 0xc1, 0x61, 0xed, 0x6f, 0x0e,
|
||||
0x3f, 0xee, 0x13, 0xf6, 0x3c, 0x71, 0x7b, 0x1e, 0xf5, 0x77, 0xe3, 0x51, 0xe0, 0xb1, 0xe7, 0x24,
|
||||
0xe8, 0x97, 0xbe, 0xca, 0x27, 0xea, 0x29, 0xf7, 0xc3, 0x83, 0x2b, 0x0e, 0x97, 0x27, 0xdf, 0x29,
|
||||
0x87, 0xcb, 0x28, 0xff, 0xce, 0x38, 0x6c, 0x9e, 0xfb, 0x83, 0xfd, 0x36, 0x41, 0x0f, 0x1d, 0xc6,
|
||||
0xa2, 0x76, 0x2b, 0xa0, 0x08, 0x9f, 0x3a, 0xc9, 0x80, 0xed, 0xb7, 0x59, 0x94, 0xe0, 0x76, 0x7a,
|
||||
0xd9, 0x59, 0xca, 0xc9, 0xec, 0xb2, 0x53, 0x6c, 0xfc, 0x71, 0xdc, 0xd1, 0x5e, 0x8e, 0x3b, 0x85,
|
||||
0xe9, 0xab, 0x71, 0x47, 0xb3, 0x26, 0x2c, 0xd2, 0x8f, 0xc1, 0xad, 0xc0, 0xf1, 0xb1, 0xf1, 0x5e,
|
||||
0x4b, 0xeb, 0xd6, 0xcd, 0x4f, 0x52, 0x0e, 0xe5, 0x3a, 0xe3, 0xf0, 0x9e, 0x8c, 0x13, 0x0b, 0xe9,
|
||||
0xf9, 0x90, 0xfa, 0x84, 0x61, 0x3f, 0x64, 0x23, 0x91, 0xb4, 0xf5, 0x16, 0xdc, 0x92, 0x3b, 0xf5,
|
||||
0x73, 0x50, 0x77, 0x10, 0x8a, 0x70, 0x1c, 0xe3, 0xd8, 0x58, 0x68, 0x2d, 0x74, 0xeb, 0xe6, 0x49,
|
||||
0xca, 0xe1, 0x14, 0xcc, 0x38, 0x7c, 0x20, 0xbd, 0x73, 0xa4, 0xe4, 0xdc, 0x2a, 0x4a, 0x42, 0xa3,
|
||||
0xc0, 0xf1, 0x89, 0x27, 0xb2, 0x36, 0x6f, 0xe8, 0xde, 0x5c, 0x76, 0x96, 0x72, 0x81, 0x35, 0xf5,
|
||||
0xd5, 0x87, 0x60, 0xc5, 0xa3, 0x7e, 0x28, 0x56, 0x84, 0x06, 0xc6, 0xad, 0x96, 0xd6, 0x5d, 0xdf,
|
||||
0xbb, 0xd3, 0x2b, 0x7a, 0xfc, 0x78, 0x4a, 0x9a, 0x9f, 0xa6, 0x1c, 0x96, 0xd5, 0x19, 0x87, 0xdb,
|
||||
0xf2, 0x50, 0x25, 0x4c, 0x35, 0x3a, 0xbd, 0xec, 0x6c, 0xcc, 0x82, 0x56, 0x79, 0xab, 0x8e, 0x41,
|
||||
0xdd, 0xc3, 0x11, 0xb3, 0x65, 0x23, 0x6f, 0xcb, 0x46, 0x3e, 0x11, 0xbf, 0x9d, 0x00, 0x9f, 0xaa,
|
||||
0x66, 0xde, 0x57, 0xde, 0x39, 0xf0, 0x96, 0x86, 0xde, 0x9d, 0xc3, 0x59, 0x85, 0x8b, 0x7e, 0x02,
|
||||
0x00, 0x09, 0x58, 0x44, 0x51, 0xe2, 0xe1, 0xc8, 0x58, 0x6c, 0x69, 0xdd, 0x65, 0x73, 0x3f, 0xe5,
|
||||
0xb0, 0x84, 0x66, 0x1c, 0xde, 0x51, 0x53, 0x52, 0x40, 0x45, 0x11, 0x8d, 0x19, 0xcc, 0x2a, 0xed,
|
||||
0xd3, 0x7f, 0xd7, 0xc0, 0x4e, 0x7c, 0x46, 0x42, 0x7b, 0x82, 0x89, 0xf1, 0xb6, 0x23, 0xec, 0xd3,
|
||||
0xa1, 0x33, 0x88, 0x8d, 0x25, 0x19, 0x86, 0x52, 0x0e, 0x0d, 0xa1, 0x3a, 0x2c, 0x89, 0xac, 0x5c,
|
||||
0x93, 0x71, 0xf8, 0xbe, 0x8c, 0x9e, 0x27, 0x28, 0x0e, 0x72, 0xff, 0x9d, 0x0a, 0x6b, 0x6e, 0x82,
|
||||
0xfe, 0x87, 0x06, 0xd6, 0x8a, 0x33, 0x23, 0xdb, 0x1d, 0x19, 0xcb, 0xf2, 0xc6, 0xfd, 0xf2, 0xbf,
|
||||
0x6e, 0x5c, 0xca, 0xe1, 0xea, 0xd4, 0xd5, 0x1c, 0x65, 0x1c, 0x76, 0xab, 0x3d, 0x44, 0xe6, 0x68,
|
||||
0xfe, 0x9d, 0xdb, 0xbc, 0x21, 0x13, 0x37, 0x4e, 0xde, 0xb2, 0x8a, 0xad, 0xbe, 0x07, 0x16, 0x43,
|
||||
0x27, 0x89, 0x31, 0x32, 0xea, 0xb2, 0x9b, 0x3b, 0x29, 0x87, 0x39, 0x92, 0x71, 0xb8, 0x2a, 0x23,
|
||||
0xd5, 0xb2, 0x6d, 0xe5, 0xb8, 0xfe, 0x03, 0xd8, 0x70, 0x06, 0x03, 0xfa, 0x02, 0x23, 0x3b, 0xc0,
|
||||
0xec, 0x05, 0x8d, 0xce, 0x62, 0x03, 0xc8, 0x2b, 0xf5, 0x75, 0xca, 0x61, 0x23, 0xe7, 0x9e, 0xe6,
|
||||
0x54, 0xf1, 0x46, 0x54, 0xf1, 0xea, 0xa0, 0x19, 0xf3, 0x48, 0x6b, 0xd6, 0x4e, 0xff, 0x0e, 0x6c,
|
||||
0x39, 0x09, 0xa3, 0xb6, 0xe3, 0x79, 0x38, 0x64, 0xf6, 0x29, 0x1d, 0x20, 0x1c, 0xc5, 0xc6, 0x8a,
|
||||
0x3c, 0xfe, 0x87, 0x29, 0x87, 0x9b, 0x82, 0xfe, 0x5c, 0xb2, 0x5f, 0x28, 0x32, 0xe3, 0xf0, 0xae,
|
||||
0x3a, 0xc2, 0x2c, 0xd3, 0xb6, 0x6e, 0xaa, 0xf5, 0x67, 0x60, 0xcd, 0x77, 0xce, 0xed, 0x18, 0x07,
|
||||
0xc8, 0x3e, 0x73, 0xc3, 0xd8, 0x58, 0x6d, 0x69, 0xdd, 0xdb, 0xe6, 0x07, 0xe2, 0x72, 0xfa, 0xce,
|
||||
0xf9, 0x37, 0x38, 0x40, 0x47, 0x6e, 0x28, 0x5c, 0x37, 0xa5, 0x6b, 0x09, 0x6b, 0xbf, 0xe1, 0x70,
|
||||
0x81, 0x04, 0xcc, 0x2a, 0x0b, 0x27, 0x86, 0x11, 0xf6, 0x86, 0xca, 0x70, 0xad, 0x62, 0x68, 0x61,
|
||||
0x6f, 0x38, 0x6b, 0x38, 0xc1, 0x2a, 0x86, 0x13, 0x50, 0x0f, 0x40, 0x83, 0xf4, 0x03, 0x1a, 0x61,
|
||||
0x54, 0xd4, 0xbf, 0xde, 0x5a, 0xe8, 0xae, 0xec, 0x6d, 0xf7, 0xd4, 0xbf, 0x46, 0xef, 0x59, 0xfe,
|
||||
0xaf, 0xa1, 0x6a, 0x32, 0x1f, 0x89, 0x59, 0x4c, 0x39, 0x5c, 0xcf, 0xb7, 0x4d, 0x1b, 0xb3, 0xa5,
|
||||
0xa6, 0xaa, 0x0c, 0xb7, 0xad, 0x19, 0x99, 0xfe, 0x93, 0x06, 0x1a, 0x21, 0x0e, 0x10, 0x09, 0xfa,
|
||||
0x45, 0x60, 0xe3, 0x9d, 0x81, 0x4f, 0x44, 0xe0, 0x15, 0x87, 0xc6, 0x01, 0x0e, 0x23, 0xec, 0x39,
|
||||
0x0c, 0xa3, 0x63, 0x65, 0x90, 0x7b, 0xa6, 0x1c, 0x6a, 0x8f, 0x8a, 0x37, 0x28, 0x2c, 0x73, 0xa5,
|
||||
0xd1, 0x30, 0x34, 0x6b, 0xbd, 0xc2, 0xc5, 0xfa, 0xaf, 0x1a, 0x68, 0xa8, 0x6e, 0x7e, 0x9f, 0xe0,
|
||||
0x98, 0xd9, 0x67, 0xc4, 0x35, 0x36, 0x64, 0x3f, 0xe3, 0x2b, 0x0e, 0xd7, 0xbe, 0x12, 0x6d, 0x92,
|
||||
0xcc, 0x11, 0x31, 0x53, 0x0e, 0xd7, 0xfc, 0x32, 0x50, 0x14, 0x5c, 0x41, 0x27, 0x4d, 0x4e, 0x2f,
|
||||
0x3b, 0x33, 0xf2, 0x59, 0xe0, 0xe5, 0xb8, 0x53, 0x4d, 0xb0, 0x2a, 0xbc, 0xab, 0x7f, 0x06, 0xea,
|
||||
0x49, 0xc0, 0xa2, 0x24, 0x66, 0x18, 0x19, 0x9b, 0x72, 0x26, 0x5b, 0xe2, 0x7f, 0xa6, 0x00, 0x33,
|
||||
0x0e, 0x1b, 0xf2, 0x04, 0x05, 0xd2, 0xb6, 0xa6, 0xac, 0xac, 0x4e, 0x3c, 0x70, 0x0c, 0xdb, 0xfd,
|
||||
0x84, 0xd8, 0x21, 0x8d, 0x98, 0xa1, 0x4f, 0xab, 0xb3, 0x24, 0xf5, 0xe5, 0xb7, 0x87, 0xc7, 0x34,
|
||||
0x62, 0xa2, 0xba, 0xa8, 0x0c, 0x14, 0xd5, 0x55, 0xd0, 0x72, 0x75, 0x55, 0xf9, 0x2c, 0x20, 0xaa,
|
||||
0xab, 0x24, 0x58, 0x13, 0x3e, 0x21, 0x62, 0x69, 0x1e, 0x5d, 0xbc, 0x6e, 0xd6, 0xc6, 0xaf, 0x9b,
|
||||
0xb5, 0x8b, 0xab, 0xa6, 0x36, 0xbe, 0x6a, 0x6a, 0x3f, 0x5f, 0x37, 0x6b, 0xaf, 0xae, 0x9b, 0xda,
|
||||
0xf8, 0xba, 0x59, 0xfb, 0xeb, 0xba, 0x59, 0x3b, 0x79, 0xf0, 0x1f, 0x1e, 0x3b, 0x35, 0x31, 0xee,
|
||||
0xa2, 0x7c, 0xf4, 0x3e, 0xfa, 0x37, 0x00, 0x00, 0xff, 0xff, 0xbf, 0x4a, 0x4f, 0x60, 0x33, 0x09,
|
||||
0x00, 0x00,
|
||||
// 1057 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x55, 0x41, 0x6f, 0xe3, 0x44,
|
||||
0x14, 0x8e, 0xe9, 0x6e, 0xb7, 0x99, 0x6d, 0x9b, 0xc6, 0x65, 0xbb, 0xde, 0x4a, 0x9b, 0x89, 0x42,
|
||||
0x0e, 0x41, 0xec, 0xa6, 0xa8, 0x70, 0xaa, 0x00, 0x89, 0xb4, 0x82, 0xad, 0x2a, 0xba, 0x65, 0x10,
|
||||
0x97, 0xdd, 0x83, 0x71, 0x3c, 0xd3, 0xac, 0xd5, 0x78, 0xc6, 0xd8, 0xe3, 0xb4, 0x95, 0x38, 0x72,
|
||||
0x80, 0x1b, 0xaa, 0xc4, 0x89, 0xcb, 0xc2, 0xdf, 0xe0, 0xc0, 0xb5, 0xb7, 0xe6, 0x08, 0x1c, 0x46,
|
||||
0xda, 0xf4, 0xe6, 0xa3, 0x8f, 0x9c, 0xd0, 0x8c, 0x1d, 0xc7, 0x76, 0x37, 0x2b, 0x24, 0x6e, 0x9e,
|
||||
0xef, 0x7b, 0xf3, 0x7d, 0xf3, 0x9e, 0xdf, 0x9b, 0x01, 0xed, 0xa1, 0xd3, 0xdf, 0xb2, 0x19, 0x3d,
|
||||
0x76, 0x06, 0x5b, 0x98, 0x8c, 0x1c, 0x9b, 0x24, 0x8b, 0xd0, 0xb7, 0xb8, 0xc3, 0x68, 0xd7, 0xf3,
|
||||
0x19, 0x67, 0xfa, 0x62, 0x02, 0x6e, 0x6e, 0xc8, 0x68, 0x05, 0xd9, 0x6c, 0xb8, 0xd5, 0x27, 0x5e,
|
||||
0xc2, 0x6f, 0x3e, 0xc8, 0xa9, 0xb0, 0x7e, 0x40, 0xfc, 0x11, 0xc1, 0x29, 0x55, 0x25, 0x67, 0x3c,
|
||||
0xf9, 0x6c, 0xfd, 0x55, 0x07, 0xeb, 0x7b, 0xca, 0x63, 0x37, 0xef, 0xa1, 0xff, 0xa1, 0x81, 0x6a,
|
||||
0xe2, 0x6d, 0x3a, 0xd8, 0xd0, 0x9a, 0x5a, 0x67, 0xb9, 0xf7, 0xab, 0x76, 0x29, 0x60, 0xe5, 0x6f,
|
||||
0x01, 0x3f, 0x1c, 0x38, 0xfc, 0x45, 0xd8, 0xef, 0xda, 0xcc, 0xdd, 0x0a, 0xce, 0xa9, 0xcd, 0x5f,
|
||||
0x38, 0x74, 0x90, 0xfb, 0xca, 0x9f, 0xa8, 0x9b, 0xa8, 0xef, 0xef, 0x4d, 0x04, 0x5c, 0x9a, 0x7e,
|
||||
0x47, 0x02, 0x2e, 0xe1, 0xf4, 0x3b, 0x16, 0xb0, 0x71, 0xe6, 0x0e, 0x77, 0x5a, 0x0e, 0x7e, 0x64,
|
||||
0x71, 0xee, 0xb7, 0x9a, 0x94, 0x61, 0x72, 0x6c, 0x85, 0x43, 0xbe, 0xd3, 0xe2, 0x7e, 0x48, 0x5a,
|
||||
0xd1, 0x55, 0xfb, 0x4e, 0x4a, 0xc6, 0x57, 0xed, 0x6c, 0xe3, 0x0f, 0xe3, 0xb6, 0x76, 0x31, 0x6e,
|
||||
0x67, 0xa2, 0x2f, 0xc7, 0x6d, 0x0d, 0x4d, 0x59, 0xac, 0x1f, 0x81, 0x5b, 0xd4, 0x72, 0x89, 0xf1,
|
||||
0x56, 0x53, 0xeb, 0x54, 0x7b, 0x1f, 0x45, 0x02, 0xaa, 0x75, 0x2c, 0xe0, 0x03, 0x65, 0x27, 0x17,
|
||||
0x4a, 0xf3, 0x11, 0x73, 0x1d, 0x4e, 0x5c, 0x8f, 0x9f, 0x4b, 0xa7, 0xf5, 0xd7, 0xe0, 0x48, 0xed,
|
||||
0xd4, 0x9f, 0x83, 0xaa, 0x85, 0xb1, 0x4f, 0x82, 0x80, 0x04, 0xc6, 0x42, 0x73, 0xa1, 0x53, 0xed,
|
||||
0x7d, 0x1c, 0x09, 0x38, 0x03, 0x63, 0x01, 0xef, 0x2b, 0xed, 0x14, 0x29, 0x2a, 0xd7, 0x6f, 0xa0,
|
||||
0x68, 0xb6, 0x55, 0x1f, 0x81, 0xbb, 0x36, 0x73, 0x3d, 0xb9, 0x72, 0x18, 0x35, 0x6e, 0x35, 0xb5,
|
||||
0xce, 0xea, 0xf6, 0xbd, 0x6e, 0x56, 0xc6, 0xdd, 0x19, 0xa9, 0x5c, 0xf3, 0xd1, 0xb1, 0x80, 0x1b,
|
||||
0xca, 0x37, 0x87, 0x25, 0xb5, 0x8c, 0xae, 0xda, 0x6b, 0x65, 0x10, 0xe5, 0xb7, 0xea, 0x04, 0x54,
|
||||
0x6d, 0xe2, 0x73, 0x53, 0xd5, 0xea, 0xb6, 0xaa, 0xd5, 0x13, 0xf9, 0x7b, 0x24, 0x78, 0x98, 0xd4,
|
||||
0xeb, 0x61, 0xa2, 0x9d, 0x02, 0xaf, 0xa9, 0xd9, 0xfd, 0x39, 0x1c, 0xca, 0x54, 0xf4, 0x67, 0x00,
|
||||
0x38, 0x94, 0xfb, 0x0c, 0x87, 0x36, 0xf1, 0x8d, 0xc5, 0xa6, 0xd6, 0x59, 0xea, 0xed, 0x44, 0x02,
|
||||
0xe6, 0xd0, 0x58, 0xc0, 0x7b, 0x49, 0x23, 0x64, 0x50, 0x96, 0x44, 0xad, 0x84, 0xa1, 0xdc, 0x3e,
|
||||
0xfd, 0x37, 0x0d, 0x6c, 0x06, 0x27, 0x8e, 0x67, 0x4e, 0x31, 0xd9, 0xc1, 0xa6, 0x4f, 0x5c, 0x36,
|
||||
0xb2, 0x86, 0x81, 0x71, 0x47, 0x99, 0xe1, 0x48, 0x40, 0x43, 0x46, 0xed, 0xe7, 0x82, 0x50, 0x1a,
|
||||
0x13, 0x0b, 0xf8, 0x8e, 0xb2, 0x9e, 0x17, 0x90, 0x1d, 0xe4, 0xe1, 0x1b, 0x23, 0xd0, 0x5c, 0x07,
|
||||
0xfd, 0x77, 0x0d, 0xac, 0x64, 0x67, 0xc6, 0x66, 0xff, 0xdc, 0x58, 0x52, 0x43, 0xf5, 0xf3, 0xff,
|
||||
0x1a, 0xaa, 0x48, 0xc0, 0xe5, 0x99, 0x6a, 0xef, 0x3c, 0x16, 0xb0, 0x53, 0xac, 0x21, 0xee, 0x9d,
|
||||
0xcf, 0x1f, 0xab, 0xfa, 0x8d, 0x30, 0x39, 0x54, 0x6a, 0x90, 0x0a, 0xb2, 0xfa, 0x36, 0x58, 0xf4,
|
||||
0xac, 0x30, 0x20, 0xd8, 0xa8, 0xaa, 0x6a, 0x6e, 0x46, 0x02, 0xa6, 0x48, 0x2c, 0xe0, 0xb2, 0xb2,
|
||||
0x4c, 0x96, 0x2d, 0x94, 0xe2, 0xfa, 0x77, 0x60, 0xcd, 0x1a, 0x0e, 0xd9, 0x29, 0xc1, 0x26, 0x25,
|
||||
0xfc, 0x94, 0xf9, 0x27, 0x81, 0x01, 0xd4, 0xd4, 0x7c, 0x19, 0x09, 0x58, 0x4b, 0xb9, 0xc3, 0x94,
|
||||
0xca, 0xae, 0x81, 0x22, 0x5e, 0x6c, 0x34, 0x63, 0x1e, 0x89, 0xca, 0x72, 0xfa, 0x37, 0x60, 0xdd,
|
||||
0x0a, 0x39, 0x33, 0x2d, 0xdb, 0x26, 0x1e, 0x37, 0x8f, 0xd9, 0x10, 0x13, 0x3f, 0x30, 0xee, 0xaa,
|
||||
0xe3, 0xbf, 0x1f, 0x09, 0x58, 0x97, 0xf4, 0xa7, 0x8a, 0xfd, 0x2c, 0x21, 0x67, 0xe3, 0x5b, 0x66,
|
||||
0x5a, 0xe8, 0x66, 0xb4, 0xfe, 0x14, 0xac, 0xb8, 0xd6, 0x99, 0x19, 0x10, 0x8a, 0xcd, 0x93, 0xbe,
|
||||
0x17, 0x18, 0xcb, 0x4d, 0xad, 0x73, 0xbb, 0xf7, 0x9e, 0x1c, 0x4e, 0xd7, 0x3a, 0xfb, 0x8a, 0x50,
|
||||
0x7c, 0xd0, 0xf7, 0xa4, 0x6a, 0x5d, 0xa9, 0xe6, 0xb0, 0xd6, 0x3f, 0x02, 0x2e, 0x38, 0x94, 0xa3,
|
||||
0x7c, 0xe0, 0x54, 0xd0, 0x27, 0xf6, 0x28, 0x11, 0x5c, 0x29, 0x08, 0x22, 0x62, 0x8f, 0xca, 0x82,
|
||||
0x53, 0xac, 0x20, 0x38, 0x05, 0x75, 0x0a, 0x6a, 0xce, 0x80, 0x32, 0x9f, 0xe0, 0x2c, 0xff, 0xd5,
|
||||
0xe6, 0x42, 0xe7, 0xee, 0xf6, 0x46, 0x37, 0x79, 0x18, 0xba, 0x4f, 0xd3, 0x87, 0x21, 0xc9, 0xa9,
|
||||
0xf7, 0x58, 0xf6, 0x62, 0x24, 0xe0, 0x6a, 0xba, 0x6d, 0x56, 0x98, 0xf5, 0xa4, 0xab, 0xf2, 0x70,
|
||||
0x0b, 0x95, 0xc2, 0xf4, 0x1f, 0x35, 0x50, 0xf3, 0x08, 0xc5, 0x0e, 0x1d, 0x64, 0x86, 0xb5, 0x37,
|
||||
0x1a, 0x3e, 0x91, 0x86, 0x13, 0x01, 0x8d, 0x3d, 0xe2, 0xf9, 0xc4, 0xb6, 0x38, 0xc1, 0x47, 0x89,
|
||||
0x40, 0xaa, 0x19, 0x09, 0xa8, 0x3d, 0xce, 0xee, 0x20, 0x2f, 0xcf, 0xe5, 0x5a, 0xc3, 0xd0, 0xd0,
|
||||
0x6a, 0x81, 0x0b, 0xf4, 0x5f, 0x34, 0x50, 0x4b, 0xaa, 0xf9, 0x6d, 0x48, 0x02, 0x6e, 0x9e, 0x38,
|
||||
0x7d, 0x63, 0x4d, 0xd5, 0x33, 0x98, 0x08, 0xb8, 0xf2, 0x85, 0x2c, 0x93, 0x62, 0x0e, 0x9c, 0x5e,
|
||||
0x24, 0xe0, 0x8a, 0x9b, 0x07, 0xb2, 0x84, 0x0b, 0xe8, 0xb4, 0xc8, 0xd1, 0x55, 0xbb, 0x14, 0x5e,
|
||||
0x06, 0x2e, 0xc6, 0xed, 0xa2, 0x03, 0x2a, 0xf0, 0x7d, 0xfd, 0x13, 0x50, 0x0d, 0x29, 0xf7, 0xc3,
|
||||
0x80, 0x13, 0x6c, 0xd4, 0x55, 0x4f, 0x36, 0xe5, 0x53, 0x92, 0x81, 0xb1, 0x80, 0x35, 0x75, 0x82,
|
||||
0x0c, 0x69, 0xa1, 0x19, 0xab, 0xb2, 0x93, 0x17, 0x1c, 0x27, 0xe6, 0x20, 0x74, 0x4c, 0x8f, 0xf9,
|
||||
0xdc, 0xd0, 0x67, 0xd9, 0x21, 0x45, 0x7d, 0xfe, 0xf5, 0xfe, 0x11, 0xf3, 0xb9, 0xcc, 0xce, 0xcf,
|
||||
0x03, 0x59, 0x76, 0x05, 0x34, 0x9f, 0x5d, 0x31, 0xbc, 0x0c, 0xc8, 0xec, 0x0a, 0x0e, 0x68, 0xca,
|
||||
0x87, 0x8e, 0x5c, 0xea, 0xdf, 0x6b, 0xa0, 0x46, 0x43, 0xd7, 0xb4, 0x19, 0xa5, 0x44, 0x5d, 0x83,
|
||||
0x81, 0xb1, 0xae, 0x4e, 0xf7, 0x7c, 0x22, 0x60, 0x1d, 0x59, 0xa7, 0x87, 0xa1, 0xbb, 0x3b, 0x23,
|
||||
0x65, 0xc7, 0xd1, 0x02, 0x12, 0x0b, 0xf8, 0x76, 0xf2, 0x4a, 0x17, 0xe0, 0xe9, 0x19, 0x2f, 0xc6,
|
||||
0xed, 0x9b, 0x2a, 0xa8, 0xa4, 0xd1, 0x3b, 0xb8, 0x7c, 0xd5, 0xa8, 0x8c, 0x5f, 0x35, 0x2a, 0x97,
|
||||
0x93, 0x86, 0x36, 0x9e, 0x34, 0xb4, 0x9f, 0xae, 0x1b, 0x95, 0x97, 0xd7, 0x0d, 0x6d, 0x7c, 0xdd,
|
||||
0xa8, 0xfc, 0x79, 0xdd, 0xa8, 0x3c, 0x7b, 0xf7, 0x3f, 0xdc, 0xb9, 0x49, 0xe3, 0xf6, 0x17, 0xd5,
|
||||
0xdd, 0xfb, 0xc1, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x75, 0x19, 0xf5, 0x92, 0x9d, 0x09, 0x00,
|
||||
0x00,
|
||||
}
|
||||
|
||||
func (m *DeviceConfiguration) Marshal() (dAtA []byte, err error) {
|
||||
@ -176,6 +179,13 @@ func (m *DeviceConfiguration) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.RawNumConnections != 0 {
|
||||
i = encodeVarintDeviceconfiguration(dAtA, i, uint64(m.RawNumConnections))
|
||||
i--
|
||||
dAtA[i] = 0x1
|
||||
i--
|
||||
dAtA[i] = 0x98
|
||||
}
|
||||
if m.RemoteGUIPort != 0 {
|
||||
i = encodeVarintDeviceconfiguration(dAtA, i, uint64(m.RemoteGUIPort))
|
||||
i--
|
||||
@ -423,6 +433,9 @@ func (m *DeviceConfiguration) ProtoSize() (n int) {
|
||||
if m.RemoteGUIPort != 0 {
|
||||
n += 2 + sovDeviceconfiguration(uint64(m.RemoteGUIPort))
|
||||
}
|
||||
if m.RawNumConnections != 0 {
|
||||
n += 2 + sovDeviceconfiguration(uint64(m.RawNumConnections))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@ -918,6 +931,25 @@ func (m *DeviceConfiguration) Unmarshal(dAtA []byte) error {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 19:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field RawNumConnections", wireType)
|
||||
}
|
||||
m.RawNumConnections = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowDeviceconfiguration
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.RawNumConnections |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipDeviceconfiguration(dAtA[iNdEx:])
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/events"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/sliceutil"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
"github.com/thejerf/suture/v4"
|
||||
)
|
||||
@ -198,9 +199,7 @@ func (w *wrapper) Unsubscribe(c Committer) {
|
||||
w.mut.Lock()
|
||||
for i := range w.subs {
|
||||
if w.subs[i] == c {
|
||||
copy(w.subs[i:], w.subs[i+1:])
|
||||
w.subs[len(w.subs)-1] = nil
|
||||
w.subs = w.subs[:len(w.subs)-1]
|
||||
w.subs = sliceutil.RemoveAndZero(w.subs, i)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -91,6 +91,7 @@ func (d *quicDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL
|
||||
if isLocal {
|
||||
priority = d.lanPriority
|
||||
}
|
||||
|
||||
return newInternalConn(&quicTlsConn{session, stream, createdConn}, connTypeQUICClient, isLocal, priority), nil
|
||||
}
|
||||
|
||||
@ -108,9 +109,10 @@ func (quicDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Confi
|
||||
commonDialer: commonDialer{
|
||||
reconnectInterval: time.Duration(quicInterval) * time.Second,
|
||||
tlsCfg: tlsCfg,
|
||||
lanChecker: lanChecker,
|
||||
lanPriority: opts.ConnectionPriorityQUICLAN,
|
||||
wanPriority: opts.ConnectionPriorityQUICWAN,
|
||||
lanChecker: lanChecker,
|
||||
allowsMultiConns: true,
|
||||
},
|
||||
registry: registry,
|
||||
}
|
||||
|
@ -105,7 +105,9 @@ func (t *quicListener) serve(ctx context.Context) error {
|
||||
defer quicTransport.Close()
|
||||
|
||||
svc := stun.New(t.cfg, t, &transportPacketConn{tran: quicTransport}, tracer)
|
||||
go svc.Serve(ctx)
|
||||
stunCtx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
go svc.Serve(stunCtx)
|
||||
|
||||
t.registry.Register(t.uri.Scheme, quicTransport)
|
||||
defer t.registry.Unregister(t.uri.Scheme, quicTransport)
|
||||
|
@ -12,6 +12,7 @@ package registry
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/sliceutil"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
)
|
||||
|
||||
@ -41,9 +42,7 @@ func (r *Registry) Unregister(scheme string, item interface{}) {
|
||||
candidates := r.available[scheme]
|
||||
for i, existingItem := range candidates {
|
||||
if existingItem == item {
|
||||
candidates[i] = candidates[len(candidates)-1]
|
||||
candidates[len(candidates)-1] = nil
|
||||
r.available[scheme] = candidates[:len(candidates)-1]
|
||||
r.available[scheme] = sliceutil.RemoveAndZero(candidates, i)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -11,10 +11,14 @@ package connections
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base32"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"net/url"
|
||||
@ -23,8 +27,10 @@ import (
|
||||
stdsync "sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/exp/constraints"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/build"
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
"github.com/syncthing/syncthing/lib/connections/registry"
|
||||
"github.com/syncthing/syncthing/lib/discover"
|
||||
@ -33,6 +39,7 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/semaphore"
|
||||
"github.com/syncthing/syncthing/lib/sliceutil"
|
||||
"github.com/syncthing/syncthing/lib/stringutil"
|
||||
"github.com/syncthing/syncthing/lib/svcutil"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
@ -66,12 +73,14 @@ var (
|
||||
errDeviceIgnored = errors.New("device is ignored")
|
||||
errConnLimitReached = errors.New("connection limit reached")
|
||||
errDevicePaused = errors.New("device is paused")
|
||||
|
||||
// A connection is being closed to make space for better ones
|
||||
errReplacingConnection = errors.New("replacing connection")
|
||||
)
|
||||
|
||||
const (
|
||||
perDeviceWarningIntv = 15 * time.Minute
|
||||
tlsHandshakeTimeout = 10 * time.Second
|
||||
minConnectionReplaceAge = 10 * time.Second
|
||||
minConnectionLoopSleep = 5 * time.Second
|
||||
stdConnectionLoopSleep = time.Minute
|
||||
worstDialerPriority = math.MaxInt32
|
||||
@ -79,6 +88,7 @@ const (
|
||||
shortLivedConnectionThreshold = 5 * time.Second
|
||||
dialMaxParallel = 64
|
||||
dialMaxParallelPerDevice = 8
|
||||
maxNumConnections = 128 // the maximum number of connections we maintain to any given device
|
||||
)
|
||||
|
||||
// From go/src/crypto/tls/cipher_suites.go
|
||||
@ -150,6 +160,7 @@ type connWithHello struct {
|
||||
type service struct {
|
||||
*suture.Supervisor
|
||||
connectionStatusHandler
|
||||
deviceConnectionTracker
|
||||
|
||||
cfg config.Wrapper
|
||||
myID protocol.DeviceID
|
||||
@ -281,21 +292,43 @@ func (s *service) handleConns(ctx context.Context) error {
|
||||
|
||||
_ = c.SetDeadline(time.Now().Add(20 * time.Second))
|
||||
go func() {
|
||||
hello, err := protocol.ExchangeHello(c, s.model.GetHello(remoteID))
|
||||
// Exchange Hello messages with the peer.
|
||||
outgoing := s.helloForDevice(remoteID)
|
||||
incoming, err := protocol.ExchangeHello(c, outgoing)
|
||||
// The timestamps are used to create the connection ID.
|
||||
c.connectionID = newConnectionID(outgoing.Timestamp, incoming.Timestamp)
|
||||
|
||||
select {
|
||||
case s.hellos <- &connWithHello{c, hello, err, remoteID, remoteCert}:
|
||||
case s.hellos <- &connWithHello{c, incoming, err, remoteID, remoteCert}:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *service) helloForDevice(remoteID protocol.DeviceID) protocol.Hello {
|
||||
hello := protocol.Hello{
|
||||
ClientName: "syncthing",
|
||||
ClientVersion: build.Version,
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
}
|
||||
if cfg, ok := s.cfg.Device(remoteID); ok {
|
||||
hello.NumConnections = cfg.NumConnections()
|
||||
// Set our name (from the config of our device ID) only if we
|
||||
// already know about the other side device ID.
|
||||
if myCfg, ok := s.cfg.Device(s.myID); ok {
|
||||
hello.DeviceName = myCfg.Name
|
||||
}
|
||||
}
|
||||
return hello
|
||||
}
|
||||
|
||||
func (s *service) connectionCheckEarly(remoteID protocol.DeviceID, c internalConn) error {
|
||||
if s.cfg.IgnoredDevice(remoteID) {
|
||||
return errDeviceIgnored
|
||||
}
|
||||
|
||||
if max := s.cfg.Options().ConnectionLimitMax; max > 0 && s.model.NumConnections() >= max {
|
||||
if max := s.cfg.Options().ConnectionLimitMax; max > 0 && s.numConnectedDevices() >= max {
|
||||
// We're not allowed to accept any more connections.
|
||||
return errConnLimitReached
|
||||
}
|
||||
@ -315,31 +348,26 @@ func (s *service) connectionCheckEarly(remoteID protocol.DeviceID, c internalCon
|
||||
return errNetworkNotAllowed
|
||||
}
|
||||
|
||||
// Lower priority is better, just like nice etc.
|
||||
if ct, ok := s.model.Connection(remoteID); ok {
|
||||
if ct.Priority() > c.priority || time.Since(ct.Statistics().StartedAt) > minConnectionReplaceAge {
|
||||
l.Debugf("Switching connections %s (existing: %s new: %s)", remoteID, ct, c)
|
||||
} else {
|
||||
// We should not already be connected to the other party. TODO: This
|
||||
// could use some better handling. If the old connection is dead but
|
||||
// hasn't timed out yet we may want to drop *that* connection and keep
|
||||
// this one. But in case we are two devices connecting to each other
|
||||
// in parallel we don't want to do that or we end up with no
|
||||
// connections still established...
|
||||
return errDeviceAlreadyConnected
|
||||
}
|
||||
currentConns := s.numConnectionsForDevice(cfg.DeviceID)
|
||||
desiredConns := s.desiredConnectionsToDevice(cfg.DeviceID)
|
||||
worstPrio := s.worstConnectionPriority(remoteID)
|
||||
ourUpgradeThreshold := c.priority + s.cfg.Options().ConnectionPriorityUpgradeThreshold
|
||||
if currentConns >= desiredConns && ourUpgradeThreshold >= worstPrio {
|
||||
l.Debugf("Not accepting connection to %s at %s: already have %d connections, desire %d", remoteID, c, currentConns, desiredConns)
|
||||
return errDeviceAlreadyConnected
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *service) handleHellos(ctx context.Context) error {
|
||||
var c internalConn
|
||||
var hello protocol.Hello
|
||||
var err error
|
||||
var remoteID protocol.DeviceID
|
||||
var remoteCert *x509.Certificate
|
||||
for {
|
||||
var c internalConn
|
||||
var hello protocol.Hello
|
||||
var err error
|
||||
var remoteID protocol.DeviceID
|
||||
var remoteCert *x509.Certificate
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
@ -416,15 +444,17 @@ func (s *service) handleHellos(ctx context.Context) error {
|
||||
rd, wr := s.limiter.getLimiters(remoteID, c, c.IsLocal())
|
||||
|
||||
protoConn := protocol.NewConnection(remoteID, rd, wr, c, s.model, c, deviceCfg.Compression, s.cfg.FolderPasswords(remoteID), s.keyGen)
|
||||
s.accountAddedConnection(protoConn, hello, s.cfg.Options().ConnectionPriorityUpgradeThreshold)
|
||||
go func() {
|
||||
<-protoConn.Closed()
|
||||
s.accountRemovedConnection(protoConn)
|
||||
s.dialNowDevicesMut.Lock()
|
||||
s.dialNowDevices[remoteID] = struct{}{}
|
||||
s.scheduleDialNow()
|
||||
s.dialNowDevicesMut.Unlock()
|
||||
}()
|
||||
|
||||
l.Infof("Established secure connection to %s at %s", remoteID, c)
|
||||
l.Infof("Established secure connection to %s at %s", remoteID.Short(), c)
|
||||
|
||||
s.model.AddConnection(protoConn, hello)
|
||||
continue
|
||||
@ -518,7 +548,7 @@ func (s *service) dialDevices(ctx context.Context, now time.Time, cfg config.Con
|
||||
allowAdditional := 0 // no limit
|
||||
connectionLimit := cfg.Options.LowestConnectionLimit()
|
||||
if connectionLimit > 0 {
|
||||
current := s.model.NumConnections()
|
||||
current := s.numConnectedDevices()
|
||||
allowAdditional = connectionLimit - current
|
||||
if allowAdditional <= 0 {
|
||||
l.Debugf("Skipping dial because we've reached the connection limit, current %d >= limit %d", current, connectionLimit)
|
||||
@ -545,19 +575,20 @@ func (s *service) dialDevices(ctx context.Context, now time.Time, cfg config.Con
|
||||
// See if we are already connected and, if so, what our cutoff is
|
||||
// for dialer priority.
|
||||
priorityCutoff := worstDialerPriority
|
||||
connection, connected := s.model.Connection(deviceCfg.DeviceID)
|
||||
if connected {
|
||||
if currentConns := s.numConnectionsForDevice(deviceCfg.DeviceID); currentConns > 0 {
|
||||
// Set the priority cutoff to the current connection's priority,
|
||||
// so that we don't attempt any dialers with worse priority.
|
||||
priorityCutoff = connection.Priority()
|
||||
priorityCutoff = s.worstConnectionPriority(deviceCfg.DeviceID)
|
||||
|
||||
// Reduce the priority cutoff by the upgrade threshold, so that
|
||||
// we don't attempt dialers that aren't considered a worthy upgrade.
|
||||
priorityCutoff -= cfg.Options.ConnectionPriorityUpgradeThreshold
|
||||
|
||||
if bestDialerPriority >= priorityCutoff {
|
||||
if bestDialerPriority >= priorityCutoff && currentConns >= s.desiredConnectionsToDevice(deviceCfg.DeviceID) {
|
||||
// Our best dialer is not any better than what we already
|
||||
// have, so nothing to do here.
|
||||
// have, and we already have the desired number of
|
||||
// connections to this device,so nothing to do here.
|
||||
l.Debugf("Skipping dial to %s because we already have %d connections and our best dialer is not better than %d", deviceCfg.DeviceID.Short(), currentConns, priorityCutoff)
|
||||
continue
|
||||
}
|
||||
}
|
||||
@ -625,14 +656,14 @@ func (s *service) resolveDialTargets(ctx context.Context, now time.Time, cfg con
|
||||
deviceID := deviceCfg.DeviceID
|
||||
|
||||
addrs := s.resolveDeviceAddrs(ctx, deviceCfg)
|
||||
l.Debugln("Resolved device", deviceID, "addresses:", addrs)
|
||||
l.Debugln("Resolved device", deviceID.Short(), "addresses:", addrs)
|
||||
|
||||
dialTargets := make([]dialTarget, 0, len(addrs))
|
||||
for _, addr := range addrs {
|
||||
// Use both device and address, as you might have two devices connected
|
||||
// to the same relay
|
||||
if !initial && nextDialAt.get(deviceID, addr).After(now) {
|
||||
l.Debugf("Not dialing %s via %v as it's not time yet", deviceID, addr)
|
||||
l.Debugf("Not dialing %s via %v as it's not time yet", deviceID.Short(), addr)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -669,8 +700,17 @@ func (s *service) resolveDialTargets(ctx context.Context, now time.Time, cfg con
|
||||
|
||||
dialer := dialerFactory.New(s.cfg.Options(), s.tlsCfg, s.registry, s.lanChecker)
|
||||
priority := dialer.Priority(uri.Host)
|
||||
if priority >= priorityCutoff {
|
||||
l.Debugf("Not dialing using %s as priority is not better than current connection (%d >= %d)", dialerFactory, priority, priorityCutoff)
|
||||
currentConns := s.numConnectionsForDevice(deviceCfg.DeviceID)
|
||||
if priority > priorityCutoff {
|
||||
l.Debugf("Not dialing %s at %s using %s as priority is worse than current connection (%d > %d)", deviceID.Short(), addr, dialerFactory, priority, priorityCutoff)
|
||||
continue
|
||||
}
|
||||
if currentConns > 0 && !dialer.AllowsMultiConns() {
|
||||
l.Debugf("Not dialing %s at %s using %s as it does not allow multiple connections and we already have a connection", deviceID.Short(), addr, dialerFactory)
|
||||
continue
|
||||
}
|
||||
if currentConns >= s.desiredConnectionsToDevice(deviceCfg.DeviceID) && priority == priorityCutoff {
|
||||
l.Debugf("Not dialing %s at %s using %s as priority is equal and we already have %d/%d connections", deviceID.Short(), addr, dialerFactory, currentConns, deviceCfg.NumConnections)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -1272,3 +1312,165 @@ func (r nextDialRegistry) sleepDurationAndCleanup(now time.Time) time.Duration {
|
||||
}
|
||||
return sleep
|
||||
}
|
||||
|
||||
func (s *service) desiredConnectionsToDevice(deviceID protocol.DeviceID) int {
|
||||
cfg, ok := s.cfg.Device(deviceID)
|
||||
if !ok {
|
||||
// We want no connections to an unknown device.
|
||||
return 0
|
||||
}
|
||||
|
||||
otherSide := s.wantConnectionsForDevice(deviceID)
|
||||
thisSide := cfg.NumConnections()
|
||||
switch {
|
||||
case otherSide <= 0:
|
||||
// The other side doesn't support multiple connections, or we
|
||||
// haven't yet connected to them so we don't know what they support
|
||||
// or not. Use a single connection until we know better.
|
||||
return 1
|
||||
|
||||
case otherSide == 1:
|
||||
// The other side supports multiple connections, but only wants
|
||||
// one. We should honour that.
|
||||
return 1
|
||||
|
||||
case thisSide == 1:
|
||||
// We want only one connection, so we should honour that.
|
||||
return 1
|
||||
|
||||
// Finally, we allow negotiation and use the higher of the two values,
|
||||
// while keeping at or below the max allowed value.
|
||||
default:
|
||||
return min(max(thisSide, otherSide), maxNumConnections)
|
||||
}
|
||||
}
|
||||
|
||||
// The deviceConnectionTracker keeps track of how many devices we are
|
||||
// connected to and how many connections we have to each device. It also
|
||||
// tracks how many connections they are willing to use.
|
||||
type deviceConnectionTracker struct {
|
||||
connectionsMut stdsync.Mutex
|
||||
connections map[protocol.DeviceID][]protocol.Connection // current connections
|
||||
wantConnections map[protocol.DeviceID]int // number of connections they want
|
||||
}
|
||||
|
||||
func (c *deviceConnectionTracker) accountAddedConnection(conn protocol.Connection, h protocol.Hello, upgradeThreshold int) {
|
||||
c.connectionsMut.Lock()
|
||||
defer c.connectionsMut.Unlock()
|
||||
// Lazily initialize the maps
|
||||
if c.connections == nil {
|
||||
c.connections = make(map[protocol.DeviceID][]protocol.Connection)
|
||||
c.wantConnections = make(map[protocol.DeviceID]int)
|
||||
}
|
||||
// Add the connection to the list of current connections and remember
|
||||
// how many total connections they want
|
||||
d := conn.DeviceID()
|
||||
c.connections[d] = append(c.connections[d], conn)
|
||||
c.wantConnections[d] = int(h.NumConnections)
|
||||
l.Debugf("Added connection for %s (now %d), they want %d connections", d.Short(), len(c.connections[d]), h.NumConnections)
|
||||
|
||||
// Close any connections we no longer want to retain.
|
||||
c.closeWorsePriorityConnectionsLocked(d, conn.Priority()-upgradeThreshold)
|
||||
}
|
||||
|
||||
func (c *deviceConnectionTracker) accountRemovedConnection(conn protocol.Connection) {
|
||||
c.connectionsMut.Lock()
|
||||
defer c.connectionsMut.Unlock()
|
||||
d := conn.DeviceID()
|
||||
cid := conn.ConnectionID()
|
||||
// Remove the connection from the list of current connections
|
||||
for i, conn := range c.connections[d] {
|
||||
if conn.ConnectionID() == cid {
|
||||
c.connections[d] = sliceutil.RemoveAndZero(c.connections[d], i)
|
||||
break
|
||||
}
|
||||
}
|
||||
// Clean up if required
|
||||
if len(c.connections[d]) == 0 {
|
||||
delete(c.connections, d)
|
||||
delete(c.wantConnections, d)
|
||||
}
|
||||
l.Debugf("Removed connection for %s (now %d)", d.Short(), c.connections[d])
|
||||
}
|
||||
|
||||
func (c *deviceConnectionTracker) numConnectionsForDevice(d protocol.DeviceID) int {
|
||||
c.connectionsMut.Lock()
|
||||
defer c.connectionsMut.Unlock()
|
||||
return len(c.connections[d])
|
||||
}
|
||||
|
||||
func (c *deviceConnectionTracker) wantConnectionsForDevice(d protocol.DeviceID) int {
|
||||
c.connectionsMut.Lock()
|
||||
defer c.connectionsMut.Unlock()
|
||||
return c.wantConnections[d]
|
||||
}
|
||||
|
||||
func (c *deviceConnectionTracker) numConnectedDevices() int {
|
||||
c.connectionsMut.Lock()
|
||||
defer c.connectionsMut.Unlock()
|
||||
return len(c.connections)
|
||||
}
|
||||
|
||||
func (c *deviceConnectionTracker) worstConnectionPriority(d protocol.DeviceID) int {
|
||||
c.connectionsMut.Lock()
|
||||
defer c.connectionsMut.Unlock()
|
||||
if len(c.connections[d]) == 0 {
|
||||
return math.MaxInt // worst possible priority
|
||||
}
|
||||
worstPriority := c.connections[d][0].Priority()
|
||||
for _, conn := range c.connections[d][1:] {
|
||||
if p := conn.Priority(); p > worstPriority {
|
||||
worstPriority = p
|
||||
}
|
||||
}
|
||||
return worstPriority
|
||||
}
|
||||
|
||||
// closeWorsePriorityConnectionsLocked closes all connections to the given
|
||||
// device that are worse than the cutoff priority. Must be called with the
|
||||
// lock held.
|
||||
func (c *deviceConnectionTracker) closeWorsePriorityConnectionsLocked(d protocol.DeviceID, cutoff int) {
|
||||
for _, conn := range c.connections[d] {
|
||||
if p := conn.Priority(); p > cutoff {
|
||||
l.Debugf("Closing connection %s to %s with priority %d (cutoff %d)", conn, d.Short(), p, cutoff)
|
||||
go conn.Close(errReplacingConnection)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// newConnectionID generates a connection ID. The connection ID is designed
|
||||
// to be unique for each connection and chronologically sortable. It is
|
||||
// based on the sum of two timestamps: when we think the connection was
|
||||
// started, and when the other side thinks the connection was started. We
|
||||
// then add some random data for good measure. This way, even if the other
|
||||
// side does some funny business with the timestamp, we will get no worse
|
||||
// than random connection IDs.
|
||||
func newConnectionID(t0, t1 int64) string {
|
||||
var buf [16]byte // 8 bytes timestamp, 8 bytes random
|
||||
binary.BigEndian.PutUint64(buf[:], uint64(t0+t1))
|
||||
_, _ = io.ReadFull(rand.Reader, buf[8:])
|
||||
enc := base32.HexEncoding.WithPadding(base32.NoPadding)
|
||||
// We encode the two parts separately and concatenate the results. The
|
||||
// reason for this is that the timestamp (64 bits) doesn't precisely
|
||||
// align to the base32 encoding (5 bits per character), so we'd get a
|
||||
// character in the middle that is a mix of bits from the timestamp and
|
||||
// from the random. We want the timestamp part deterministic.
|
||||
return enc.EncodeToString(buf[:8]) + enc.EncodeToString(buf[8:])
|
||||
}
|
||||
|
||||
// temporary implementations of min and max, to be removed once we can use
|
||||
// Go 1.21 builtins. :)
|
||||
|
||||
func min[T constraints.Ordered](a, b T) T {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func max[T constraints.Ordered](a, b T) T {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ type internalConn struct {
|
||||
isLocal bool
|
||||
priority int
|
||||
establishedAt time.Time
|
||||
connectionID string // set after Hello exchange
|
||||
}
|
||||
|
||||
type connType int
|
||||
@ -88,12 +89,13 @@ func (t connType) Transport() string {
|
||||
}
|
||||
|
||||
func newInternalConn(tc tlsConn, connType connType, isLocal bool, priority int) internalConn {
|
||||
now := time.Now()
|
||||
return internalConn{
|
||||
tlsConn: tc,
|
||||
connType: connType,
|
||||
isLocal: isLocal,
|
||||
priority: priority,
|
||||
establishedAt: time.Now().Truncate(time.Second),
|
||||
establishedAt: now.Truncate(time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,12 +140,16 @@ func (c internalConn) EstablishedAt() time.Time {
|
||||
return c.establishedAt
|
||||
}
|
||||
|
||||
func (c internalConn) ConnectionID() string {
|
||||
return c.connectionID
|
||||
}
|
||||
|
||||
func (c internalConn) String() string {
|
||||
t := "WAN"
|
||||
if c.isLocal {
|
||||
t = "LAN"
|
||||
}
|
||||
return fmt.Sprintf("%s-%s/%s/%s/%s-P%d", c.LocalAddr(), c.RemoteAddr(), c.Type(), c.Crypto(), t, c.Priority())
|
||||
return fmt.Sprintf("%s-%s/%s/%s/%s-P%d-%s", c.LocalAddr(), c.RemoteAddr(), c.Type(), c.Crypto(), t, c.Priority(), c.connectionID)
|
||||
}
|
||||
|
||||
type dialerFactory interface {
|
||||
@ -160,6 +166,7 @@ type commonDialer struct {
|
||||
lanChecker *lanChecker
|
||||
lanPriority int
|
||||
wanPriority int
|
||||
allowsMultiConns bool
|
||||
}
|
||||
|
||||
func (d *commonDialer) RedialFrequency() time.Duration {
|
||||
@ -173,10 +180,15 @@ func (d *commonDialer) Priority(host string) int {
|
||||
return d.wanPriority
|
||||
}
|
||||
|
||||
func (d *commonDialer) AllowsMultiConns() bool {
|
||||
return d.allowsMultiConns
|
||||
}
|
||||
|
||||
type genericDialer interface {
|
||||
Dial(context.Context, protocol.DeviceID, *url.URL) (internalConn, error)
|
||||
RedialFrequency() time.Duration
|
||||
Priority(host string) int
|
||||
AllowsMultiConns() bool
|
||||
}
|
||||
|
||||
type listenerFactory interface {
|
||||
@ -212,10 +224,7 @@ type genericListener interface {
|
||||
type Model interface {
|
||||
protocol.Model
|
||||
AddConnection(conn protocol.Connection, hello protocol.Hello)
|
||||
NumConnections() int
|
||||
Connection(remoteID protocol.DeviceID) (protocol.Connection, bool)
|
||||
OnHello(protocol.DeviceID, net.Addr, protocol.Hello) error
|
||||
GetHello(protocol.DeviceID) protocol.HelloIntf
|
||||
DeviceStatistics() (map[protocol.DeviceID]stats.DeviceStatistics, error)
|
||||
}
|
||||
|
||||
|
@ -62,6 +62,7 @@ func (d *tcpDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL)
|
||||
if isLocal {
|
||||
priority = d.lanPriority
|
||||
}
|
||||
|
||||
return newInternalConn(tc, connTypeTCPClient, isLocal, priority), nil
|
||||
}
|
||||
|
||||
@ -73,9 +74,10 @@ func (tcpDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Config
|
||||
trafficClass: opts.TrafficClass,
|
||||
reconnectInterval: time.Duration(opts.ReconnectIntervalS) * time.Second,
|
||||
tlsCfg: tlsCfg,
|
||||
lanChecker: lanChecker,
|
||||
lanPriority: opts.ConnectionPriorityTCPLAN,
|
||||
wanPriority: opts.ConnectionPriorityTCPWAN,
|
||||
lanChecker: lanChecker,
|
||||
allowsMultiConns: true,
|
||||
},
|
||||
registry: registry,
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ func newFakeConnection(id protocol.DeviceID, model Model) *fakeConnection {
|
||||
return f.fileData[name], nil
|
||||
})
|
||||
f.DeviceIDReturns(id)
|
||||
f.ConnectionIDReturns(rand.String(16))
|
||||
f.CloseCalls(func(err error) {
|
||||
f.closeOnce.Do(func() {
|
||||
close(f.closed)
|
||||
|
@ -530,7 +530,7 @@ func setupROFolder(t *testing.T) (*testModel, *receiveOnlyFolder, context.Cancel
|
||||
cfg.Folders = []config.FolderConfiguration{fcfg}
|
||||
replace(t, w, cfg)
|
||||
|
||||
m := newModel(t, w, myID, "syncthing", "dev", nil)
|
||||
m := newModel(t, w, myID, nil)
|
||||
m.ServeBackground()
|
||||
<-m.started
|
||||
must(t, m.ScanFolder("ro"))
|
||||
|
@ -507,7 +507,7 @@ nextFile:
|
||||
|
||||
devices := snap.Availability(fileName)
|
||||
for _, dev := range devices {
|
||||
if _, ok := f.model.Connection(dev); ok {
|
||||
if f.model.ConnectedTo(dev) {
|
||||
// Handle the file normally, by copying and pulling, etc.
|
||||
f.handleFile(fi, snap, copyChan)
|
||||
continue nextFile
|
||||
|
@ -76,18 +76,16 @@ type Model struct {
|
||||
result1 model.FolderCompletion
|
||||
result2 error
|
||||
}
|
||||
ConnectionStub func(protocol.DeviceID) (protocol.Connection, bool)
|
||||
connectionMutex sync.RWMutex
|
||||
connectionArgsForCall []struct {
|
||||
ConnectedToStub func(protocol.DeviceID) bool
|
||||
connectedToMutex sync.RWMutex
|
||||
connectedToArgsForCall []struct {
|
||||
arg1 protocol.DeviceID
|
||||
}
|
||||
connectionReturns struct {
|
||||
result1 protocol.Connection
|
||||
result2 bool
|
||||
connectedToReturns struct {
|
||||
result1 bool
|
||||
}
|
||||
connectionReturnsOnCall map[int]struct {
|
||||
result1 protocol.Connection
|
||||
result2 bool
|
||||
connectedToReturnsOnCall map[int]struct {
|
||||
result1 bool
|
||||
}
|
||||
ConnectionStatsStub func() map[string]interface{}
|
||||
connectionStatsMutex sync.RWMutex
|
||||
@ -262,17 +260,6 @@ type Model struct {
|
||||
result1 map[string][]versioner.FileVersion
|
||||
result2 error
|
||||
}
|
||||
GetHelloStub func(protocol.DeviceID) protocol.HelloIntf
|
||||
getHelloMutex sync.RWMutex
|
||||
getHelloArgsForCall []struct {
|
||||
arg1 protocol.DeviceID
|
||||
}
|
||||
getHelloReturns struct {
|
||||
result1 protocol.HelloIntf
|
||||
}
|
||||
getHelloReturnsOnCall map[int]struct {
|
||||
result1 protocol.HelloIntf
|
||||
}
|
||||
GetMtimeMappingStub func(string, string) (fs.MtimeMapping, error)
|
||||
getMtimeMappingMutex sync.RWMutex
|
||||
getMtimeMappingArgsForCall []struct {
|
||||
@ -378,16 +365,6 @@ type Model struct {
|
||||
result3 []db.FileInfoTruncated
|
||||
result4 error
|
||||
}
|
||||
NumConnectionsStub func() int
|
||||
numConnectionsMutex sync.RWMutex
|
||||
numConnectionsArgsForCall []struct {
|
||||
}
|
||||
numConnectionsReturns struct {
|
||||
result1 int
|
||||
}
|
||||
numConnectionsReturnsOnCall map[int]struct {
|
||||
result1 int
|
||||
}
|
||||
OnHelloStub func(protocol.DeviceID, net.Addr, protocol.Hello) error
|
||||
onHelloMutex sync.RWMutex
|
||||
onHelloArgsForCall []struct {
|
||||
@ -888,68 +865,65 @@ func (fake *Model) CompletionReturnsOnCall(i int, result1 model.FolderCompletion
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *Model) Connection(arg1 protocol.DeviceID) (protocol.Connection, bool) {
|
||||
fake.connectionMutex.Lock()
|
||||
ret, specificReturn := fake.connectionReturnsOnCall[len(fake.connectionArgsForCall)]
|
||||
fake.connectionArgsForCall = append(fake.connectionArgsForCall, struct {
|
||||
func (fake *Model) ConnectedTo(arg1 protocol.DeviceID) bool {
|
||||
fake.connectedToMutex.Lock()
|
||||
ret, specificReturn := fake.connectedToReturnsOnCall[len(fake.connectedToArgsForCall)]
|
||||
fake.connectedToArgsForCall = append(fake.connectedToArgsForCall, struct {
|
||||
arg1 protocol.DeviceID
|
||||
}{arg1})
|
||||
stub := fake.ConnectionStub
|
||||
fakeReturns := fake.connectionReturns
|
||||
fake.recordInvocation("Connection", []interface{}{arg1})
|
||||
fake.connectionMutex.Unlock()
|
||||
stub := fake.ConnectedToStub
|
||||
fakeReturns := fake.connectedToReturns
|
||||
fake.recordInvocation("ConnectedTo", []interface{}{arg1})
|
||||
fake.connectedToMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1, ret.result2
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1, fakeReturns.result2
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *Model) ConnectionCallCount() int {
|
||||
fake.connectionMutex.RLock()
|
||||
defer fake.connectionMutex.RUnlock()
|
||||
return len(fake.connectionArgsForCall)
|
||||
func (fake *Model) ConnectedToCallCount() int {
|
||||
fake.connectedToMutex.RLock()
|
||||
defer fake.connectedToMutex.RUnlock()
|
||||
return len(fake.connectedToArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *Model) ConnectionCalls(stub func(protocol.DeviceID) (protocol.Connection, bool)) {
|
||||
fake.connectionMutex.Lock()
|
||||
defer fake.connectionMutex.Unlock()
|
||||
fake.ConnectionStub = stub
|
||||
func (fake *Model) ConnectedToCalls(stub func(protocol.DeviceID) bool) {
|
||||
fake.connectedToMutex.Lock()
|
||||
defer fake.connectedToMutex.Unlock()
|
||||
fake.ConnectedToStub = stub
|
||||
}
|
||||
|
||||
func (fake *Model) ConnectionArgsForCall(i int) protocol.DeviceID {
|
||||
fake.connectionMutex.RLock()
|
||||
defer fake.connectionMutex.RUnlock()
|
||||
argsForCall := fake.connectionArgsForCall[i]
|
||||
func (fake *Model) ConnectedToArgsForCall(i int) protocol.DeviceID {
|
||||
fake.connectedToMutex.RLock()
|
||||
defer fake.connectedToMutex.RUnlock()
|
||||
argsForCall := fake.connectedToArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *Model) ConnectionReturns(result1 protocol.Connection, result2 bool) {
|
||||
fake.connectionMutex.Lock()
|
||||
defer fake.connectionMutex.Unlock()
|
||||
fake.ConnectionStub = nil
|
||||
fake.connectionReturns = struct {
|
||||
result1 protocol.Connection
|
||||
result2 bool
|
||||
}{result1, result2}
|
||||
func (fake *Model) ConnectedToReturns(result1 bool) {
|
||||
fake.connectedToMutex.Lock()
|
||||
defer fake.connectedToMutex.Unlock()
|
||||
fake.ConnectedToStub = nil
|
||||
fake.connectedToReturns = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *Model) ConnectionReturnsOnCall(i int, result1 protocol.Connection, result2 bool) {
|
||||
fake.connectionMutex.Lock()
|
||||
defer fake.connectionMutex.Unlock()
|
||||
fake.ConnectionStub = nil
|
||||
if fake.connectionReturnsOnCall == nil {
|
||||
fake.connectionReturnsOnCall = make(map[int]struct {
|
||||
result1 protocol.Connection
|
||||
result2 bool
|
||||
func (fake *Model) ConnectedToReturnsOnCall(i int, result1 bool) {
|
||||
fake.connectedToMutex.Lock()
|
||||
defer fake.connectedToMutex.Unlock()
|
||||
fake.ConnectedToStub = nil
|
||||
if fake.connectedToReturnsOnCall == nil {
|
||||
fake.connectedToReturnsOnCall = make(map[int]struct {
|
||||
result1 bool
|
||||
})
|
||||
}
|
||||
fake.connectionReturnsOnCall[i] = struct {
|
||||
result1 protocol.Connection
|
||||
result2 bool
|
||||
}{result1, result2}
|
||||
fake.connectedToReturnsOnCall[i] = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *Model) ConnectionStats() map[string]interface{} {
|
||||
@ -1797,67 +1771,6 @@ func (fake *Model) GetFolderVersionsReturnsOnCall(i int, result1 map[string][]ve
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *Model) GetHello(arg1 protocol.DeviceID) protocol.HelloIntf {
|
||||
fake.getHelloMutex.Lock()
|
||||
ret, specificReturn := fake.getHelloReturnsOnCall[len(fake.getHelloArgsForCall)]
|
||||
fake.getHelloArgsForCall = append(fake.getHelloArgsForCall, struct {
|
||||
arg1 protocol.DeviceID
|
||||
}{arg1})
|
||||
stub := fake.GetHelloStub
|
||||
fakeReturns := fake.getHelloReturns
|
||||
fake.recordInvocation("GetHello", []interface{}{arg1})
|
||||
fake.getHelloMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *Model) GetHelloCallCount() int {
|
||||
fake.getHelloMutex.RLock()
|
||||
defer fake.getHelloMutex.RUnlock()
|
||||
return len(fake.getHelloArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *Model) GetHelloCalls(stub func(protocol.DeviceID) protocol.HelloIntf) {
|
||||
fake.getHelloMutex.Lock()
|
||||
defer fake.getHelloMutex.Unlock()
|
||||
fake.GetHelloStub = stub
|
||||
}
|
||||
|
||||
func (fake *Model) GetHelloArgsForCall(i int) protocol.DeviceID {
|
||||
fake.getHelloMutex.RLock()
|
||||
defer fake.getHelloMutex.RUnlock()
|
||||
argsForCall := fake.getHelloArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *Model) GetHelloReturns(result1 protocol.HelloIntf) {
|
||||
fake.getHelloMutex.Lock()
|
||||
defer fake.getHelloMutex.Unlock()
|
||||
fake.GetHelloStub = nil
|
||||
fake.getHelloReturns = struct {
|
||||
result1 protocol.HelloIntf
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *Model) GetHelloReturnsOnCall(i int, result1 protocol.HelloIntf) {
|
||||
fake.getHelloMutex.Lock()
|
||||
defer fake.getHelloMutex.Unlock()
|
||||
fake.GetHelloStub = nil
|
||||
if fake.getHelloReturnsOnCall == nil {
|
||||
fake.getHelloReturnsOnCall = make(map[int]struct {
|
||||
result1 protocol.HelloIntf
|
||||
})
|
||||
}
|
||||
fake.getHelloReturnsOnCall[i] = struct {
|
||||
result1 protocol.HelloIntf
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *Model) GetMtimeMapping(arg1 string, arg2 string) (fs.MtimeMapping, error) {
|
||||
fake.getMtimeMappingMutex.Lock()
|
||||
ret, specificReturn := fake.getMtimeMappingReturnsOnCall[len(fake.getMtimeMappingArgsForCall)]
|
||||
@ -2331,59 +2244,6 @@ func (fake *Model) NeedFolderFilesReturnsOnCall(i int, result1 []db.FileInfoTrun
|
||||
}{result1, result2, result3, result4}
|
||||
}
|
||||
|
||||
func (fake *Model) NumConnections() int {
|
||||
fake.numConnectionsMutex.Lock()
|
||||
ret, specificReturn := fake.numConnectionsReturnsOnCall[len(fake.numConnectionsArgsForCall)]
|
||||
fake.numConnectionsArgsForCall = append(fake.numConnectionsArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.NumConnectionsStub
|
||||
fakeReturns := fake.numConnectionsReturns
|
||||
fake.recordInvocation("NumConnections", []interface{}{})
|
||||
fake.numConnectionsMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *Model) NumConnectionsCallCount() int {
|
||||
fake.numConnectionsMutex.RLock()
|
||||
defer fake.numConnectionsMutex.RUnlock()
|
||||
return len(fake.numConnectionsArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *Model) NumConnectionsCalls(stub func() int) {
|
||||
fake.numConnectionsMutex.Lock()
|
||||
defer fake.numConnectionsMutex.Unlock()
|
||||
fake.NumConnectionsStub = stub
|
||||
}
|
||||
|
||||
func (fake *Model) NumConnectionsReturns(result1 int) {
|
||||
fake.numConnectionsMutex.Lock()
|
||||
defer fake.numConnectionsMutex.Unlock()
|
||||
fake.NumConnectionsStub = nil
|
||||
fake.numConnectionsReturns = struct {
|
||||
result1 int
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *Model) NumConnectionsReturnsOnCall(i int, result1 int) {
|
||||
fake.numConnectionsMutex.Lock()
|
||||
defer fake.numConnectionsMutex.Unlock()
|
||||
fake.NumConnectionsStub = nil
|
||||
if fake.numConnectionsReturnsOnCall == nil {
|
||||
fake.numConnectionsReturnsOnCall = make(map[int]struct {
|
||||
result1 int
|
||||
})
|
||||
}
|
||||
fake.numConnectionsReturnsOnCall[i] = struct {
|
||||
result1 int
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *Model) OnHello(arg1 protocol.DeviceID, arg2 net.Addr, arg3 protocol.Hello) error {
|
||||
fake.onHelloMutex.Lock()
|
||||
ret, specificReturn := fake.onHelloReturnsOnCall[len(fake.onHelloArgsForCall)]
|
||||
@ -3419,8 +3279,8 @@ func (fake *Model) Invocations() map[string][][]interface{} {
|
||||
defer fake.clusterConfigMutex.RUnlock()
|
||||
fake.completionMutex.RLock()
|
||||
defer fake.completionMutex.RUnlock()
|
||||
fake.connectionMutex.RLock()
|
||||
defer fake.connectionMutex.RUnlock()
|
||||
fake.connectedToMutex.RLock()
|
||||
defer fake.connectedToMutex.RUnlock()
|
||||
fake.connectionStatsMutex.RLock()
|
||||
defer fake.connectionStatsMutex.RUnlock()
|
||||
fake.currentFolderFileMutex.RLock()
|
||||
@ -3449,8 +3309,6 @@ func (fake *Model) Invocations() map[string][][]interface{} {
|
||||
defer fake.folderStatisticsMutex.RUnlock()
|
||||
fake.getFolderVersionsMutex.RLock()
|
||||
defer fake.getFolderVersionsMutex.RUnlock()
|
||||
fake.getHelloMutex.RLock()
|
||||
defer fake.getHelloMutex.RUnlock()
|
||||
fake.getMtimeMappingMutex.RLock()
|
||||
defer fake.getMtimeMappingMutex.RUnlock()
|
||||
fake.globalDirectoryTreeMutex.RLock()
|
||||
@ -3465,8 +3323,6 @@ func (fake *Model) Invocations() map[string][][]interface{} {
|
||||
defer fake.localChangedFolderFilesMutex.RUnlock()
|
||||
fake.needFolderFilesMutex.RLock()
|
||||
defer fake.needFolderFilesMutex.RUnlock()
|
||||
fake.numConnectionsMutex.RLock()
|
||||
defer fake.numConnectionsMutex.RUnlock()
|
||||
fake.onHelloMutex.RLock()
|
||||
defer fake.onHelloMutex.RUnlock()
|
||||
fake.overrideMutex.RLock()
|
||||
|
@ -37,6 +37,7 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/ignore"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/rand"
|
||||
"github.com/syncthing/syncthing/lib/scanner"
|
||||
"github.com/syncthing/syncthing/lib/semaphore"
|
||||
"github.com/syncthing/syncthing/lib/stats"
|
||||
@ -108,6 +109,7 @@ type Model interface {
|
||||
DeviceStatistics() (map[protocol.DeviceID]stats.DeviceStatistics, error)
|
||||
FolderStatistics() (map[string]stats.FolderStatistics, error)
|
||||
UsageReportingStats(report *contract.Report, version int, preview bool)
|
||||
ConnectedTo(remoteID protocol.DeviceID) bool
|
||||
|
||||
PendingDevices() (map[protocol.DeviceID]db.ObservedDevice, error)
|
||||
PendingFolders(device protocol.DeviceID) (map[string]db.PendingFolder, error)
|
||||
@ -124,8 +126,6 @@ type model struct {
|
||||
// constructor parameters
|
||||
cfg config.Wrapper
|
||||
id protocol.DeviceID
|
||||
clientName string
|
||||
clientVersion string
|
||||
db *db.Lowlevel
|
||||
protectedFiles []string
|
||||
evLogger events.Logger
|
||||
@ -143,6 +143,7 @@ type model struct {
|
||||
fatalChan chan error
|
||||
started chan struct{}
|
||||
keyGen *protocol.KeyGenerator
|
||||
promotionTimer *time.Timer
|
||||
|
||||
// fields protected by fmut
|
||||
fmut sync.RWMutex
|
||||
@ -158,9 +159,11 @@ type model struct {
|
||||
|
||||
// fields protected by pmut
|
||||
pmut sync.RWMutex
|
||||
conn map[protocol.DeviceID]protocol.Connection
|
||||
connections map[string]protocol.Connection // connection ID -> connection
|
||||
deviceConnIDs map[protocol.DeviceID][]string // device -> connection IDs (invariant: if the key exists, the value is len >= 1, with the primary connection at the start of the slice)
|
||||
promotedConnID map[protocol.DeviceID]string // device -> latest promoted connection ID
|
||||
connRequestLimiters map[protocol.DeviceID]*semaphore.Semaphore
|
||||
closed map[protocol.DeviceID]chan struct{}
|
||||
closed map[string]chan struct{} // connection ID -> closed channel
|
||||
helloMessages map[protocol.DeviceID]protocol.Hello
|
||||
deviceDownloads map[protocol.DeviceID]*deviceDownloadState
|
||||
remoteFolderStates map[protocol.DeviceID]map[string]remoteFolderState // deviceID -> folders
|
||||
@ -179,13 +182,11 @@ var folderFactories = make(map[config.FolderType]folderFactory)
|
||||
var (
|
||||
errDeviceUnknown = errors.New("unknown device")
|
||||
errDevicePaused = errors.New("device is paused")
|
||||
errDeviceRemoved = errors.New("device has been removed")
|
||||
ErrFolderPaused = errors.New("folder is paused")
|
||||
ErrFolderNotRunning = errors.New("folder is not running")
|
||||
ErrFolderMissing = errors.New("no such folder")
|
||||
errNoVersioner = errors.New("folder has no versioner")
|
||||
// errors about why a connection is closed
|
||||
errReplacingConnection = errors.New("replacing connection")
|
||||
errStopped = errors.New("Syncthing is being stopped")
|
||||
errEncryptionInvConfigLocal = errors.New("can't encrypt outgoing data because local data is encrypted (folder-type receive-encrypted)")
|
||||
errEncryptionInvConfigRemote = errors.New("remote has encrypted data and encrypts that data for us - this is impossible")
|
||||
@ -203,7 +204,7 @@ var (
|
||||
// NewModel creates and starts a new model. The model starts in read-only mode,
|
||||
// where it sends index information to connected peers and responds to requests
|
||||
// for file data without altering the local folder in any way.
|
||||
func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersion string, ldb *db.Lowlevel, protectedFiles []string, evLogger events.Logger, keyGen *protocol.KeyGenerator) Model {
|
||||
func NewModel(cfg config.Wrapper, id protocol.DeviceID, ldb *db.Lowlevel, protectedFiles []string, evLogger events.Logger, keyGen *protocol.KeyGenerator) Model {
|
||||
spec := svcutil.SpecWithDebugLogger(l)
|
||||
m := &model{
|
||||
Supervisor: suture.New("model", spec),
|
||||
@ -211,8 +212,6 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersio
|
||||
// constructor parameters
|
||||
cfg: cfg,
|
||||
id: id,
|
||||
clientName: clientName,
|
||||
clientVersion: clientVersion,
|
||||
db: ldb,
|
||||
protectedFiles: protectedFiles,
|
||||
evLogger: evLogger,
|
||||
@ -226,6 +225,7 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersio
|
||||
fatalChan: make(chan error),
|
||||
started: make(chan struct{}),
|
||||
keyGen: keyGen,
|
||||
promotionTimer: time.NewTimer(0),
|
||||
|
||||
// fields protected by fmut
|
||||
fmut: sync.NewRWMutex(),
|
||||
@ -240,16 +240,19 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersio
|
||||
|
||||
// fields protected by pmut
|
||||
pmut: sync.NewRWMutex(),
|
||||
conn: make(map[protocol.DeviceID]protocol.Connection),
|
||||
connections: make(map[string]protocol.Connection),
|
||||
deviceConnIDs: make(map[protocol.DeviceID][]string),
|
||||
promotedConnID: make(map[protocol.DeviceID]string),
|
||||
connRequestLimiters: make(map[protocol.DeviceID]*semaphore.Semaphore),
|
||||
closed: make(map[protocol.DeviceID]chan struct{}),
|
||||
closed: make(map[string]chan struct{}),
|
||||
helloMessages: make(map[protocol.DeviceID]protocol.Hello),
|
||||
deviceDownloads: make(map[protocol.DeviceID]*deviceDownloadState),
|
||||
remoteFolderStates: make(map[protocol.DeviceID]map[string]remoteFolderState),
|
||||
indexHandlers: newServiceMap[protocol.DeviceID, *indexHandlerRegistry](evLogger),
|
||||
}
|
||||
for devID := range cfg.Devices() {
|
||||
for devID, cfg := range cfg.Devices() {
|
||||
m.deviceStatRefs[devID] = stats.NewDeviceStatisticsReference(m.db, devID)
|
||||
m.setConnRequestLimitersPLocked(cfg)
|
||||
}
|
||||
m.Add(m.folderRunners)
|
||||
m.Add(m.progressEmitter)
|
||||
@ -272,11 +275,18 @@ func (m *model) serve(ctx context.Context) error {
|
||||
|
||||
close(m.started)
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case err := <-m.fatalChan:
|
||||
return svcutil.AsFatalErr(err, svcutil.ExitError)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
l.Debugln(m, "context closed, stopping", ctx.Err())
|
||||
return ctx.Err()
|
||||
case err := <-m.fatalChan:
|
||||
l.Debugln(m, "fatal error, stopping", err)
|
||||
return svcutil.AsFatalErr(err, svcutil.ExitError)
|
||||
case <-m.promotionTimer.C:
|
||||
l.Debugln("promotion timer fired")
|
||||
m.promoteConnections()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,9 +313,9 @@ func (m *model) initFolders(cfg config.Configuration) error {
|
||||
|
||||
func (m *model) closeAllConnectionsAndWait() {
|
||||
m.pmut.RLock()
|
||||
closed := make([]chan struct{}, 0, len(m.conn))
|
||||
for id, conn := range m.conn {
|
||||
closed = append(closed, m.closed[id])
|
||||
closed := make([]chan struct{}, 0, len(m.connections))
|
||||
for connID, conn := range m.connections {
|
||||
closed = append(closed, m.closed[connID])
|
||||
go conn.Close(errStopped)
|
||||
}
|
||||
m.pmut.RUnlock()
|
||||
@ -635,7 +645,7 @@ func (m *model) UsageReportingStats(report *contract.Report, version int, previe
|
||||
|
||||
// Transport stats
|
||||
m.pmut.RLock()
|
||||
for _, conn := range m.conn {
|
||||
for _, conn := range m.connections {
|
||||
report.TransportStats[conn.Transport()]++
|
||||
}
|
||||
m.pmut.RUnlock()
|
||||
@ -699,22 +709,28 @@ func (m *model) UsageReportingStats(report *contract.Report, version int, previe
|
||||
}
|
||||
}
|
||||
|
||||
type ConnectionInfo struct {
|
||||
protocol.Statistics
|
||||
type ConnectionStats struct {
|
||||
protocol.Statistics // Total for primary + secondaries
|
||||
|
||||
Connected bool `json:"connected"`
|
||||
Paused bool `json:"paused"`
|
||||
Address string `json:"address"`
|
||||
ClientVersion string `json:"clientVersion"`
|
||||
Type string `json:"type"`
|
||||
IsLocal bool `json:"isLocal"`
|
||||
Crypto string `json:"crypto"`
|
||||
|
||||
Address string `json:"address"` // mirror values from Primary, for compatibility with <1.24.0
|
||||
Type string `json:"type"` // mirror values from Primary, for compatibility with <1.24.0
|
||||
IsLocal bool `json:"isLocal"` // mirror values from Primary, for compatibility with <1.24.0
|
||||
Crypto string `json:"crypto"` // mirror values from Primary, for compatibility with <1.24.0
|
||||
|
||||
Primary ConnectionInfo `json:"primary,omitempty"`
|
||||
Secondary []ConnectionInfo `json:"secondary,omitempty"`
|
||||
}
|
||||
|
||||
// NumConnections returns the current number of active connected devices.
|
||||
func (m *model) NumConnections() int {
|
||||
m.pmut.RLock()
|
||||
defer m.pmut.RUnlock()
|
||||
return len(m.conn)
|
||||
type ConnectionInfo struct {
|
||||
protocol.Statistics
|
||||
Address string `json:"address"`
|
||||
Type string `json:"type"`
|
||||
IsLocal bool `json:"isLocal"`
|
||||
Crypto string `json:"crypto"`
|
||||
}
|
||||
|
||||
// ConnectionStats returns a map with connection statistics for each device.
|
||||
@ -724,29 +740,59 @@ func (m *model) ConnectionStats() map[string]interface{} {
|
||||
|
||||
res := make(map[string]interface{})
|
||||
devs := m.cfg.Devices()
|
||||
conns := make(map[string]ConnectionInfo, len(devs))
|
||||
conns := make(map[string]ConnectionStats, len(devs))
|
||||
for device, deviceCfg := range devs {
|
||||
if device == m.id {
|
||||
continue
|
||||
}
|
||||
hello := m.helloMessages[device]
|
||||
versionString := hello.ClientVersion
|
||||
if hello.ClientName != "syncthing" {
|
||||
versionString = hello.ClientName + " " + hello.ClientVersion
|
||||
}
|
||||
ci := ConnectionInfo{
|
||||
ClientVersion: strings.TrimSpace(versionString),
|
||||
connIDs, ok := m.deviceConnIDs[device]
|
||||
cs := ConnectionStats{
|
||||
Connected: ok,
|
||||
Paused: deviceCfg.Paused,
|
||||
ClientVersion: strings.TrimSpace(versionString),
|
||||
}
|
||||
if conn, ok := m.conn[device]; ok {
|
||||
ci.Type = conn.Type()
|
||||
ci.IsLocal = conn.IsLocal()
|
||||
ci.Crypto = conn.Crypto()
|
||||
ci.Connected = ok
|
||||
ci.Statistics = conn.Statistics()
|
||||
if addr := conn.RemoteAddr(); addr != nil {
|
||||
ci.Address = addr.String()
|
||||
if ok {
|
||||
conn := m.connections[connIDs[0]]
|
||||
|
||||
cs.Primary.Type = conn.Type()
|
||||
cs.Primary.IsLocal = conn.IsLocal()
|
||||
cs.Primary.Crypto = conn.Crypto()
|
||||
cs.Primary.Statistics = conn.Statistics()
|
||||
cs.Primary.Address = conn.RemoteAddr().String()
|
||||
|
||||
cs.Type = cs.Primary.Type
|
||||
cs.IsLocal = cs.Primary.IsLocal
|
||||
cs.Crypto = cs.Primary.Crypto
|
||||
cs.Address = cs.Primary.Address
|
||||
cs.Statistics = cs.Primary.Statistics
|
||||
|
||||
for _, connID := range connIDs[1:] {
|
||||
conn = m.connections[connID]
|
||||
sec := ConnectionInfo{
|
||||
Statistics: conn.Statistics(),
|
||||
Address: conn.RemoteAddr().String(),
|
||||
Type: conn.Type(),
|
||||
IsLocal: conn.IsLocal(),
|
||||
Crypto: conn.Crypto(),
|
||||
}
|
||||
if sec.At.After(cs.At) {
|
||||
cs.At = sec.At
|
||||
}
|
||||
if sec.StartedAt.Before(cs.StartedAt) {
|
||||
cs.StartedAt = sec.StartedAt
|
||||
}
|
||||
cs.InBytesTotal += sec.InBytesTotal
|
||||
cs.OutBytesTotal += sec.OutBytesTotal
|
||||
cs.Secondary = append(cs.Secondary, sec)
|
||||
}
|
||||
}
|
||||
|
||||
conns[device.String()] = ci
|
||||
conns[device.String()] = cs
|
||||
}
|
||||
|
||||
res["connections"] = conns
|
||||
@ -1138,17 +1184,16 @@ func (m *model) handleIndex(conn protocol.Connection, folder string, fs []protoc
|
||||
}
|
||||
|
||||
m.pmut.RLock()
|
||||
indexHandler, ok := m.indexHandlers.Get(deviceID)
|
||||
indexHandler, ok := m.getIndexHandlerPRLocked(conn)
|
||||
m.pmut.RUnlock()
|
||||
if !ok {
|
||||
// This should be impossible, as an index handler always exists for an
|
||||
// open connection, and this method can't be called on a closed
|
||||
// connection
|
||||
// This should be impossible, as an index handler is registered when
|
||||
// we send a cluster config, and that is what triggers index
|
||||
// sending.
|
||||
m.evLogger.Log(events.Failure, "index sender does not exist for connection on which indexes were received")
|
||||
l.Debugf("%v for folder (ID %q) sent from device %q: missing index handler", op, folder, deviceID)
|
||||
return fmt.Errorf("index handler missing: %s", folder)
|
||||
return fmt.Errorf("%s: %w", folder, ErrFolderNotRunning)
|
||||
}
|
||||
|
||||
return indexHandler.ReceiveIndex(folder, fs, update, op)
|
||||
}
|
||||
|
||||
@ -1161,24 +1206,26 @@ type ClusterConfigReceivedEventData struct {
|
||||
}
|
||||
|
||||
func (m *model) ClusterConfig(conn protocol.Connection, cm protocol.ClusterConfig) error {
|
||||
deviceID := conn.DeviceID()
|
||||
|
||||
if cm.Secondary {
|
||||
// No handling of secondary connection ClusterConfigs; they merely
|
||||
// indicate the connection is ready to start.
|
||||
l.Debugf("Skipping secondary ClusterConfig from %v at %s", deviceID.Short(), conn)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check the peer device's announced folders against our own. Emits events
|
||||
// for folders that we don't expect (unknown or not shared).
|
||||
// Also, collect a list of folders we do share, and if he's interested in
|
||||
// temporary indexes, subscribe the connection.
|
||||
|
||||
deviceID := conn.DeviceID()
|
||||
l.Debugf("Handling ClusterConfig from %v", deviceID.Short())
|
||||
|
||||
m.pmut.RLock()
|
||||
indexHandlerRegistry, ok := m.indexHandlers.Get(deviceID)
|
||||
m.pmut.RUnlock()
|
||||
if !ok {
|
||||
panic("bug: ClusterConfig called on closed or nonexistent connection")
|
||||
}
|
||||
l.Debugf("Handling ClusterConfig from %v at %s", deviceID.Short(), conn)
|
||||
indexHandlerRegistry := m.ensureIndexHandler(conn)
|
||||
|
||||
deviceCfg, ok := m.cfg.Device(deviceID)
|
||||
if !ok {
|
||||
l.Debugln("Device disappeared from config while processing cluster-config")
|
||||
l.Debugf("Device %s disappeared from config while processing cluster-config", deviceID.Short())
|
||||
return errDeviceUnknown
|
||||
}
|
||||
|
||||
@ -1198,11 +1245,11 @@ func (m *model) ClusterConfig(conn protocol.Connection, cm protocol.ClusterConfi
|
||||
}
|
||||
}
|
||||
if info.remote.ID == protocol.EmptyDeviceID {
|
||||
l.Infof("Device %v sent cluster-config without the device info for the remote on folder %v", deviceID, folder.Description())
|
||||
l.Infof("Device %v sent cluster-config without the device info for the remote on folder %v", deviceID.Short(), folder.Description())
|
||||
return errMissingRemoteInClusterConfig
|
||||
}
|
||||
if info.local.ID == protocol.EmptyDeviceID {
|
||||
l.Infof("Device %v sent cluster-config without the device info for us locally on folder %v", deviceID, folder.Description())
|
||||
l.Infof("Device %v sent cluster-config without the device info for us locally on folder %v", deviceID.Short(), folder.Description())
|
||||
return errMissingLocalInClusterConfig
|
||||
}
|
||||
ccDeviceInfos[folder.ID] = info
|
||||
@ -1260,12 +1307,16 @@ func (m *model) ClusterConfig(conn protocol.Connection, cm protocol.ClusterConfi
|
||||
})
|
||||
|
||||
if len(tempIndexFolders) > 0 {
|
||||
var connOK bool
|
||||
var conn protocol.Connection
|
||||
m.pmut.RLock()
|
||||
conn, ok := m.conn[deviceID]
|
||||
if connIDs, connIDOK := m.deviceConnIDs[deviceID]; connIDOK {
|
||||
conn, connOK = m.connections[connIDs[0]]
|
||||
}
|
||||
m.pmut.RUnlock()
|
||||
// In case we've got ClusterConfig, and the connection disappeared
|
||||
// from infront of our nose.
|
||||
if ok {
|
||||
if connOK {
|
||||
m.progressEmitter.temporaryIndexSubscribe(conn, tempIndexFolders)
|
||||
}
|
||||
}
|
||||
@ -1291,6 +1342,57 @@ func (m *model) ClusterConfig(conn protocol.Connection, cm protocol.ClusterConfi
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *model) ensureIndexHandler(conn protocol.Connection) *indexHandlerRegistry {
|
||||
deviceID := conn.DeviceID()
|
||||
connID := conn.ConnectionID()
|
||||
|
||||
m.pmut.Lock()
|
||||
defer m.pmut.Unlock()
|
||||
|
||||
indexHandlerRegistry, ok := m.indexHandlers.Get(deviceID)
|
||||
if ok && indexHandlerRegistry.conn.ConnectionID() == connID {
|
||||
// This is an existing and proper index handler for this connection.
|
||||
return indexHandlerRegistry
|
||||
}
|
||||
|
||||
if ok {
|
||||
// A handler exists, but it's for another connection than the one we
|
||||
// now got a ClusterConfig on. This should be unusual as it means
|
||||
// the other side has decided to start using a new primary
|
||||
// connection but we haven't seen it close yet. Ideally it will
|
||||
// close shortly by itself...
|
||||
l.Infof("Abandoning old index handler for %s (%s) in favour of %s", deviceID.Short(), indexHandlerRegistry.conn.ConnectionID(), connID)
|
||||
m.indexHandlers.RemoveAndWait(deviceID, 0)
|
||||
}
|
||||
|
||||
// Create a new index handler for this device.
|
||||
indexHandlerRegistry = newIndexHandlerRegistry(conn, m.deviceDownloads[deviceID], m.evLogger)
|
||||
for id, fcfg := range m.folderCfgs {
|
||||
l.Debugln("Registering folder", id, "for", deviceID.Short())
|
||||
runner, _ := m.folderRunners.Get(id)
|
||||
indexHandlerRegistry.RegisterFolderState(fcfg, m.folderFiles[id], runner)
|
||||
}
|
||||
m.indexHandlers.Add(deviceID, indexHandlerRegistry)
|
||||
|
||||
return indexHandlerRegistry
|
||||
}
|
||||
|
||||
func (m *model) getIndexHandlerPRLocked(conn protocol.Connection) (*indexHandlerRegistry, bool) {
|
||||
// Reads from index handlers, which requires pmut to be read locked
|
||||
|
||||
deviceID := conn.DeviceID()
|
||||
connID := conn.ConnectionID()
|
||||
|
||||
indexHandlerRegistry, ok := m.indexHandlers.Get(deviceID)
|
||||
if ok && indexHandlerRegistry.conn.ConnectionID() == connID {
|
||||
// This is an existing and proper index handler for this connection.
|
||||
return indexHandlerRegistry, true
|
||||
}
|
||||
|
||||
// There is no index handler, or it's not registered for this connection.
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.DeviceConfiguration, ccDeviceInfos map[string]*clusterConfigDeviceInfo, indexHandlers *indexHandlerRegistry) ([]string, map[string]remoteFolderState, error) {
|
||||
var folderDevice config.FolderDeviceConfiguration
|
||||
tempIndexFolders := make([]string, 0, len(folders))
|
||||
@ -1539,8 +1641,8 @@ func (m *model) sendClusterConfig(ids []protocol.DeviceID) {
|
||||
ccConns := make([]protocol.Connection, 0, len(ids))
|
||||
m.pmut.RLock()
|
||||
for _, id := range ids {
|
||||
if conn, ok := m.conn[id]; ok {
|
||||
ccConns = append(ccConns, conn)
|
||||
if connIDs, ok := m.deviceConnIDs[id]; ok {
|
||||
ccConns = append(ccConns, m.connections[connIDs[0]])
|
||||
}
|
||||
}
|
||||
m.pmut.RUnlock()
|
||||
@ -1777,33 +1879,62 @@ func (m *model) introduceDevice(device protocol.Device, introducerCfg config.Dev
|
||||
|
||||
// Closed is called when a connection has been closed
|
||||
func (m *model) Closed(conn protocol.Connection, err error) {
|
||||
device := conn.DeviceID()
|
||||
connID := conn.ConnectionID()
|
||||
deviceID := conn.DeviceID()
|
||||
|
||||
m.pmut.Lock()
|
||||
conn, ok := m.conn[device]
|
||||
conn, ok := m.connections[connID]
|
||||
if !ok {
|
||||
m.pmut.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
delete(m.conn, device)
|
||||
delete(m.connRequestLimiters, device)
|
||||
delete(m.helloMessages, device)
|
||||
delete(m.deviceDownloads, device)
|
||||
delete(m.remoteFolderStates, device)
|
||||
closed := m.closed[device]
|
||||
delete(m.closed, device)
|
||||
wait := m.indexHandlers.RemoveAndWaitChan(device, 0)
|
||||
closed := m.closed[connID]
|
||||
delete(m.closed, connID)
|
||||
delete(m.connections, connID)
|
||||
|
||||
removedIsPrimary := m.promotedConnID[deviceID] == connID
|
||||
remainingConns := without(m.deviceConnIDs[deviceID], connID)
|
||||
var wait <-chan error
|
||||
if removedIsPrimary {
|
||||
m.progressEmitter.temporaryIndexUnsubscribe(conn)
|
||||
if idxh, ok := m.indexHandlers.Get(deviceID); ok && idxh.conn.ConnectionID() == connID {
|
||||
wait = m.indexHandlers.RemoveAndWaitChan(deviceID, 0)
|
||||
}
|
||||
m.scheduleConnectionPromotion()
|
||||
}
|
||||
if len(remainingConns) == 0 {
|
||||
// All device connections closed
|
||||
delete(m.deviceConnIDs, deviceID)
|
||||
delete(m.promotedConnID, deviceID)
|
||||
delete(m.connRequestLimiters, deviceID)
|
||||
delete(m.helloMessages, deviceID)
|
||||
delete(m.remoteFolderStates, deviceID)
|
||||
delete(m.deviceDownloads, deviceID)
|
||||
} else {
|
||||
// Some connections remain
|
||||
m.deviceConnIDs[deviceID] = remainingConns
|
||||
}
|
||||
|
||||
m.pmut.Unlock()
|
||||
<-wait
|
||||
if wait != nil {
|
||||
<-wait
|
||||
}
|
||||
|
||||
m.progressEmitter.temporaryIndexUnsubscribe(conn)
|
||||
m.deviceDidClose(device, time.Since(conn.EstablishedAt()))
|
||||
m.fmut.RLock()
|
||||
m.deviceDidCloseFRLocked(deviceID, time.Since(conn.EstablishedAt()))
|
||||
m.fmut.RUnlock()
|
||||
|
||||
l.Infof("Connection to %s at %s closed: %v", device, conn, err)
|
||||
m.evLogger.Log(events.DeviceDisconnected, map[string]string{
|
||||
"id": device.String(),
|
||||
"error": err.Error(),
|
||||
})
|
||||
k := map[bool]string{false: "secondary", true: "primary"}[removedIsPrimary]
|
||||
l.Infof("Lost %s connection to %s at %s: %v (%d remain)", k, deviceID.Short(), conn, err, len(remainingConns))
|
||||
|
||||
if len(remainingConns) == 0 {
|
||||
l.Infof("Connection to %s at %s closed: %v", deviceID.Short(), conn, err)
|
||||
m.evLogger.Log(events.DeviceDisconnected, map[string]string{
|
||||
"id": deviceID.String(),
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
close(closed)
|
||||
}
|
||||
|
||||
@ -1852,36 +1983,36 @@ func (m *model) Request(conn protocol.Connection, folder, name string, _, size i
|
||||
if !ok {
|
||||
// The folder might be already unpaused in the config, but not yet
|
||||
// in the model.
|
||||
l.Debugf("Request from %s for file %s in unstarted folder %q", deviceID, name, folder)
|
||||
l.Debugf("Request from %s for file %s in unstarted folder %q", deviceID.Short(), name, folder)
|
||||
return nil, protocol.ErrGeneric
|
||||
}
|
||||
|
||||
if !folderCfg.SharedWith(deviceID) {
|
||||
l.Warnf("Request from %s for file %s in unshared folder %q", deviceID, name, folder)
|
||||
l.Warnf("Request from %s for file %s in unshared folder %q", deviceID.Short(), name, folder)
|
||||
return nil, protocol.ErrGeneric
|
||||
}
|
||||
if folderCfg.Paused {
|
||||
l.Debugf("Request from %s for file %s in paused folder %q", deviceID, name, folder)
|
||||
l.Debugf("Request from %s for file %s in paused folder %q", deviceID.Short(), name, folder)
|
||||
return nil, protocol.ErrGeneric
|
||||
}
|
||||
|
||||
// Make sure the path is valid and in canonical form
|
||||
if name, err = fs.Canonicalize(name); err != nil {
|
||||
l.Debugf("Request from %s in folder %q for invalid filename %s", deviceID, folder, name)
|
||||
l.Debugf("Request from %s in folder %q for invalid filename %s", deviceID.Short(), folder, name)
|
||||
return nil, protocol.ErrGeneric
|
||||
}
|
||||
|
||||
if deviceID != protocol.LocalDeviceID {
|
||||
l.Debugf("%v REQ(in): %s: %q / %q o=%d s=%d t=%v", m, deviceID, folder, name, offset, size, fromTemporary)
|
||||
l.Debugf("%v REQ(in): %s: %q / %q o=%d s=%d t=%v", m, deviceID.Short(), folder, name, offset, size, fromTemporary)
|
||||
}
|
||||
|
||||
if fs.IsInternal(name) {
|
||||
l.Debugf("%v REQ(in) for internal file: %s: %q / %q o=%d s=%d", m, deviceID, folder, name, offset, size)
|
||||
l.Debugf("%v REQ(in) for internal file: %s: %q / %q o=%d s=%d", m, deviceID.Short(), folder, name, offset, size)
|
||||
return nil, protocol.ErrInvalid
|
||||
}
|
||||
|
||||
if folderIgnores.Match(name).IsIgnored() {
|
||||
l.Debugf("%v REQ(in) for ignored file: %s: %q / %q o=%d s=%d", m, deviceID, folder, name, offset, size)
|
||||
l.Debugf("%v REQ(in) for ignored file: %s: %q / %q o=%d s=%d", m, deviceID.Short(), folder, name, offset, size)
|
||||
return nil, protocol.ErrInvalid
|
||||
}
|
||||
|
||||
@ -1908,7 +2039,7 @@ func (m *model) Request(conn protocol.Connection, folder, name string, _, size i
|
||||
folderFs := folderCfg.Filesystem(nil)
|
||||
|
||||
if err := osutil.TraversesSymlink(folderFs, filepath.Dir(name)); err != nil {
|
||||
l.Debugf("%v REQ(in) traversal check: %s - %s: %q / %q o=%d s=%d", m, err, deviceID, folder, name, offset, size)
|
||||
l.Debugf("%v REQ(in) traversal check: %s - %s: %q / %q o=%d s=%d", m, err, deviceID.Short(), folder, name, offset, size)
|
||||
return nil, protocol.ErrNoSuchFile
|
||||
}
|
||||
|
||||
@ -1920,7 +2051,7 @@ func (m *model) Request(conn protocol.Connection, folder, name string, _, size i
|
||||
if info, err := folderFs.Lstat(tempFn); err != nil || !info.IsRegular() {
|
||||
// Reject reads for anything that doesn't exist or is something
|
||||
// other than a regular file.
|
||||
l.Debugf("%v REQ(in) failed stating temp file (%v): %s: %q / %q o=%d s=%d", m, err, deviceID, folder, name, offset, size)
|
||||
l.Debugf("%v REQ(in) failed stating temp file (%v): %s: %q / %q o=%d s=%d", m, err, deviceID.Short(), folder, name, offset, size)
|
||||
return nil, protocol.ErrNoSuchFile
|
||||
}
|
||||
_, err := readOffsetIntoBuf(folderFs, tempFn, offset, res.data)
|
||||
@ -1934,13 +2065,13 @@ func (m *model) Request(conn protocol.Connection, folder, name string, _, size i
|
||||
if info, err := folderFs.Lstat(name); err != nil || !info.IsRegular() {
|
||||
// Reject reads for anything that doesn't exist or is something
|
||||
// other than a regular file.
|
||||
l.Debugf("%v REQ(in) failed stating file (%v): %s: %q / %q o=%d s=%d", m, err, deviceID, folder, name, offset, size)
|
||||
l.Debugf("%v REQ(in) failed stating file (%v): %s: %q / %q o=%d s=%d", m, err, deviceID.Short(), folder, name, offset, size)
|
||||
return nil, protocol.ErrNoSuchFile
|
||||
}
|
||||
|
||||
n, err := readOffsetIntoBuf(folderFs, name, offset, res.data)
|
||||
if fs.IsNotExist(err) {
|
||||
l.Debugf("%v REQ(in) file doesn't exist: %s: %q / %q o=%d s=%d", m, deviceID, folder, name, offset, size)
|
||||
l.Debugf("%v REQ(in) file doesn't exist: %s: %q / %q o=%d s=%d", m, deviceID.Short(), folder, name, offset, size)
|
||||
return nil, protocol.ErrNoSuchFile
|
||||
} else if err == io.EOF {
|
||||
// Read beyond end of file. This might indicate a problem, or it
|
||||
@ -1949,13 +2080,13 @@ func (m *model) Request(conn protocol.Connection, folder, name string, _, size i
|
||||
// next step take care of it, by only hashing the part we actually
|
||||
// managed to read.
|
||||
} else if err != nil {
|
||||
l.Debugf("%v REQ(in) failed reading file (%v): %s: %q / %q o=%d s=%d", m, err, deviceID, folder, name, offset, size)
|
||||
l.Debugf("%v REQ(in) failed reading file (%v): %s: %q / %q o=%d s=%d", m, err, deviceID.Short(), folder, name, offset, size)
|
||||
return nil, protocol.ErrGeneric
|
||||
}
|
||||
|
||||
if folderCfg.Type != config.FolderTypeReceiveEncrypted && len(hash) > 0 && !scanner.Validate(res.data[:n], hash, weakHash) {
|
||||
m.recheckFile(deviceID, folder, name, offset, hash, weakHash)
|
||||
l.Debugf("%v REQ(in) failed validating data: %s: %q / %q o=%d s=%d", m, deviceID, folder, name, offset, size)
|
||||
l.Debugf("%v REQ(in) failed validating data: %s: %q / %q o=%d s=%d", m, deviceID.Short(), folder, name, offset, size)
|
||||
return nil, protocol.ErrNoSuchFile
|
||||
}
|
||||
|
||||
@ -2074,15 +2205,12 @@ func (m *model) GetMtimeMapping(folder string, file string) (fs.MtimeMapping, er
|
||||
return fs.GetMtimeMapping(fcfg.Filesystem(ffs), file)
|
||||
}
|
||||
|
||||
// Connection returns the current connection for device, and a boolean whether a connection was found.
|
||||
func (m *model) Connection(deviceID protocol.DeviceID) (protocol.Connection, bool) {
|
||||
// Connection returns if we are connected to the given device.
|
||||
func (m *model) ConnectedTo(deviceID protocol.DeviceID) bool {
|
||||
m.pmut.RLock()
|
||||
cn, ok := m.conn[deviceID]
|
||||
_, ok := m.deviceConnIDs[deviceID]
|
||||
m.pmut.RUnlock()
|
||||
if ok {
|
||||
m.deviceWasSeen(deviceID)
|
||||
}
|
||||
return cn, ok
|
||||
return ok
|
||||
}
|
||||
|
||||
// LoadIgnores loads or refreshes the ignore patterns from disk, if the
|
||||
@ -2200,74 +2328,29 @@ func (m *model) OnHello(remoteID protocol.DeviceID, addr net.Addr, hello protoco
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetHello is called when we are about to connect to some remote device.
|
||||
func (m *model) GetHello(id protocol.DeviceID) protocol.HelloIntf {
|
||||
name := ""
|
||||
if _, ok := m.cfg.Device(id); ok {
|
||||
// Set our name (from the config of our device ID) only if we already know about the other side device ID.
|
||||
if myCfg, ok := m.cfg.Device(m.id); ok {
|
||||
name = myCfg.Name
|
||||
}
|
||||
}
|
||||
return &protocol.Hello{
|
||||
DeviceName: name,
|
||||
ClientName: m.clientName,
|
||||
ClientVersion: m.clientVersion,
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// folder changes.
|
||||
func (m *model) AddConnection(conn protocol.Connection, hello protocol.Hello) {
|
||||
deviceID := conn.DeviceID()
|
||||
device, ok := m.cfg.Device(deviceID)
|
||||
deviceCfg, ok := m.cfg.Device(deviceID)
|
||||
if !ok {
|
||||
l.Infoln("Trying to add connection to unknown device")
|
||||
return
|
||||
}
|
||||
|
||||
// The slightly unusual locking sequence here is because we must acquire
|
||||
// fmut before pmut. (The locks can be *released* in any order.)
|
||||
m.fmut.RLock()
|
||||
m.pmut.Lock()
|
||||
if oldConn, ok := m.conn[deviceID]; ok {
|
||||
l.Infoln("Replacing old connection", oldConn, "with", conn, "for", deviceID)
|
||||
// There is an existing connection to this device that we are
|
||||
// replacing. We must close the existing connection and wait for the
|
||||
// close to complete before adding the new connection. We do the
|
||||
// actual close without holding pmut as the connection will call
|
||||
// back into Closed() for the cleanup.
|
||||
closed := m.closed[deviceID]
|
||||
m.fmut.RUnlock()
|
||||
m.pmut.Unlock()
|
||||
oldConn.Close(errReplacingConnection)
|
||||
<-closed
|
||||
// Again, lock fmut before pmut.
|
||||
m.fmut.RLock()
|
||||
m.pmut.Lock()
|
||||
}
|
||||
|
||||
m.conn[deviceID] = conn
|
||||
connID := conn.ConnectionID()
|
||||
closed := make(chan struct{})
|
||||
m.closed[deviceID] = closed
|
||||
m.deviceDownloads[deviceID] = newDeviceDownloadState()
|
||||
indexRegistry := newIndexHandlerRegistry(conn, m.deviceDownloads[deviceID], m.evLogger)
|
||||
for id, fcfg := range m.folderCfgs {
|
||||
runner, _ := m.folderRunners.Get(id)
|
||||
indexRegistry.RegisterFolderState(fcfg, m.folderFiles[id], runner)
|
||||
}
|
||||
m.indexHandlers.Add(deviceID, indexRegistry)
|
||||
m.fmut.RUnlock()
|
||||
// 0: default, <0: no limiting
|
||||
switch {
|
||||
case device.MaxRequestKiB > 0:
|
||||
m.connRequestLimiters[deviceID] = semaphore.New(1024 * device.MaxRequestKiB)
|
||||
case device.MaxRequestKiB == 0:
|
||||
m.connRequestLimiters[deviceID] = semaphore.New(1024 * defaultPullerPendingKiB)
|
||||
}
|
||||
|
||||
m.pmut.Lock()
|
||||
|
||||
m.connections[connID] = conn
|
||||
m.closed[connID] = closed
|
||||
m.helloMessages[deviceID] = hello
|
||||
m.deviceConnIDs[deviceID] = append(m.deviceConnIDs[deviceID], connID)
|
||||
if m.deviceDownloads[deviceID] == nil {
|
||||
m.deviceDownloads[deviceID] = newDeviceDownloadState()
|
||||
}
|
||||
|
||||
event := map[string]string{
|
||||
"id": deviceID.String(),
|
||||
@ -2284,17 +2367,15 @@ func (m *model) AddConnection(conn protocol.Connection, hello protocol.Hello) {
|
||||
|
||||
m.evLogger.Log(events.DeviceConnected, event)
|
||||
|
||||
l.Infof(`Device %s client is "%s %s" named "%s" at %s`, deviceID, hello.ClientName, hello.ClientVersion, hello.DeviceName, conn)
|
||||
if len(m.deviceConnIDs[deviceID]) == 1 {
|
||||
l.Infof(`Device %s client is "%s %s" named "%s" at %s`, deviceID.Short(), hello.ClientName, hello.ClientVersion, hello.DeviceName, conn)
|
||||
} else {
|
||||
l.Infof(`Additional connection (+%d) for device %s at %s`, len(m.deviceConnIDs[deviceID])-1, deviceID.Short(), conn)
|
||||
}
|
||||
|
||||
conn.Start()
|
||||
m.pmut.Unlock()
|
||||
|
||||
// Acquires fmut, so has to be done outside of pmut.
|
||||
cm, passwords := m.generateClusterConfig(deviceID)
|
||||
conn.SetFolderPasswords(passwords)
|
||||
conn.ClusterConfig(cm)
|
||||
|
||||
if (device.Name == "" || m.cfg.Options().OverwriteRemoteDevNames) && hello.DeviceName != "" {
|
||||
if (deviceCfg.Name == "" || m.cfg.Options().OverwriteRemoteDevNames) && hello.DeviceName != "" {
|
||||
m.cfg.Modify(func(cfg *config.Configuration) {
|
||||
for i := range cfg.Devices {
|
||||
if cfg.Devices[i].DeviceID == deviceID {
|
||||
@ -2308,26 +2389,78 @@ func (m *model) AddConnection(conn protocol.Connection, hello protocol.Hello) {
|
||||
}
|
||||
|
||||
m.deviceWasSeen(deviceID)
|
||||
m.scheduleConnectionPromotion()
|
||||
}
|
||||
|
||||
func (m *model) scheduleConnectionPromotion() {
|
||||
// Keeps deferring to prevent multiple executions in quick succession,
|
||||
// e.g. if multiple connections to a single device are closed.
|
||||
m.promotionTimer.Reset(time.Second)
|
||||
}
|
||||
|
||||
// promoteConnections checks for devices that have connections, but where
|
||||
// the primary connection hasn't started index handlers etc. yet, and
|
||||
// promotes the primary connection to be the index handling one. This should
|
||||
// be called after adding new connections, and after closing a primary
|
||||
// device connection.
|
||||
func (m *model) promoteConnections() {
|
||||
m.fmut.RLock() // for generateClusterConfigFRLocked
|
||||
defer m.fmut.RUnlock()
|
||||
|
||||
m.pmut.Lock() // for most other things
|
||||
defer m.pmut.Unlock()
|
||||
|
||||
for deviceID, connIDs := range m.deviceConnIDs {
|
||||
cm, passwords := m.generateClusterConfigFRLocked(deviceID)
|
||||
if m.promotedConnID[deviceID] != connIDs[0] {
|
||||
// The previously promoted connection is not the current
|
||||
// primary; we should promote the primary connection to be the
|
||||
// index handling one. We do this by sending a ClusterConfig on
|
||||
// it, which will cause the other side to start sending us index
|
||||
// messages there. (On our side, we manage index handlers based
|
||||
// on where we get ClusterConfigs from the peer.)
|
||||
conn := m.connections[connIDs[0]]
|
||||
l.Debugf("Promoting connection to %s at %s", deviceID.Short(), conn)
|
||||
if conn.Statistics().StartedAt.IsZero() {
|
||||
conn.SetFolderPasswords(passwords)
|
||||
conn.Start()
|
||||
}
|
||||
conn.ClusterConfig(cm)
|
||||
m.promotedConnID[deviceID] = connIDs[0]
|
||||
}
|
||||
|
||||
// Make sure any other new connections also get started, and that
|
||||
// they get a secondary-marked ClusterConfig.
|
||||
for _, connID := range connIDs[1:] {
|
||||
conn := m.connections[connID]
|
||||
if conn.Statistics().StartedAt.IsZero() {
|
||||
conn.SetFolderPasswords(passwords)
|
||||
conn.Start()
|
||||
conn.ClusterConfig(protocol.ClusterConfig{Secondary: true})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *model) DownloadProgress(conn protocol.Connection, folder string, updates []protocol.FileDownloadProgressUpdate) error {
|
||||
deviceID := conn.DeviceID()
|
||||
|
||||
m.fmut.RLock()
|
||||
cfg, ok := m.folderCfgs[folder]
|
||||
m.fmut.RUnlock()
|
||||
|
||||
device := conn.DeviceID()
|
||||
if !ok || cfg.DisableTempIndexes || !cfg.SharedWith(device) {
|
||||
if !ok || cfg.DisableTempIndexes || !cfg.SharedWith(deviceID) {
|
||||
return nil
|
||||
}
|
||||
|
||||
m.pmut.RLock()
|
||||
downloads := m.deviceDownloads[device]
|
||||
downloads := m.deviceDownloads[deviceID]
|
||||
m.pmut.RUnlock()
|
||||
downloads.Update(folder, updates)
|
||||
state := downloads.GetBlockCounts(folder)
|
||||
|
||||
m.evLogger.Log(events.RemoteDownloadProgress, map[string]interface{}{
|
||||
"device": device.String(),
|
||||
"device": deviceID.String(),
|
||||
"folder": folder,
|
||||
"state": state,
|
||||
})
|
||||
@ -2344,27 +2477,47 @@ func (m *model) deviceWasSeen(deviceID protocol.DeviceID) {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *model) deviceDidClose(deviceID protocol.DeviceID, duration time.Duration) {
|
||||
m.fmut.RLock()
|
||||
sr, ok := m.deviceStatRefs[deviceID]
|
||||
m.fmut.RUnlock()
|
||||
if ok {
|
||||
func (m *model) deviceDidCloseFRLocked(deviceID protocol.DeviceID, duration time.Duration) {
|
||||
if sr, ok := m.deviceStatRefs[deviceID]; ok {
|
||||
_ = sr.LastConnectionDuration(duration)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *model) requestGlobal(ctx context.Context, deviceID protocol.DeviceID, folder, name string, blockNo int, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) {
|
||||
m.pmut.RLock()
|
||||
nc, ok := m.conn[deviceID]
|
||||
m.pmut.RUnlock()
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("requestGlobal: no such device: %s", deviceID)
|
||||
conn, connOK := m.requestConnectionForDevice(deviceID)
|
||||
if !connOK {
|
||||
return nil, fmt.Errorf("requestGlobal: no connection to device: %s", deviceID.Short())
|
||||
}
|
||||
|
||||
l.Debugf("%v REQ(out): %s: %q / %q b=%d o=%d s=%d h=%x wh=%x ft=%t", m, deviceID, folder, name, blockNo, offset, size, hash, weakHash, fromTemporary)
|
||||
l.Debugf("%v REQ(out): %s (%s): %q / %q b=%d o=%d s=%d h=%x wh=%x ft=%t", m, deviceID.Short(), conn, folder, name, blockNo, offset, size, hash, weakHash, fromTemporary)
|
||||
return conn.Request(ctx, folder, name, blockNo, offset, size, hash, weakHash, fromTemporary)
|
||||
}
|
||||
|
||||
return nc.Request(ctx, folder, name, blockNo, offset, size, hash, weakHash, fromTemporary)
|
||||
// requestConnectionForDevice returns a connection to the given device, to
|
||||
// be used for sending a request. If there is only one device connection,
|
||||
// this is the one to use. If there are multiple then we avoid the first
|
||||
// ("primary") connection, which is dedicated to index data, and pick a
|
||||
// random one of the others.
|
||||
func (m *model) requestConnectionForDevice(deviceID protocol.DeviceID) (protocol.Connection, bool) {
|
||||
m.pmut.RLock()
|
||||
defer m.pmut.RUnlock()
|
||||
|
||||
connIDs, ok := m.deviceConnIDs[deviceID]
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// If there is an entry in deviceConns, it always contains at least one
|
||||
// connection.
|
||||
connID := connIDs[0]
|
||||
if len(connIDs) > 1 {
|
||||
// Pick a random connection of the non-primary ones
|
||||
idx := rand.Intn(len(connIDs)-1) + 1
|
||||
connID = connIDs[idx]
|
||||
}
|
||||
|
||||
conn, connOK := m.connections[connID]
|
||||
return conn, connOK
|
||||
}
|
||||
|
||||
func (m *model) ScanFolders() map[string]error {
|
||||
@ -2455,11 +2608,13 @@ func (m *model) numHashers(folder string) int {
|
||||
// generateClusterConfig returns a ClusterConfigMessage that is correct and the
|
||||
// set of folder passwords for the given peer device
|
||||
func (m *model) generateClusterConfig(device protocol.DeviceID) (protocol.ClusterConfig, map[string]string) {
|
||||
var message protocol.ClusterConfig
|
||||
|
||||
m.fmut.RLock()
|
||||
defer m.fmut.RUnlock()
|
||||
return m.generateClusterConfigFRLocked(device)
|
||||
}
|
||||
|
||||
func (m *model) generateClusterConfigFRLocked(device protocol.DeviceID) (protocol.ClusterConfig, map[string]string) {
|
||||
var message protocol.ClusterConfig
|
||||
folders := m.cfg.FolderList()
|
||||
passwords := make(map[string]string, len(folders))
|
||||
for _, folderCfg := range folders {
|
||||
@ -2771,7 +2926,7 @@ func (m *model) availabilityInSnapshotPRlocked(cfg config.FolderConfiguration, s
|
||||
if state := m.remoteFolderStates[device][cfg.ID]; state != remoteFolderValid {
|
||||
continue
|
||||
}
|
||||
_, ok := m.conn[device]
|
||||
_, ok := m.deviceConnIDs[device]
|
||||
if ok {
|
||||
availabilities = append(availabilities, Availability{ID: device, FromTemporary: false})
|
||||
}
|
||||
@ -2904,13 +3059,6 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// Removing a device. We actually don't need to do anything.
|
||||
// Because folder config has changed (since the device lists do not match)
|
||||
// Folders for that had device got "restarted", which involves killing
|
||||
// connections to all devices that we were sharing the folder with.
|
||||
// At some point model.Close() will get called for that device which will
|
||||
// clean residue device state that is not part of any folder.
|
||||
|
||||
// Pausing a device, unpausing is handled by the connection service.
|
||||
fromDevices := from.DeviceMap()
|
||||
toDevices := to.DeviceMap()
|
||||
@ -2941,7 +3089,14 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
|
||||
l.Infoln("Resuming", deviceID)
|
||||
m.evLogger.Log(events.DeviceResumed, map[string]string{"device": deviceID.String()})
|
||||
}
|
||||
|
||||
if toCfg.MaxRequestKiB != fromCfg.MaxRequestKiB {
|
||||
m.pmut.Lock()
|
||||
m.setConnRequestLimitersPLocked(toCfg)
|
||||
m.pmut.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up after removed devices
|
||||
removedDevices := make([]protocol.DeviceID, 0, len(fromDevices))
|
||||
m.fmut.Lock()
|
||||
@ -2955,14 +3110,18 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
|
||||
m.pmut.RLock()
|
||||
for _, id := range closeDevices {
|
||||
delete(clusterConfigDevices, id)
|
||||
if conn, ok := m.conn[id]; ok {
|
||||
go conn.Close(errDevicePaused)
|
||||
if conns, ok := m.deviceConnIDs[id]; ok {
|
||||
for _, connID := range conns {
|
||||
go m.connections[connID].Close(errDevicePaused)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, id := range removedDevices {
|
||||
delete(clusterConfigDevices, id)
|
||||
if conn, ok := m.conn[id]; ok {
|
||||
go conn.Close(errDeviceRemoved)
|
||||
if conns, ok := m.deviceConnIDs[id]; ok {
|
||||
for _, connID := range conns {
|
||||
go m.connections[connID].Close(errDevicePaused)
|
||||
}
|
||||
}
|
||||
}
|
||||
m.pmut.RUnlock()
|
||||
@ -2986,6 +3145,17 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *model) setConnRequestLimitersPLocked(cfg config.DeviceConfiguration) {
|
||||
// Touches connRequestLimiters which is protected by pmut.
|
||||
// 0: default, <0: no limiting
|
||||
switch {
|
||||
case cfg.MaxRequestKiB > 0:
|
||||
m.connRequestLimiters[cfg.DeviceID] = semaphore.New(1024 * cfg.MaxRequestKiB)
|
||||
case cfg.MaxRequestKiB == 0:
|
||||
m.connRequestLimiters[cfg.DeviceID] = semaphore.New(1024 * defaultPullerPendingKiB)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *model) cleanPending(existingDevices map[protocol.DeviceID]config.DeviceConfiguration, existingFolders map[string]config.FolderConfiguration, ignoredDevices deviceIDSet, removedFolders map[string]struct{}) {
|
||||
var removedPendingFolders []map[string]string
|
||||
pendingFolders, err := m.db.PendingFolders()
|
||||
@ -3332,3 +3502,12 @@ type redactedError struct {
|
||||
error
|
||||
redacted error
|
||||
}
|
||||
|
||||
func without[E comparable, S ~[]E](s S, e E) S {
|
||||
for i, x := range s {
|
||||
if x == e {
|
||||
return append(s[:i], s[i+1:]...)
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
@ -270,7 +270,7 @@ func TestDeviceRename(t *testing.T) {
|
||||
cfg, cfgCancel := newConfigWrapper(rawCfg)
|
||||
defer cfgCancel()
|
||||
|
||||
m := newModel(t, cfg, myID, "syncthing", "dev", nil)
|
||||
m := newModel(t, cfg, myID, nil)
|
||||
|
||||
if cfg.Devices()[device1].Name != "" {
|
||||
t.Errorf("Device already has a name")
|
||||
@ -422,7 +422,7 @@ func TestClusterConfig(t *testing.T) {
|
||||
|
||||
wrapper, cancel := newConfigWrapper(cfg)
|
||||
defer cancel()
|
||||
m := newModel(t, wrapper, myID, "syncthing", "dev", nil)
|
||||
m := newModel(t, wrapper, myID, nil)
|
||||
m.ServeBackground()
|
||||
defer cleanupModel(m)
|
||||
|
||||
@ -903,7 +903,7 @@ func TestIssue5063(t *testing.T) {
|
||||
defer cancel()
|
||||
|
||||
m.pmut.Lock()
|
||||
for _, c := range m.conn {
|
||||
for _, c := range m.connections {
|
||||
conn := c.(*fakeConnection)
|
||||
conn.CloseCalls(func(_ error) {})
|
||||
defer m.Closed(c, errStopped) // to unblock deferred m.Stop()
|
||||
@ -1626,7 +1626,7 @@ func TestROScanRecovery(t *testing.T) {
|
||||
},
|
||||
})
|
||||
defer cancel()
|
||||
m := newModel(t, cfg, myID, "syncthing", "dev", nil)
|
||||
m := newModel(t, cfg, myID, nil)
|
||||
|
||||
set := newFileSet(t, "default", m.db)
|
||||
set.Update(protocol.LocalDeviceID, []protocol.FileInfo{
|
||||
@ -1673,7 +1673,7 @@ func TestRWScanRecovery(t *testing.T) {
|
||||
},
|
||||
})
|
||||
defer cancel()
|
||||
m := newModel(t, cfg, myID, "syncthing", "dev", nil)
|
||||
m := newModel(t, cfg, myID, nil)
|
||||
|
||||
set := newFileSet(t, "default", m.db)
|
||||
set.Update(protocol.LocalDeviceID, []protocol.FileInfo{
|
||||
@ -2073,7 +2073,7 @@ func TestIssue4357(t *testing.T) {
|
||||
// Create a separate wrapper not to pollute other tests.
|
||||
wrapper, cancel := newConfigWrapper(config.Configuration{Version: config.CurrentVersion})
|
||||
defer cancel()
|
||||
m := newModel(t, wrapper, myID, "syncthing", "dev", nil)
|
||||
m := newModel(t, wrapper, myID, nil)
|
||||
m.ServeBackground()
|
||||
defer cleanupModel(m)
|
||||
|
||||
@ -2125,7 +2125,7 @@ func TestIssue4357(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIndexesForUnknownDevicesDropped(t *testing.T) {
|
||||
m := newModel(t, defaultCfgWrapper, myID, "syncthing", "dev", nil)
|
||||
m := newModel(t, defaultCfgWrapper, myID, nil)
|
||||
|
||||
files := newFileSet(t, "default", m.db)
|
||||
files.Drop(device1)
|
||||
@ -2231,7 +2231,7 @@ func TestSharedWithClearedOnDisconnect(t *testing.T) {
|
||||
t.Error("device still in config")
|
||||
}
|
||||
|
||||
if _, ok := m.conn[device2]; ok {
|
||||
if _, ok := m.deviceConnIDs[device2]; ok {
|
||||
t.Error("conn not missing")
|
||||
}
|
||||
|
||||
@ -2374,7 +2374,7 @@ func TestCustomMarkerName(t *testing.T) {
|
||||
|
||||
ffs := fcfg.Filesystem(nil)
|
||||
|
||||
m := newModel(t, cfg, myID, "syncthing", "dev", nil)
|
||||
m := newModel(t, cfg, myID, nil)
|
||||
|
||||
set := newFileSet(t, "default", m.db)
|
||||
set.Update(protocol.LocalDeviceID, []protocol.FileInfo{
|
||||
@ -2736,7 +2736,7 @@ func TestIssue4094(t *testing.T) {
|
||||
// Create a separate wrapper not to pollute other tests.
|
||||
wrapper, cancel := newConfigWrapper(config.Configuration{Version: config.CurrentVersion})
|
||||
defer cancel()
|
||||
m := newModel(t, wrapper, myID, "syncthing", "dev", nil)
|
||||
m := newModel(t, wrapper, myID, nil)
|
||||
m.ServeBackground()
|
||||
defer cleanupModel(m)
|
||||
|
||||
@ -2971,6 +2971,7 @@ func TestConnCloseOnRestart(t *testing.T) {
|
||||
br := &testutil.BlockingRW{}
|
||||
nw := &testutil.NoopRW{}
|
||||
ci := &protocolmocks.ConnectionInfo{}
|
||||
ci.ConnectionIDReturns(srand.String(16))
|
||||
m.AddConnection(protocol.NewConnection(device1, br, nw, testutil.NoopCloser{}, m, ci, protocol.CompressionNever, nil, m.keyGen), protocol.Hello{})
|
||||
m.pmut.RLock()
|
||||
if len(m.closed) != 1 {
|
||||
@ -3639,6 +3640,7 @@ func testConfigChangeTriggersClusterConfigs(t *testing.T, expectFirst, expectSec
|
||||
})
|
||||
m.AddConnection(fc1, protocol.Hello{})
|
||||
m.AddConnection(fc2, protocol.Hello{})
|
||||
m.promoteConnections()
|
||||
|
||||
// Initial CCs
|
||||
select {
|
||||
@ -3690,7 +3692,7 @@ func TestIssue6961(t *testing.T) {
|
||||
must(t, err)
|
||||
waiter.Wait()
|
||||
// Always recalc/repair when opening a fileset.
|
||||
m := newModel(t, wcfg, myID, "syncthing", "dev", nil)
|
||||
m := newModel(t, wcfg, myID, nil)
|
||||
m.db.Close()
|
||||
m.db, err = db.NewLowlevel(backend.OpenMemory(), m.evLogger, db.WithRecheckInterval(time.Millisecond))
|
||||
if err != nil {
|
||||
@ -3952,7 +3954,7 @@ func TestCCFolderNotRunning(t *testing.T) {
|
||||
w, fcfg, wCancel := newDefaultCfgWrapper()
|
||||
defer wCancel()
|
||||
tfs := fcfg.Filesystem(nil)
|
||||
m := newModel(t, w, myID, "syncthing", "dev", nil)
|
||||
m := newModel(t, w, myID, nil)
|
||||
defer cleanupModelAndRemoveDir(m, tfs.URI())
|
||||
|
||||
// A connection can happen before all the folders are started.
|
||||
|
@ -1214,7 +1214,7 @@ func TestRequestIndexSenderClusterConfigBeforeStart(t *testing.T) {
|
||||
|
||||
// Initialise db with an entry and then stop everything again
|
||||
must(t, tfs.Mkdir(dir1, 0o777))
|
||||
m := newModel(t, w, myID, "syncthing", "dev", nil)
|
||||
m := newModel(t, w, myID, nil)
|
||||
defer cleanupModelAndRemoveDir(m, tfs.URI())
|
||||
m.ServeBackground()
|
||||
m.ScanFolders()
|
||||
@ -1223,7 +1223,7 @@ func TestRequestIndexSenderClusterConfigBeforeStart(t *testing.T) {
|
||||
|
||||
// Add connection (sends incoming cluster config) before starting the new model
|
||||
m = &testModel{
|
||||
model: NewModel(m.cfg, m.id, m.clientName, m.clientVersion, m.db, m.protectedFiles, m.evLogger, protocol.NewKeyGenerator()).(*model),
|
||||
model: NewModel(m.cfg, m.id, m.db, m.protectedFiles, m.evLogger, protocol.NewKeyGenerator()).(*model),
|
||||
evCancel: m.evCancel,
|
||||
stopped: make(chan struct{}),
|
||||
}
|
||||
|
@ -39,7 +39,9 @@ func init() {
|
||||
device1, _ = protocol.DeviceIDFromString("AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR")
|
||||
device2, _ = protocol.DeviceIDFromString("GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY")
|
||||
device1Conn.DeviceIDReturns(device1)
|
||||
device1Conn.ConnectionIDReturns(rand.String(16))
|
||||
device2Conn.DeviceIDReturns(device2)
|
||||
device2Conn.ConnectionIDReturns(rand.String(16))
|
||||
|
||||
cfg := config.New(myID)
|
||||
cfg.Options.MinHomeDiskFree.Value = 0 // avoids unnecessary free space checks
|
||||
@ -127,7 +129,7 @@ func setupModelWithConnectionFromWrapper(t testing.TB, w config.Wrapper) (*testM
|
||||
|
||||
func setupModel(t testing.TB, w config.Wrapper) *testModel {
|
||||
t.Helper()
|
||||
m := newModel(t, w, myID, "syncthing", "dev", nil)
|
||||
m := newModel(t, w, myID, nil)
|
||||
m.ServeBackground()
|
||||
<-m.started
|
||||
|
||||
@ -144,14 +146,14 @@ type testModel struct {
|
||||
stopped chan struct{}
|
||||
}
|
||||
|
||||
func newModel(t testing.TB, cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersion string, protectedFiles []string) *testModel {
|
||||
func newModel(t testing.TB, cfg config.Wrapper, id protocol.DeviceID, protectedFiles []string) *testModel {
|
||||
t.Helper()
|
||||
evLogger := events.NewLogger()
|
||||
ldb, err := db.NewLowlevel(backend.OpenMemory(), evLogger)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m := NewModel(cfg, id, clientName, clientVersion, ldb, protectedFiles, evLogger, protocol.NewKeyGenerator()).(*model)
|
||||
m := NewModel(cfg, id, ldb, protectedFiles, evLogger, protocol.NewKeyGenerator()).(*model)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
go evLogger.Serve(ctx)
|
||||
return &testModel{
|
||||
|
@ -211,9 +211,11 @@ func (FileDownloadProgressUpdateType) EnumDescriptor() ([]byte, []int) {
|
||||
}
|
||||
|
||||
type Hello struct {
|
||||
DeviceName string `protobuf:"bytes,1,opt,name=device_name,json=deviceName,proto3" json:"deviceName" xml:"deviceName"`
|
||||
ClientName string `protobuf:"bytes,2,opt,name=client_name,json=clientName,proto3" json:"clientName" xml:"clientName"`
|
||||
ClientVersion string `protobuf:"bytes,3,opt,name=client_version,json=clientVersion,proto3" json:"clientVersion" xml:"clientVersion"`
|
||||
DeviceName string `protobuf:"bytes,1,opt,name=device_name,json=deviceName,proto3" json:"deviceName" xml:"deviceName"`
|
||||
ClientName string `protobuf:"bytes,2,opt,name=client_name,json=clientName,proto3" json:"clientName" xml:"clientName"`
|
||||
ClientVersion string `protobuf:"bytes,3,opt,name=client_version,json=clientVersion,proto3" json:"clientVersion" xml:"clientVersion"`
|
||||
NumConnections int `protobuf:"varint,4,opt,name=num_connections,json=numConnections,proto3,casttype=int" json:"numConnections" xml:"numConnections"`
|
||||
Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp" xml:"timestamp"`
|
||||
}
|
||||
|
||||
func (m *Hello) Reset() { *m = Hello{} }
|
||||
@ -288,7 +290,8 @@ func (m *Header) XXX_DiscardUnknown() {
|
||||
var xxx_messageInfo_Header proto.InternalMessageInfo
|
||||
|
||||
type ClusterConfig struct {
|
||||
Folders []Folder `protobuf:"bytes,1,rep,name=folders,proto3" json:"folders" xml:"folder"`
|
||||
Folders []Folder `protobuf:"bytes,1,rep,name=folders,proto3" json:"folders" xml:"folder"`
|
||||
Secondary bool `protobuf:"varint,2,opt,name=secondary,proto3" json:"secondary" xml:"secondary"`
|
||||
}
|
||||
|
||||
func (m *ClusterConfig) Reset() { *m = ClusterConfig{} }
|
||||
@ -1142,205 +1145,211 @@ func init() {
|
||||
func init() { proto.RegisterFile("lib/protocol/bep.proto", fileDescriptor_311ef540e10d9705) }
|
||||
|
||||
var fileDescriptor_311ef540e10d9705 = []byte{
|
||||
// 3163 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x5a, 0x4d, 0x6c, 0x1b, 0xc7,
|
||||
0xbd, 0x17, 0xc5, 0x0f, 0x51, 0x23, 0xc9, 0xa6, 0xc6, 0x5f, 0x0c, 0x6d, 0x6b, 0xf9, 0x26, 0xce,
|
||||
0x7b, 0x8a, 0xf2, 0x62, 0x27, 0xca, 0xc7, 0xcb, 0x8b, 0xf3, 0x1c, 0x88, 0x22, 0x25, 0x33, 0x96,
|
||||
0x49, 0x65, 0x28, 0xdb, 0xb1, 0xf1, 0x1e, 0x88, 0x15, 0x77, 0x44, 0x2d, 0x4c, 0xee, 0xf2, 0xed,
|
||||
0x52, 0x5f, 0x41, 0x2f, 0x6d, 0x80, 0x20, 0xd0, 0xa1, 0x28, 0x72, 0x2a, 0x8a, 0x0a, 0x0d, 0x7a,
|
||||
0xe9, 0xad, 0x40, 0x0f, 0xbd, 0xe4, 0xd4, 0xa3, 0x8f, 0x46, 0x80, 0x02, 0x45, 0x0f, 0x0b, 0xc4,
|
||||
0xbe, 0xb4, 0xec, 0x8d, 0xc7, 0x9e, 0x8a, 0xf9, 0xcf, 0xec, 0xec, 0xac, 0x3e, 0x52, 0x39, 0x39,
|
||||
0xf4, 0x64, 0xfe, 0x7f, 0xff, 0xdf, 0xff, 0xbf, 0xb3, 0xf3, 0xff, 0x9a, 0x59, 0x19, 0x5d, 0xec,
|
||||
0xd8, 0xeb, 0x37, 0x7a, 0x9e, 0xdb, 0x77, 0x5b, 0x6e, 0xe7, 0xc6, 0x3a, 0xeb, 0x5d, 0x07, 0x01,
|
||||
0x67, 0x43, 0xac, 0x30, 0xce, 0x76, 0xfb, 0x02, 0x2c, 0xbc, 0xec, 0xb1, 0x9e, 0xeb, 0x0b, 0xfa,
|
||||
0xfa, 0xd6, 0xc6, 0x8d, 0xb6, 0xdb, 0x76, 0x41, 0x80, 0x5f, 0x82, 0x44, 0x9e, 0x25, 0x50, 0xfa,
|
||||
0x36, 0xeb, 0x74, 0x5c, 0xbc, 0x88, 0x26, 0x2c, 0xb6, 0x6d, 0xb7, 0x58, 0xd3, 0x31, 0xbb, 0x2c,
|
||||
0x9f, 0x28, 0x26, 0x66, 0xc7, 0x4b, 0x64, 0x10, 0x18, 0x48, 0xc0, 0x35, 0xb3, 0xcb, 0x86, 0x81,
|
||||
0x91, 0xdb, 0xed, 0x76, 0xde, 0x27, 0x11, 0x44, 0xa8, 0xa6, 0xe7, 0x4e, 0x5a, 0x1d, 0x9b, 0x39,
|
||||
0x7d, 0xe1, 0x64, 0x34, 0x72, 0x22, 0xe0, 0x98, 0x93, 0x08, 0x22, 0x54, 0xd3, 0xe3, 0x3a, 0x3a,
|
||||
0x23, 0x9d, 0x6c, 0x33, 0xcf, 0xb7, 0x5d, 0x27, 0x9f, 0x04, 0x3f, 0xb3, 0x83, 0xc0, 0x98, 0x12,
|
||||
0x9a, 0xfb, 0x42, 0x31, 0x0c, 0x8c, 0x73, 0x9a, 0x2b, 0x89, 0x12, 0x1a, 0x67, 0x91, 0xdf, 0x25,
|
||||
0x50, 0xe6, 0x36, 0x33, 0x2d, 0xe6, 0xe1, 0x05, 0x94, 0xea, 0xef, 0xf5, 0xc4, 0xeb, 0x9d, 0x99,
|
||||
0xbf, 0x70, 0x3d, 0xdc, 0xb8, 0xeb, 0x77, 0x99, 0xef, 0x9b, 0x6d, 0xb6, 0xb6, 0xd7, 0x63, 0xa5,
|
||||
0x8b, 0x83, 0xc0, 0x00, 0xda, 0x30, 0x30, 0x10, 0xf8, 0xe7, 0x02, 0xa1, 0x80, 0x61, 0x0b, 0x4d,
|
||||
0xb4, 0xdc, 0x6e, 0xcf, 0x63, 0x3e, 0xac, 0x6d, 0x14, 0x3c, 0x5d, 0x39, 0xe2, 0x69, 0x31, 0xe2,
|
||||
0x94, 0xae, 0x0d, 0x02, 0x43, 0x37, 0x1a, 0x06, 0xc6, 0xb4, 0x58, 0x77, 0x84, 0x11, 0xaa, 0x33,
|
||||
0xc8, 0xff, 0xa2, 0xa9, 0xc5, 0xce, 0x96, 0xdf, 0x67, 0xde, 0xa2, 0xeb, 0x6c, 0xd8, 0x6d, 0x7c,
|
||||
0x07, 0x8d, 0x6d, 0xb8, 0x1d, 0x8b, 0x79, 0x7e, 0x3e, 0x51, 0x4c, 0xce, 0x4e, 0xcc, 0xe7, 0xa2,
|
||||
0x47, 0x2e, 0x81, 0xa2, 0x64, 0x3c, 0x09, 0x8c, 0x91, 0x41, 0x60, 0x84, 0xc4, 0x61, 0x60, 0x4c,
|
||||
0xc2, 0x63, 0x84, 0x4c, 0x68, 0xa8, 0x20, 0x5f, 0xa7, 0x50, 0x46, 0x18, 0xe1, 0xeb, 0x68, 0xd4,
|
||||
0xb6, 0x64, 0xb8, 0x67, 0x9e, 0x05, 0xc6, 0x68, 0xb5, 0x3c, 0x08, 0x8c, 0x51, 0xdb, 0x1a, 0x06,
|
||||
0x46, 0x16, 0xac, 0x6d, 0x8b, 0x7c, 0xf9, 0xf4, 0xda, 0x68, 0xb5, 0x4c, 0x47, 0x6d, 0x0b, 0x5f,
|
||||
0x47, 0xe9, 0x8e, 0xb9, 0xce, 0x3a, 0x32, 0xb8, 0xf9, 0x41, 0x60, 0x08, 0x60, 0x18, 0x18, 0x13,
|
||||
0xc0, 0x07, 0x89, 0x50, 0x81, 0xe2, 0x9b, 0x68, 0xdc, 0x63, 0xa6, 0xd5, 0x74, 0x9d, 0xce, 0x1e,
|
||||
0x04, 0x32, 0x5b, 0x9a, 0x19, 0x04, 0x46, 0x96, 0x83, 0x75, 0xa7, 0xb3, 0x37, 0x0c, 0x8c, 0x33,
|
||||
0x60, 0x16, 0x02, 0x84, 0x2a, 0x1d, 0x6e, 0x22, 0x6c, 0xb7, 0x1d, 0xd7, 0x63, 0xcd, 0x1e, 0xf3,
|
||||
0xba, 0x36, 0x6c, 0x8d, 0x9f, 0x4f, 0x81, 0x97, 0x37, 0x06, 0x81, 0x31, 0x2d, 0xb4, 0xab, 0x91,
|
||||
0x72, 0x18, 0x18, 0x97, 0xc4, 0xaa, 0x0f, 0x6b, 0x08, 0x3d, 0xca, 0xc6, 0x77, 0xd0, 0x94, 0x7c,
|
||||
0x80, 0xc5, 0x3a, 0xac, 0xcf, 0xf2, 0x69, 0xf0, 0xfd, 0xef, 0x83, 0xc0, 0x98, 0x14, 0x8a, 0x32,
|
||||
0xe0, 0xc3, 0xc0, 0xc0, 0x9a, 0x5b, 0x01, 0x12, 0x1a, 0xe3, 0x60, 0x0b, 0x9d, 0xb7, 0x6c, 0xdf,
|
||||
0x5c, 0xef, 0xb0, 0x66, 0x9f, 0x75, 0x7b, 0x4d, 0xdb, 0xb1, 0xd8, 0x2e, 0xf3, 0xf3, 0x19, 0xf0,
|
||||
0x39, 0x3f, 0x08, 0x0c, 0x2c, 0xf5, 0x6b, 0xac, 0xdb, 0xab, 0x0a, 0xed, 0x30, 0x30, 0xf2, 0xa2,
|
||||
0xa6, 0x8e, 0xa8, 0x08, 0x3d, 0x86, 0x8f, 0xe7, 0x51, 0xa6, 0x67, 0x6e, 0xf9, 0xcc, 0xca, 0x8f,
|
||||
0x81, 0xdf, 0xc2, 0x20, 0x30, 0x24, 0xa2, 0x02, 0x2e, 0x44, 0x42, 0x25, 0xce, 0x93, 0x47, 0x54,
|
||||
0xa9, 0x9f, 0xcf, 0x1d, 0x4e, 0x9e, 0x32, 0x28, 0xa2, 0xe4, 0x91, 0x44, 0xe5, 0x4b, 0xc8, 0x84,
|
||||
0x86, 0x0a, 0xf2, 0x87, 0x0c, 0xca, 0x08, 0x23, 0x5c, 0x52, 0xc9, 0x33, 0x59, 0x9a, 0xe7, 0x0e,
|
||||
0xfe, 0x1c, 0x18, 0x59, 0xa1, 0xab, 0x96, 0x4f, 0x4a, 0xa6, 0x2f, 0x9e, 0x5e, 0x4b, 0x68, 0x09,
|
||||
0x35, 0x87, 0x52, 0x5a, 0xb3, 0x80, 0xda, 0x73, 0x44, 0x9b, 0x10, 0xb5, 0xe7, 0x40, 0x83, 0x00,
|
||||
0x0c, 0x7f, 0x80, 0xc6, 0x4d, 0xcb, 0xe2, 0x35, 0xc2, 0xfc, 0x7c, 0xb2, 0x98, 0xe4, 0x39, 0x3b,
|
||||
0x08, 0x8c, 0x08, 0x1c, 0x06, 0xc6, 0x14, 0x58, 0x49, 0x84, 0xd0, 0x48, 0x87, 0xff, 0x2f, 0x5e,
|
||||
0xb9, 0xa9, 0xc3, 0x3d, 0xe0, 0x87, 0x95, 0x2c, 0xcf, 0xf4, 0x16, 0xf3, 0x64, 0xeb, 0x4b, 0x8b,
|
||||
0x82, 0xe2, 0x99, 0xce, 0x41, 0xd9, 0xf8, 0x44, 0xa6, 0x87, 0x00, 0xa1, 0x4a, 0x87, 0x97, 0xd1,
|
||||
0x64, 0xd7, 0xdc, 0x6d, 0xfa, 0xec, 0xff, 0xb7, 0x98, 0xd3, 0x62, 0x90, 0x33, 0x49, 0xb1, 0x8a,
|
||||
0xae, 0xb9, 0xdb, 0x90, 0xb0, 0x5a, 0x85, 0x86, 0x11, 0xaa, 0x33, 0x70, 0x09, 0x21, 0xdb, 0xe9,
|
||||
0x7b, 0xae, 0xb5, 0xd5, 0x62, 0x9e, 0x4c, 0x11, 0xe8, 0xc0, 0x11, 0xaa, 0x3a, 0x70, 0x04, 0x11,
|
||||
0xaa, 0xe9, 0x71, 0x1b, 0x65, 0x21, 0x77, 0x9b, 0xb6, 0x95, 0xcf, 0x16, 0x13, 0xb3, 0xa9, 0xd2,
|
||||
0x8a, 0x0c, 0xee, 0x18, 0x64, 0x21, 0xc4, 0x36, 0xfc, 0xc9, 0x73, 0x06, 0xd8, 0x55, 0x4b, 0xed,
|
||||
0xbe, 0x94, 0x79, 0xdf, 0x08, 0x69, 0xbf, 0x88, 0x7e, 0xd2, 0x90, 0x8f, 0x7f, 0x84, 0x0a, 0xfe,
|
||||
0x63, 0x9b, 0x57, 0x8a, 0x78, 0x76, 0xdf, 0x76, 0x9d, 0xa6, 0xc7, 0xba, 0xee, 0xb6, 0xd9, 0xf1,
|
||||
0xf3, 0xe3, 0xb0, 0xf8, 0x5b, 0x83, 0xc0, 0xc8, 0x73, 0x56, 0x55, 0x23, 0x51, 0xc9, 0x19, 0x06,
|
||||
0xc6, 0x0c, 0x3c, 0xf1, 0x24, 0x02, 0xa1, 0x27, 0xda, 0xe2, 0x5d, 0xf4, 0x12, 0x73, 0x5a, 0xde,
|
||||
0x5e, 0x0f, 0x1e, 0xdb, 0x33, 0x7d, 0x7f, 0xc7, 0xf5, 0xac, 0x66, 0xdf, 0x7d, 0xcc, 0x9c, 0x3c,
|
||||
0x82, 0xa4, 0xfe, 0x60, 0x10, 0x18, 0x97, 0x22, 0xd2, 0xaa, 0xe4, 0xac, 0x71, 0xca, 0x30, 0x30,
|
||||
0xae, 0xc2, 0xb3, 0x4f, 0xd0, 0x13, 0x7a, 0x92, 0x25, 0xf9, 0x49, 0x02, 0xa5, 0x61, 0x33, 0x78,
|
||||
0x35, 0x8b, 0xa6, 0x2c, 0x5b, 0x30, 0x54, 0xb3, 0x40, 0x8e, 0xb4, 0x6f, 0x89, 0xe3, 0x0a, 0x4a,
|
||||
0x6f, 0xd8, 0x1d, 0xe6, 0xe7, 0x47, 0xa1, 0x96, 0xb1, 0x36, 0x08, 0xec, 0x0e, 0xab, 0x3a, 0x1b,
|
||||
0x6e, 0xe9, 0xb2, 0xac, 0x66, 0x41, 0x54, 0xb5, 0xc4, 0x25, 0x42, 0x05, 0x48, 0xbe, 0x48, 0xa0,
|
||||
0x09, 0x58, 0xc4, 0xbd, 0x9e, 0x65, 0xf6, 0xd9, 0xbf, 0x72, 0x29, 0x9f, 0x4f, 0xa1, 0x6c, 0x68,
|
||||
0xa0, 0x1a, 0x42, 0xe2, 0x14, 0x0d, 0x61, 0x0e, 0xa5, 0x7c, 0xfb, 0x53, 0x06, 0x83, 0x25, 0x29,
|
||||
0xb8, 0x5c, 0x56, 0x5c, 0x2e, 0x10, 0x0a, 0x18, 0xfe, 0x10, 0xa1, 0xae, 0x6b, 0xd9, 0x1b, 0x36,
|
||||
0xb3, 0x9a, 0x3e, 0x14, 0x68, 0xb2, 0x54, 0xe4, 0xdd, 0x23, 0x44, 0x1b, 0xc3, 0xc0, 0x38, 0x2b,
|
||||
0xca, 0x2b, 0x44, 0x08, 0x8d, 0xb4, 0xbc, 0x7f, 0x28, 0x07, 0xeb, 0x7b, 0xf9, 0x49, 0xa8, 0x8c,
|
||||
0x0f, 0xc2, 0xca, 0x68, 0x6c, 0xba, 0x5e, 0x1f, 0xca, 0x41, 0x3d, 0xa6, 0xb4, 0xa7, 0x4a, 0x2d,
|
||||
0x82, 0x08, 0xaf, 0x04, 0x49, 0xa6, 0x1a, 0x15, 0xaf, 0xa0, 0xb1, 0xf0, 0xc0, 0xc3, 0x33, 0x3f,
|
||||
0xd6, 0xa4, 0xef, 0xb3, 0x56, 0xdf, 0xf5, 0x4a, 0xc5, 0xb0, 0x49, 0x6f, 0xab, 0x03, 0x90, 0x28,
|
||||
0xb8, 0xed, 0xf0, 0xe8, 0x13, 0x6a, 0xf0, 0xfb, 0x28, 0xab, 0x9a, 0x09, 0x82, 0x77, 0x85, 0x66,
|
||||
0xe4, 0x47, 0x9d, 0x44, 0x34, 0x23, 0x5f, 0xb5, 0x11, 0xa5, 0xc3, 0x1f, 0xa1, 0xcc, 0x7a, 0xc7,
|
||||
0x6d, 0x3d, 0x0e, 0xa7, 0xc5, 0xb9, 0x68, 0x21, 0x25, 0x8e, 0x43, 0x5c, 0xaf, 0xca, 0xb5, 0x48,
|
||||
0xaa, 0x1a, 0xff, 0x20, 0x12, 0x2a, 0x61, 0x7e, 0x9a, 0xf3, 0xf7, 0xba, 0x1d, 0xdb, 0x79, 0xdc,
|
||||
0xec, 0x9b, 0x5e, 0x9b, 0xf5, 0xf3, 0xd3, 0xd1, 0x69, 0x4e, 0x6a, 0xd6, 0x40, 0xa1, 0x4e, 0x73,
|
||||
0x31, 0x94, 0xd0, 0x38, 0x8b, 0x9f, 0x31, 0x85, 0xeb, 0xe6, 0xa6, 0xe9, 0x6f, 0xe6, 0x31, 0xd4,
|
||||
0x29, 0x74, 0x38, 0x01, 0xdf, 0x36, 0xfd, 0x4d, 0xb5, 0xed, 0x11, 0x44, 0xa8, 0xa6, 0xc7, 0xb7,
|
||||
0xd0, 0xb8, 0xac, 0x4d, 0x66, 0xe5, 0xcf, 0x81, 0x0b, 0x48, 0x05, 0x05, 0xaa, 0x54, 0x50, 0x08,
|
||||
0xa1, 0x91, 0x16, 0x97, 0xe4, 0x39, 0x52, 0x9c, 0xfe, 0x2e, 0x1e, 0x4d, 0xfb, 0x53, 0x1c, 0x24,
|
||||
0x97, 0xd0, 0xc4, 0xe1, 0x53, 0xcd, 0x94, 0xe8, 0xf8, 0xbd, 0xd8, 0x79, 0x46, 0x74, 0xfc, 0x9e,
|
||||
0x7e, 0x92, 0xd1, 0x19, 0xf8, 0x23, 0x2d, 0x2d, 0x1d, 0x3f, 0x3f, 0x51, 0x4c, 0xcc, 0xa6, 0x4b,
|
||||
0xaf, 0xea, 0x79, 0x58, 0xf3, 0x8f, 0xe4, 0x61, 0xcd, 0x27, 0x7f, 0x0f, 0x8c, 0xa4, 0xed, 0xf4,
|
||||
0xa9, 0x46, 0xc3, 0x1b, 0x48, 0xec, 0x52, 0x13, 0xaa, 0x6a, 0x0a, 0x5c, 0x2d, 0x3f, 0x0b, 0x8c,
|
||||
0x49, 0x6a, 0xee, 0x40, 0xe8, 0x1b, 0xf6, 0xa7, 0x8c, 0x6f, 0xd4, 0x7a, 0x28, 0xa8, 0x8d, 0x52,
|
||||
0x48, 0xe8, 0xf8, 0xcb, 0xa7, 0xd7, 0x62, 0x66, 0x34, 0x32, 0xc2, 0xf7, 0x51, 0xb6, 0xd7, 0x31,
|
||||
0xfb, 0x1b, 0xae, 0xd7, 0xcd, 0x9f, 0x81, 0x64, 0xd7, 0xf6, 0x70, 0x55, 0x6a, 0xca, 0x66, 0xdf,
|
||||
0x2c, 0x11, 0x99, 0x66, 0x8a, 0xaf, 0x32, 0x37, 0x04, 0x08, 0x55, 0x3a, 0x5c, 0x46, 0x13, 0x1d,
|
||||
0xb7, 0x65, 0x76, 0x9a, 0x1b, 0x1d, 0xb3, 0xed, 0xe7, 0xff, 0x32, 0x06, 0x9b, 0x0a, 0xd9, 0x01,
|
||||
0xf8, 0x12, 0x87, 0xd5, 0x66, 0x44, 0x10, 0xa1, 0x9a, 0x1e, 0xdf, 0x46, 0x93, 0xb2, 0x8c, 0x44,
|
||||
0x8e, 0xfd, 0x75, 0x0c, 0x32, 0x04, 0x62, 0x23, 0x15, 0x32, 0xcb, 0xa6, 0xf5, 0xea, 0x13, 0x69,
|
||||
0xa6, 0x33, 0xf0, 0xc7, 0xe8, 0xac, 0xed, 0xb8, 0x16, 0x6b, 0xb6, 0x36, 0x4d, 0xa7, 0xcd, 0x78,
|
||||
0x7c, 0x06, 0x63, 0x50, 0x8d, 0x90, 0xff, 0xa0, 0x5b, 0x04, 0x15, 0xc4, 0xe8, 0x9c, 0x9c, 0x9e,
|
||||
0x1a, 0x4a, 0x68, 0x9c, 0x85, 0x77, 0x91, 0x36, 0x56, 0x9a, 0x7d, 0xcf, 0xb4, 0x3b, 0xcc, 0x13,
|
||||
0xf1, 0xfa, 0xdb, 0x18, 0x04, 0xec, 0xc3, 0x41, 0x60, 0x5c, 0x88, 0x38, 0x6b, 0x82, 0x22, 0x83,
|
||||
0x75, 0xf9, 0xd0, 0xc8, 0xd2, 0xb4, 0x2a, 0x23, 0x8e, 0x37, 0xc6, 0xef, 0xf2, 0x53, 0x24, 0x3f,
|
||||
0xe9, 0x5a, 0xf2, 0x48, 0x7b, 0x45, 0x9c, 0x17, 0x01, 0x52, 0xad, 0x48, 0xca, 0x70, 0x60, 0x84,
|
||||
0x5f, 0x98, 0xa2, 0x31, 0xdb, 0xd9, 0x36, 0x3b, 0x76, 0x78, 0x64, 0x7d, 0xef, 0x59, 0x60, 0x20,
|
||||
0x6a, 0xee, 0x54, 0x05, 0x2a, 0x4e, 0x10, 0xf0, 0x53, 0x3b, 0x41, 0x80, 0xcc, 0x4f, 0x10, 0x1a,
|
||||
0x93, 0x86, 0x3c, 0xde, 0x56, 0x1c, 0x37, 0x76, 0x2b, 0xc8, 0x82, 0x6b, 0xd8, 0x56, 0xc7, 0x8d,
|
||||
0xdf, 0x08, 0xc4, 0xb6, 0xc6, 0x50, 0x42, 0xe3, 0xac, 0xf7, 0x53, 0x3f, 0xff, 0xca, 0x18, 0x21,
|
||||
0xdf, 0x26, 0xd0, 0xb8, 0x6a, 0x71, 0x7c, 0xba, 0x40, 0xfc, 0x93, 0x10, 0x7e, 0xa8, 0xe6, 0x4d,
|
||||
0x11, 0x77, 0x51, 0xcd, 0x9b, 0x10, 0x70, 0xc0, 0xf8, 0xf4, 0x74, 0x37, 0x36, 0x7c, 0xd6, 0x87,
|
||||
0xb9, 0x95, 0x14, 0xd3, 0x53, 0x20, 0x6a, 0x7a, 0x0a, 0x91, 0x50, 0x89, 0xe3, 0x37, 0xe5, 0xf4,
|
||||
0x1a, 0x85, 0xb0, 0x5d, 0x3d, 0x7e, 0x7a, 0x85, 0x41, 0x11, 0x43, 0xec, 0x26, 0x1a, 0xdf, 0x61,
|
||||
0xe6, 0x63, 0x91, 0x97, 0xa2, 0x65, 0x40, 0x5f, 0xe7, 0xa0, 0xcc, 0x49, 0x51, 0x1d, 0x21, 0x40,
|
||||
0xa8, 0xd2, 0xc9, 0x77, 0x7c, 0x84, 0x32, 0x62, 0x9c, 0xe0, 0x55, 0x94, 0x6d, 0xb9, 0x5b, 0x4e,
|
||||
0x3f, 0xba, 0x54, 0x4e, 0xeb, 0xa7, 0x61, 0xd0, 0x94, 0xfe, 0x2d, 0x2c, 0xc0, 0x90, 0xaa, 0x62,
|
||||
0x24, 0x01, 0x7e, 0x8c, 0x95, 0x2a, 0xf2, 0x59, 0x02, 0x8d, 0x49, 0x43, 0x7c, 0x5b, 0x5d, 0x0e,
|
||||
0x52, 0xa5, 0xf7, 0x0e, 0x4d, 0xc9, 0xef, 0xbe, 0x68, 0xea, 0x13, 0x52, 0xde, 0x39, 0xb7, 0xcd,
|
||||
0xce, 0x96, 0xd8, 0xa8, 0x94, 0xb8, 0x73, 0x02, 0xa0, 0x86, 0x0e, 0x48, 0x84, 0x0a, 0x94, 0x7c,
|
||||
0x96, 0x42, 0x93, 0x7a, 0x13, 0xe1, 0xed, 0x7a, 0xcb, 0xb1, 0x77, 0x61, 0x31, 0xb1, 0x53, 0xca,
|
||||
0x3d, 0xc7, 0xde, 0x85, 0x36, 0x53, 0x78, 0x12, 0x18, 0x09, 0x1e, 0x00, 0xce, 0x53, 0x01, 0xe0,
|
||||
0x02, 0xa1, 0x80, 0xe1, 0x8f, 0xd1, 0xd8, 0x8e, 0xed, 0x58, 0xee, 0x8e, 0x0f, 0xcb, 0x98, 0xd0,
|
||||
0x6f, 0x0e, 0x0f, 0x84, 0x02, 0x3c, 0x15, 0xa5, 0xa7, 0x90, 0xad, 0xb6, 0x4b, 0xca, 0x84, 0x86,
|
||||
0x1a, 0xbc, 0x8c, 0xd2, 0x1d, 0xdb, 0xd9, 0xda, 0x85, 0x04, 0x8b, 0x8d, 0xd9, 0x4f, 0xcc, 0x7e,
|
||||
0xdf, 0x03, 0x77, 0x57, 0xa4, 0x3b, 0xc1, 0x8c, 0x2e, 0xd9, 0x5c, 0xe2, 0x97, 0x6c, 0xfe, 0x2f,
|
||||
0xbe, 0x83, 0x32, 0x96, 0xe9, 0xed, 0xd8, 0xe2, 0x52, 0x73, 0x82, 0xa7, 0x19, 0xe9, 0x49, 0x52,
|
||||
0xa3, 0x0b, 0x1e, 0x88, 0x84, 0x4a, 0x1c, 0x33, 0x34, 0xb6, 0xe1, 0x31, 0xb6, 0xee, 0x5b, 0x70,
|
||||
0x48, 0x3a, 0xc1, 0xdb, 0xbb, 0xdc, 0x1b, 0xbf, 0x06, 0x2c, 0x79, 0x8c, 0x95, 0x1a, 0x70, 0x0d,
|
||||
0x90, 0x66, 0xea, 0x8d, 0xa5, 0x0c, 0xd7, 0x00, 0x49, 0xa3, 0x21, 0x09, 0x37, 0x51, 0xc6, 0x61,
|
||||
0x7d, 0xfe, 0x94, 0xcc, 0xc9, 0x4f, 0x99, 0x97, 0x4f, 0xc9, 0xd4, 0x58, 0x5f, 0x3c, 0x44, 0x1a,
|
||||
0xa9, 0xd5, 0x0b, 0x91, 0x3f, 0x42, 0x72, 0xa8, 0x64, 0x90, 0xcf, 0x47, 0x51, 0x36, 0x8c, 0x2f,
|
||||
0x3f, 0xfc, 0xb9, 0x3b, 0x0e, 0xf3, 0xf4, 0xaf, 0x5b, 0x30, 0xf1, 0x01, 0x95, 0xd7, 0x33, 0x31,
|
||||
0xc8, 0x14, 0x42, 0x68, 0xa4, 0xe5, 0x0e, 0xda, 0x9e, 0xbb, 0xd5, 0xd3, 0xbf, 0x6c, 0x81, 0x03,
|
||||
0x40, 0x63, 0x0e, 0x14, 0x42, 0x68, 0xa4, 0xc5, 0x37, 0x51, 0x72, 0xcb, 0xb6, 0x20, 0xd4, 0xe9,
|
||||
0xd2, 0xab, 0xcf, 0x02, 0x23, 0x79, 0x0f, 0x2a, 0x80, 0xa3, 0xc3, 0xc0, 0x18, 0x17, 0x09, 0x67,
|
||||
0x5b, 0xda, 0xf8, 0xe4, 0x0c, 0xca, 0xf5, 0xdc, 0xb8, 0x6d, 0x5b, 0x10, 0x5d, 0x69, 0xbc, 0x2c,
|
||||
0x8c, 0xdb, 0x9a, 0x71, 0x3b, 0x6e, 0xbc, 0xcc, 0x8d, 0x39, 0xf6, 0xcb, 0x04, 0x9a, 0xd0, 0x32,
|
||||
0xf4, 0x87, 0xef, 0xc5, 0x0a, 0x3a, 0x23, 0x1c, 0xd8, 0x7e, 0x13, 0x5e, 0x10, 0xf6, 0x43, 0x7e,
|
||||
0x36, 0x01, 0x4d, 0xd5, 0x5f, 0xe6, 0xb8, 0xfa, 0x6c, 0xa2, 0x83, 0x84, 0xc6, 0x38, 0xa4, 0x81,
|
||||
0xc6, 0x55, 0xc0, 0xf1, 0x12, 0xca, 0xec, 0x72, 0x21, 0x6c, 0x48, 0x67, 0x0f, 0x65, 0x45, 0x74,
|
||||
0xec, 0x14, 0x34, 0x55, 0x10, 0x20, 0x12, 0x2a, 0x61, 0xd2, 0x42, 0x69, 0xe0, 0xbf, 0xd0, 0x6d,
|
||||
0x22, 0xd6, 0x67, 0x26, 0xff, 0x79, 0x9f, 0xf9, 0x71, 0x0a, 0x8d, 0x51, 0x7e, 0x68, 0xf6, 0xfb,
|
||||
0xf8, 0x1d, 0xd5, 0xed, 0xd2, 0xa5, 0x57, 0x4e, 0x6a, 0x6f, 0x51, 0x74, 0xc2, 0xaf, 0x1f, 0xd1,
|
||||
0xa5, 0x6b, 0xf4, 0xd4, 0x97, 0xae, 0xf0, 0x95, 0x92, 0xa7, 0x78, 0xa5, 0x68, 0x2c, 0xa5, 0x5e,
|
||||
0x78, 0x2c, 0xa5, 0x4f, 0x3f, 0x96, 0xc2, 0x49, 0x99, 0x39, 0xc5, 0xa4, 0xac, 0xa3, 0x33, 0x1b,
|
||||
0x9e, 0xdb, 0x85, 0x6f, 0x64, 0xae, 0x67, 0x7a, 0x7b, 0xf2, 0x54, 0x00, 0xa3, 0x9b, 0x6b, 0xd6,
|
||||
0x42, 0x85, 0x1a, 0xdd, 0x31, 0x94, 0xd0, 0x38, 0x2b, 0x3e, 0x13, 0xb3, 0x2f, 0x36, 0x13, 0xf1,
|
||||
0x2d, 0x94, 0x15, 0x27, 0x5e, 0xc7, 0x85, 0x6b, 0x57, 0xba, 0xf4, 0x32, 0x6f, 0x65, 0x80, 0xd5,
|
||||
0x5c, 0xd5, 0xca, 0xa4, 0xac, 0x5e, 0x3b, 0x24, 0x90, 0xdf, 0x26, 0x50, 0x96, 0x32, 0xbf, 0xe7,
|
||||
0x3a, 0x3e, 0xfb, 0xbe, 0x49, 0x30, 0x87, 0x52, 0x96, 0xd9, 0x37, 0x65, 0xda, 0xc1, 0xee, 0x71,
|
||||
0x59, 0xed, 0x1e, 0x17, 0x08, 0x05, 0x0c, 0x7f, 0x88, 0x52, 0x2d, 0xd7, 0x12, 0xc1, 0x3f, 0xa3,
|
||||
0x37, 0xcd, 0x8a, 0xe7, 0xb9, 0xde, 0xa2, 0x6b, 0xc9, 0x6b, 0x07, 0x27, 0x29, 0x07, 0x5c, 0x20,
|
||||
0x14, 0x30, 0xf2, 0x9b, 0x04, 0xca, 0x95, 0xdd, 0x1d, 0xa7, 0xe3, 0x9a, 0xd6, 0xaa, 0xe7, 0xb6,
|
||||
0x3d, 0xe6, 0xfb, 0xdf, 0xeb, 0xee, 0xdf, 0x44, 0x63, 0x5b, 0xf0, 0xe5, 0x20, 0xbc, 0xfd, 0x5f,
|
||||
0x8b, 0x5f, 0x83, 0x0e, 0x3f, 0x44, 0x7c, 0x66, 0x88, 0x3e, 0x34, 0x4a, 0x63, 0xe5, 0x5f, 0xc8,
|
||||
0x84, 0x86, 0x0a, 0xf2, 0xeb, 0x24, 0x2a, 0x9c, 0xec, 0x08, 0x77, 0xd1, 0x84, 0x60, 0x36, 0xb5,
|
||||
0x4f, 0xfa, 0xb3, 0xa7, 0x59, 0x03, 0x5c, 0xce, 0xe0, 0x52, 0xb0, 0xa5, 0x64, 0x75, 0x29, 0x88,
|
||||
0x20, 0x42, 0x35, 0xfd, 0x0b, 0x7d, 0xa7, 0xd4, 0xae, 0xf2, 0xc9, 0x1f, 0x7e, 0x95, 0x6f, 0xa0,
|
||||
0x29, 0x91, 0xa2, 0xe1, 0x07, 0xe5, 0x54, 0x31, 0x39, 0x9b, 0x2e, 0x5d, 0xe7, 0xdd, 0x76, 0x5d,
|
||||
0x1c, 0x56, 0xc3, 0x4f, 0xc9, 0xd3, 0x51, 0xb2, 0x0a, 0x30, 0xcc, 0xb6, 0xdc, 0x08, 0x8d, 0x71,
|
||||
0xf1, 0x52, 0xec, 0xa6, 0x27, 0x4a, 0xfd, 0x3f, 0x4e, 0x79, 0xb3, 0xd3, 0x6e, 0x72, 0x24, 0x83,
|
||||
0x52, 0xab, 0xb6, 0xd3, 0x26, 0x37, 0x51, 0x7a, 0xb1, 0xe3, 0xfa, 0xd0, 0x71, 0x3c, 0x66, 0xfa,
|
||||
0xae, 0xa3, 0xa7, 0x92, 0x40, 0x54, 0xa8, 0x85, 0x48, 0xa8, 0xc4, 0xe7, 0xbe, 0x4e, 0xa2, 0x09,
|
||||
0xed, 0x2f, 0x30, 0xf8, 0x7f, 0xd0, 0xe5, 0xbb, 0x95, 0x46, 0x63, 0x61, 0xb9, 0xd2, 0x5c, 0x7b,
|
||||
0xb8, 0x5a, 0x69, 0x2e, 0xae, 0xdc, 0x6b, 0xac, 0x55, 0x68, 0x73, 0xb1, 0x5e, 0x5b, 0xaa, 0x2e,
|
||||
0xe7, 0x46, 0x0a, 0x57, 0xf6, 0x0f, 0x8a, 0x79, 0xcd, 0x22, 0xfe, 0xb7, 0x92, 0xff, 0x44, 0x38,
|
||||
0x66, 0x5e, 0xad, 0x95, 0x2b, 0x9f, 0xe4, 0x12, 0x85, 0xf3, 0xfb, 0x07, 0xc5, 0x9c, 0x66, 0x25,
|
||||
0x3e, 0xc1, 0xfd, 0x37, 0x7a, 0xe9, 0x28, 0xbb, 0x79, 0x6f, 0xb5, 0xbc, 0xb0, 0x56, 0xc9, 0x8d,
|
||||
0x16, 0x0a, 0xfb, 0x07, 0xc5, 0x8b, 0x87, 0x8d, 0x64, 0x0a, 0xbe, 0x81, 0xce, 0xc7, 0x4c, 0x69,
|
||||
0xe5, 0xe3, 0x7b, 0x95, 0xc6, 0x5a, 0x2e, 0x59, 0xb8, 0xb8, 0x7f, 0x50, 0xc4, 0x9a, 0x55, 0x38,
|
||||
0x26, 0xe6, 0xd1, 0x85, 0x43, 0x16, 0x8d, 0xd5, 0x7a, 0xad, 0x51, 0xc9, 0xa5, 0x0a, 0x97, 0xf6,
|
||||
0x0f, 0x8a, 0xe7, 0x62, 0x26, 0xb2, 0xab, 0x2c, 0xa2, 0x99, 0x98, 0x4d, 0xb9, 0xfe, 0xa0, 0xb6,
|
||||
0x52, 0x5f, 0x28, 0x37, 0x57, 0x69, 0x7d, 0x99, 0x56, 0x1a, 0x8d, 0x5c, 0xba, 0x60, 0xec, 0x1f,
|
||||
0x14, 0x2f, 0x6b, 0xc6, 0x47, 0x2a, 0x7c, 0x0e, 0x4d, 0xc7, 0x9c, 0xac, 0x56, 0x6b, 0xcb, 0xb9,
|
||||
0x4c, 0xe1, 0xdc, 0xfe, 0x41, 0xf1, 0xac, 0x66, 0xc7, 0x63, 0x79, 0x64, 0xff, 0x16, 0x57, 0xea,
|
||||
0x8d, 0x4a, 0x6e, 0xec, 0xc8, 0xfe, 0x41, 0xc0, 0xe7, 0x7e, 0x95, 0x40, 0xf8, 0xe8, 0x1f, 0xbd,
|
||||
0xf0, 0x7b, 0x28, 0x1f, 0x3a, 0x59, 0xac, 0xdf, 0x5d, 0xe5, 0xeb, 0xac, 0xd6, 0x6b, 0xcd, 0x5a,
|
||||
0xbd, 0x56, 0xc9, 0x8d, 0xc4, 0x76, 0x55, 0xb3, 0xaa, 0xb9, 0x0e, 0xc3, 0x75, 0x74, 0xe9, 0x38,
|
||||
0xcb, 0x95, 0x47, 0x6f, 0xe7, 0x12, 0x85, 0xf9, 0xfd, 0x83, 0xe2, 0x85, 0xa3, 0x86, 0x2b, 0x8f,
|
||||
0xde, 0xfe, 0xe6, 0xa7, 0xaf, 0x1c, 0xaf, 0x98, 0xe3, 0x07, 0x20, 0x7d, 0x69, 0x6f, 0xa2, 0xf3,
|
||||
0xba, 0xe3, 0xbb, 0x95, 0xb5, 0x85, 0xf2, 0xc2, 0xda, 0x42, 0x6e, 0x44, 0xc4, 0x40, 0xa3, 0xde,
|
||||
0x65, 0x7d, 0x13, 0xda, 0xee, 0x6b, 0x68, 0x3a, 0xf6, 0x16, 0x95, 0xfb, 0x15, 0x1a, 0x66, 0x94,
|
||||
0xbe, 0x7e, 0xb6, 0xcd, 0x3c, 0xfc, 0x3a, 0xc2, 0x3a, 0x79, 0x61, 0xe5, 0xc1, 0xc2, 0xc3, 0x46,
|
||||
0x6e, 0xb4, 0x70, 0x61, 0xff, 0xa0, 0x38, 0xad, 0xb1, 0x17, 0x3a, 0x3b, 0xe6, 0x9e, 0x3f, 0xf7,
|
||||
0xfb, 0x51, 0x34, 0xa9, 0x7f, 0x37, 0xc2, 0xaf, 0xa3, 0x73, 0x4b, 0xd5, 0x15, 0x9e, 0x89, 0x4b,
|
||||
0x75, 0x11, 0x01, 0x2e, 0xe6, 0x46, 0xc4, 0xe3, 0x74, 0x2a, 0xff, 0x8d, 0xff, 0x0b, 0xe5, 0x0f,
|
||||
0xd1, 0xcb, 0x55, 0x5a, 0x59, 0x5c, 0xab, 0xd3, 0x87, 0xb9, 0x44, 0xe1, 0x25, 0xbe, 0x61, 0xba,
|
||||
0x4d, 0xd9, 0xf6, 0xa0, 0x05, 0xed, 0xe1, 0x5b, 0xe8, 0xf2, 0x21, 0xc3, 0xc6, 0xc3, 0xbb, 0x2b,
|
||||
0xd5, 0xda, 0x1d, 0xf1, 0xbc, 0xd1, 0xc2, 0xd5, 0xfd, 0x83, 0xe2, 0x25, 0xdd, 0xb6, 0x21, 0x3e,
|
||||
0xc5, 0x71, 0x28, 0x9b, 0xc0, 0xb7, 0x51, 0xf1, 0x04, 0xfb, 0x68, 0x01, 0xc9, 0x02, 0xd9, 0x3f,
|
||||
0x28, 0x5e, 0x39, 0xc6, 0x89, 0x5a, 0x47, 0x36, 0x81, 0xdf, 0x42, 0x17, 0x8f, 0xf7, 0x14, 0xd6,
|
||||
0xc5, 0x31, 0xf6, 0x73, 0x7f, 0x4c, 0xa0, 0x71, 0x35, 0xf5, 0xf8, 0xa6, 0x55, 0x28, 0xad, 0xf3,
|
||||
0x26, 0x51, 0xae, 0x34, 0x6b, 0xf5, 0x26, 0x48, 0xe1, 0xa6, 0x29, 0x5e, 0xcd, 0x85, 0x9f, 0x3c,
|
||||
0xc7, 0x35, 0xfa, 0x72, 0xa5, 0x56, 0xa1, 0xd5, 0xc5, 0x30, 0xa2, 0x8a, 0xbd, 0xcc, 0x1c, 0xe6,
|
||||
0xd9, 0x2d, 0xfc, 0x36, 0xba, 0x14, 0x77, 0xde, 0xb8, 0xb7, 0x78, 0x3b, 0xdc, 0x25, 0x58, 0xa0,
|
||||
0xf6, 0x80, 0xc6, 0x56, 0x6b, 0x13, 0x02, 0xf3, 0x4e, 0xcc, 0xaa, 0x5a, 0xbb, 0xbf, 0xb0, 0x52,
|
||||
0x2d, 0x0b, 0xab, 0x64, 0x21, 0xbf, 0x7f, 0x50, 0x3c, 0xaf, 0xac, 0xe4, 0x07, 0x0e, 0x6e, 0x36,
|
||||
0xf7, 0x4d, 0x02, 0xcd, 0x7c, 0xf7, 0xf0, 0xc2, 0x0f, 0xd0, 0xab, 0xb0, 0x5f, 0x47, 0x5a, 0x81,
|
||||
0xec, 0x5b, 0x62, 0x0f, 0x17, 0x56, 0x57, 0x2b, 0xb5, 0x72, 0x6e, 0xa4, 0x30, 0xbb, 0x7f, 0x50,
|
||||
0xbc, 0xf6, 0xdd, 0x2e, 0x17, 0x7a, 0x3d, 0xe6, 0x58, 0xa7, 0x74, 0xbc, 0x54, 0xa7, 0xcb, 0x95,
|
||||
0xb5, 0x5c, 0xe2, 0x34, 0x8e, 0x97, 0x5c, 0xaf, 0xcd, 0xfa, 0xa5, 0xbb, 0x4f, 0xbe, 0x9d, 0x19,
|
||||
0x79, 0xfa, 0xed, 0xcc, 0xc8, 0x93, 0x67, 0x33, 0x89, 0xa7, 0xcf, 0x66, 0x12, 0x3f, 0x7b, 0x3e,
|
||||
0x33, 0xf2, 0xd5, 0xf3, 0x99, 0xc4, 0xd3, 0xe7, 0x33, 0x23, 0x7f, 0x7a, 0x3e, 0x33, 0xf2, 0xe8,
|
||||
0xb5, 0xb6, 0xdd, 0xdf, 0xdc, 0x5a, 0xbf, 0xde, 0x72, 0xbb, 0x37, 0xfc, 0x3d, 0xa7, 0xd5, 0xdf,
|
||||
0xb4, 0x9d, 0xb6, 0xf6, 0x4b, 0xff, 0xcf, 0x0f, 0xeb, 0x19, 0xf8, 0xf5, 0xd6, 0x3f, 0x02, 0x00,
|
||||
0x00, 0xff, 0xff, 0x68, 0x4a, 0x6e, 0xeb, 0x13, 0x21, 0x00, 0x00,
|
||||
// 3251 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x5a, 0x4b, 0x6c, 0x23, 0x47,
|
||||
0x7a, 0x16, 0x9f, 0xa2, 0x4a, 0x8f, 0xa1, 0x6a, 0x5e, 0x34, 0x67, 0xac, 0x66, 0x6a, 0x67, 0x13,
|
||||
0x59, 0x9b, 0x1d, 0xaf, 0xb5, 0xde, 0x8d, 0x63, 0x3b, 0x36, 0xc4, 0x87, 0x34, 0x5c, 0x6b, 0x48,
|
||||
0xb9, 0xa8, 0x19, 0xaf, 0x07, 0x08, 0x88, 0x16, 0xbb, 0x44, 0x35, 0x86, 0xec, 0x66, 0xba, 0x9b,
|
||||
0x7a, 0x2c, 0x72, 0x49, 0x16, 0x58, 0x2c, 0x74, 0x08, 0x82, 0x3d, 0x05, 0xc1, 0x0a, 0x59, 0xe4,
|
||||
0x92, 0x5b, 0x80, 0x1c, 0x72, 0xd9, 0x53, 0x8e, 0x73, 0x1c, 0x2c, 0x10, 0x20, 0xc8, 0xa1, 0x01,
|
||||
0xcf, 0x5c, 0x12, 0xe6, 0xc6, 0x63, 0x0e, 0x41, 0x50, 0x7f, 0x55, 0x57, 0x57, 0xeb, 0xe1, 0x68,
|
||||
0xec, 0x43, 0x4e, 0xc3, 0xfa, 0xfe, 0xef, 0xff, 0xab, 0xba, 0xea, 0x7f, 0x55, 0x69, 0xd0, 0x9d,
|
||||
0x81, 0xbd, 0xf7, 0xee, 0xc8, 0x73, 0x03, 0xb7, 0xe7, 0x0e, 0xde, 0xdd, 0x63, 0xa3, 0x87, 0x30,
|
||||
0xc0, 0x85, 0x08, 0x2b, 0xcf, 0xb1, 0xe3, 0x40, 0x80, 0xe5, 0xef, 0x78, 0x6c, 0xe4, 0xfa, 0x82,
|
||||
0xbe, 0x37, 0xde, 0x7f, 0xb7, 0xef, 0xf6, 0x5d, 0x18, 0xc0, 0x2f, 0x41, 0x22, 0xff, 0x93, 0x46,
|
||||
0xb9, 0x47, 0x6c, 0x30, 0x70, 0x71, 0x0d, 0xcd, 0x5b, 0xec, 0xd0, 0xee, 0xb1, 0xae, 0x63, 0x0e,
|
||||
0x59, 0x29, 0x55, 0x49, 0xad, 0xce, 0x55, 0xc9, 0x24, 0x34, 0x90, 0x80, 0x5b, 0xe6, 0x90, 0x4d,
|
||||
0x43, 0xa3, 0x78, 0x3c, 0x1c, 0x7c, 0x48, 0x62, 0x88, 0x50, 0x4d, 0xce, 0x8d, 0xf4, 0x06, 0x36,
|
||||
0x73, 0x02, 0x61, 0x24, 0x1d, 0x1b, 0x11, 0x70, 0xc2, 0x48, 0x0c, 0x11, 0xaa, 0xc9, 0x71, 0x1b,
|
||||
0x2d, 0x49, 0x23, 0x87, 0xcc, 0xf3, 0x6d, 0xd7, 0x29, 0x65, 0xc0, 0xce, 0xea, 0x24, 0x34, 0x16,
|
||||
0x85, 0xe4, 0xa9, 0x10, 0x4c, 0x43, 0xe3, 0xa6, 0x66, 0x4a, 0xa2, 0x84, 0x26, 0x59, 0xf8, 0x19,
|
||||
0xba, 0xe1, 0x8c, 0x87, 0xdd, 0x9e, 0xeb, 0x38, 0xac, 0x17, 0xd8, 0xae, 0xe3, 0x97, 0xb2, 0x95,
|
||||
0xd4, 0x6a, 0xae, 0xfa, 0xde, 0x24, 0x34, 0x96, 0x9c, 0xf1, 0xb0, 0x16, 0x4b, 0xa6, 0xa1, 0x71,
|
||||
0x0b, 0x4c, 0x26, 0x61, 0xf2, 0xdf, 0xa1, 0x91, 0xb1, 0x9d, 0x80, 0x9e, 0xa3, 0xe3, 0x4f, 0xd0,
|
||||
0x5c, 0x60, 0x0f, 0x99, 0x1f, 0x98, 0xc3, 0x51, 0x29, 0x57, 0x49, 0xad, 0x66, 0xaa, 0x95, 0x49,
|
||||
0x68, 0xc4, 0xe0, 0x34, 0x34, 0x6e, 0x80, 0x41, 0x85, 0x10, 0x1a, 0x4b, 0xc9, 0x3f, 0xa5, 0x50,
|
||||
0xfe, 0x11, 0x33, 0x2d, 0xe6, 0xe1, 0x0d, 0x94, 0x0d, 0x4e, 0x46, 0x62, 0xeb, 0x97, 0xd6, 0x6f,
|
||||
0x3f, 0x8c, 0x0e, 0xf5, 0xe1, 0x63, 0xe6, 0xfb, 0x66, 0x9f, 0xed, 0x9e, 0x8c, 0x58, 0xf5, 0xce,
|
||||
0x24, 0x34, 0x80, 0x36, 0x0d, 0x0d, 0x24, 0xec, 0x9e, 0x8c, 0x18, 0xa1, 0x80, 0x61, 0x0b, 0xcd,
|
||||
0xf7, 0xdc, 0xe1, 0xc8, 0x63, 0x3e, 0xec, 0x5b, 0x1a, 0x2c, 0xdd, 0xbf, 0x60, 0xa9, 0x16, 0x73,
|
||||
0xaa, 0x0f, 0x26, 0xa1, 0xa1, 0x2b, 0x4d, 0x43, 0x63, 0x59, 0xec, 0x69, 0x8c, 0x11, 0xaa, 0x33,
|
||||
0xc8, 0xaf, 0x53, 0x68, 0xb1, 0x36, 0x18, 0xfb, 0x01, 0xf3, 0x6a, 0xae, 0xb3, 0x6f, 0xf7, 0xf1,
|
||||
0x67, 0x68, 0x76, 0xdf, 0x1d, 0x58, 0xcc, 0xf3, 0x4b, 0xa9, 0x4a, 0x66, 0x75, 0x7e, 0xbd, 0x18,
|
||||
0xcf, 0xb9, 0x09, 0x82, 0xaa, 0xf1, 0x22, 0x34, 0x66, 0x26, 0xa1, 0x11, 0x11, 0xa7, 0xa1, 0xb1,
|
||||
0x00, 0xf3, 0x88, 0x31, 0xa1, 0x91, 0x80, 0x6f, 0xa9, 0xcf, 0x7a, 0xae, 0x63, 0x99, 0xde, 0x09,
|
||||
0x7c, 0x42, 0x41, 0x6c, 0xa9, 0x02, 0xd5, 0x96, 0x2a, 0x84, 0xd0, 0x58, 0x4a, 0x7e, 0x9b, 0x45,
|
||||
0x79, 0x31, 0x29, 0x7e, 0x88, 0xd2, 0xb6, 0x25, 0x7d, 0x79, 0xe5, 0x55, 0x68, 0xa4, 0x9b, 0xf5,
|
||||
0x49, 0x68, 0xa4, 0x6d, 0x6b, 0x1a, 0x1a, 0x05, 0x30, 0x61, 0x5b, 0xe4, 0x57, 0x2f, 0x1f, 0xa4,
|
||||
0x9b, 0x75, 0x9a, 0xb6, 0x2d, 0xfc, 0x10, 0xe5, 0x06, 0xe6, 0x1e, 0x1b, 0x48, 0xcf, 0x2d, 0x4d,
|
||||
0x42, 0x43, 0x00, 0xd3, 0xd0, 0x98, 0x07, 0x3e, 0x8c, 0x08, 0x15, 0x28, 0xfe, 0x08, 0xcd, 0x79,
|
||||
0xcc, 0xb4, 0xba, 0xae, 0x33, 0x38, 0x01, 0x2f, 0x2d, 0x54, 0x57, 0x26, 0xa1, 0x51, 0xe0, 0x60,
|
||||
0xdb, 0x19, 0xf0, 0x95, 0x2e, 0x81, 0x5a, 0x04, 0x10, 0xaa, 0x64, 0xb8, 0x8b, 0xb0, 0xdd, 0x77,
|
||||
0x5c, 0x8f, 0x75, 0x47, 0xcc, 0x1b, 0xda, 0xb0, 0xb7, 0xc2, 0x33, 0x0b, 0xd5, 0x1f, 0x4c, 0x42,
|
||||
0x63, 0x59, 0x48, 0x77, 0x62, 0xe1, 0x34, 0x34, 0xee, 0x8a, 0x55, 0x9f, 0x97, 0x10, 0x7a, 0x91,
|
||||
0x8d, 0x3f, 0x43, 0x8b, 0x72, 0x02, 0x8b, 0x0d, 0x58, 0xc0, 0xc0, 0x3f, 0x0b, 0xd5, 0xdf, 0x9f,
|
||||
0x84, 0xc6, 0x82, 0x10, 0xd4, 0x01, 0x9f, 0x86, 0x06, 0xd6, 0xcc, 0x0a, 0x90, 0xd0, 0x04, 0x07,
|
||||
0x5b, 0xe8, 0x96, 0x65, 0xfb, 0xe6, 0xde, 0x80, 0x75, 0x03, 0x36, 0x1c, 0x75, 0x6d, 0xc7, 0x62,
|
||||
0xc7, 0xcc, 0x2f, 0xe5, 0xc1, 0xe6, 0xfa, 0x24, 0x34, 0xb0, 0x94, 0xef, 0xb2, 0xe1, 0xa8, 0x29,
|
||||
0xa4, 0xd3, 0xd0, 0x28, 0x89, 0x84, 0x71, 0x41, 0x44, 0xe8, 0x25, 0x7c, 0xbc, 0x8e, 0xf2, 0x23,
|
||||
0x73, 0xec, 0x33, 0xab, 0x34, 0x0b, 0x76, 0xcb, 0x93, 0xd0, 0x90, 0x88, 0x72, 0x18, 0x31, 0x24,
|
||||
0x54, 0xe2, 0xdc, 0xf9, 0x44, 0x0a, 0xf2, 0x4b, 0xc5, 0xf3, 0xce, 0x57, 0x07, 0x41, 0xec, 0x7c,
|
||||
0x92, 0xa8, 0x6c, 0x89, 0x31, 0xa1, 0x91, 0x80, 0xfc, 0x4b, 0x1e, 0xe5, 0x85, 0x12, 0xae, 0x2a,
|
||||
0xe7, 0x59, 0xa8, 0xae, 0x73, 0x03, 0xff, 0x1e, 0x1a, 0x05, 0x21, 0x6b, 0xd6, 0xaf, 0x72, 0xa6,
|
||||
0x5f, 0xbe, 0x7c, 0x90, 0xd2, 0x1c, 0x6a, 0x0d, 0x65, 0xb5, 0x4c, 0x08, 0xc1, 0xeb, 0x88, 0x1c,
|
||||
0x28, 0x82, 0xd7, 0x81, 0xec, 0x07, 0x18, 0xfe, 0x18, 0xcd, 0x99, 0x96, 0xc5, 0x83, 0x8c, 0xf9,
|
||||
0xa5, 0x4c, 0x25, 0xc3, 0x7d, 0x96, 0xfb, 0xbd, 0x02, 0xa7, 0xa1, 0xb1, 0x08, 0x5a, 0x12, 0x21,
|
||||
0x34, 0x96, 0xe1, 0x3f, 0x4d, 0x86, 0x7e, 0xf6, 0x7c, 0x12, 0xf9, 0x76, 0x31, 0xcf, 0x3d, 0xbd,
|
||||
0xc7, 0x3c, 0x99, 0xd7, 0x73, 0x22, 0xa0, 0xb8, 0xa7, 0x73, 0x50, 0x66, 0x75, 0xe1, 0xe9, 0x11,
|
||||
0x40, 0xa8, 0x92, 0xe1, 0x2d, 0xb4, 0x30, 0x34, 0x8f, 0xbb, 0x3e, 0xfb, 0xb3, 0x31, 0x73, 0x7a,
|
||||
0x0c, 0x7c, 0x26, 0x23, 0x56, 0x31, 0x34, 0x8f, 0x3b, 0x12, 0x56, 0xab, 0xd0, 0x30, 0x42, 0x75,
|
||||
0x06, 0xae, 0x22, 0x64, 0x3b, 0x81, 0xe7, 0x5a, 0xe3, 0x1e, 0xf3, 0xa4, 0x8b, 0x40, 0x79, 0x89,
|
||||
0x51, 0x55, 0x5e, 0x62, 0x88, 0x50, 0x4d, 0x8e, 0xfb, 0xa8, 0x00, 0xbe, 0xdb, 0xb5, 0xad, 0x52,
|
||||
0xa1, 0x92, 0x5a, 0xcd, 0x56, 0xb7, 0xe5, 0xe1, 0xce, 0x82, 0x17, 0xc2, 0xd9, 0x46, 0x3f, 0xb9,
|
||||
0xcf, 0x00, 0xbb, 0x69, 0xa9, 0xdd, 0x97, 0x63, 0x9e, 0x37, 0x22, 0xda, 0xdf, 0xc6, 0x3f, 0x69,
|
||||
0xc4, 0xc7, 0x7f, 0x8e, 0xca, 0xfe, 0x73, 0x9b, 0x47, 0x8a, 0x98, 0x9b, 0x17, 0x8c, 0xae, 0xc7,
|
||||
0x86, 0xee, 0xa1, 0x39, 0xf0, 0x4b, 0x73, 0xb0, 0xf8, 0x4f, 0x26, 0xa1, 0x51, 0xe2, 0xac, 0xa6,
|
||||
0x46, 0xa2, 0x92, 0x33, 0x0d, 0x8d, 0x15, 0x91, 0xe7, 0xae, 0x20, 0x10, 0x7a, 0xa5, 0x2e, 0x3e,
|
||||
0x46, 0x6f, 0x31, 0xa7, 0xe7, 0x9d, 0x8c, 0x60, 0xda, 0x91, 0xe9, 0xfb, 0x47, 0xae, 0x67, 0x75,
|
||||
0x03, 0xf7, 0x39, 0x73, 0x4a, 0x08, 0x9c, 0xfa, 0xe3, 0x49, 0x68, 0xdc, 0x8d, 0x49, 0x3b, 0x92,
|
||||
0xb3, 0xcb, 0x29, 0xd3, 0xd0, 0x78, 0x1b, 0xe6, 0xbe, 0x42, 0x4e, 0xe8, 0x55, 0x9a, 0xe4, 0x2f,
|
||||
0x53, 0x28, 0x07, 0x9b, 0xc1, 0xa3, 0x59, 0x24, 0x75, 0x99, 0x82, 0x21, 0x9a, 0x05, 0x72, 0x21,
|
||||
0xfd, 0x4b, 0x1c, 0x37, 0x50, 0x6e, 0xdf, 0x1e, 0x30, 0xbf, 0x94, 0x86, 0x58, 0xc6, 0x5a, 0x21,
|
||||
0xb1, 0x07, 0xac, 0xe9, 0xec, 0xbb, 0xd5, 0x7b, 0x32, 0x9a, 0x05, 0x51, 0xc5, 0x12, 0x1f, 0x11,
|
||||
0x2a, 0x40, 0xf2, 0xcb, 0x14, 0x9a, 0x87, 0x45, 0x3c, 0x19, 0x59, 0x66, 0xc0, 0xfe, 0x3f, 0x97,
|
||||
0xf2, 0x8b, 0x45, 0x54, 0x88, 0x14, 0x54, 0x42, 0x48, 0x5d, 0x23, 0x21, 0xac, 0xa1, 0xac, 0x6f,
|
||||
0xff, 0x8c, 0x41, 0x61, 0xc9, 0x08, 0x2e, 0x1f, 0x2b, 0x2e, 0x1f, 0x10, 0x0a, 0x18, 0xfe, 0x14,
|
||||
0xa1, 0xa1, 0x6b, 0xd9, 0xfb, 0x36, 0xb3, 0xba, 0xbe, 0xde, 0x88, 0x44, 0x68, 0x47, 0x55, 0x4d,
|
||||
0x85, 0x10, 0x1a, 0x4b, 0x79, 0xfe, 0x50, 0x06, 0xf6, 0x4e, 0x4a, 0x0b, 0x10, 0x19, 0x1f, 0x47,
|
||||
0x91, 0xd1, 0x39, 0x70, 0xbd, 0x00, 0xc2, 0x41, 0x4d, 0x53, 0x3d, 0x51, 0xa1, 0x16, 0x43, 0x84,
|
||||
0x47, 0x82, 0x24, 0x53, 0x8d, 0x8a, 0xb7, 0xd1, 0x6c, 0xd4, 0xcd, 0x71, 0xcf, 0x4f, 0x24, 0xe9,
|
||||
0xa7, 0xac, 0x17, 0xb8, 0x5e, 0xb5, 0x12, 0x25, 0xe9, 0x43, 0xd5, 0xdd, 0x89, 0x80, 0x3b, 0x8c,
|
||||
0xfa, 0xba, 0x48, 0x82, 0x3f, 0x44, 0x05, 0x95, 0x4c, 0x10, 0x7c, 0x2b, 0x24, 0x23, 0x3f, 0xce,
|
||||
0x24, 0x4b, 0xb2, 0x41, 0x88, 0xd2, 0x88, 0x92, 0xe1, 0x9f, 0xa0, 0xfc, 0xde, 0xc0, 0xed, 0x3d,
|
||||
0x8f, 0xaa, 0xc5, 0xcd, 0x78, 0x21, 0x55, 0x8e, 0xc3, 0xb9, 0xbe, 0x2d, 0xd7, 0x22, 0xa9, 0xaa,
|
||||
0xfc, 0xc3, 0x90, 0x50, 0x09, 0xf3, 0x56, 0xd5, 0x3f, 0x19, 0x0e, 0x6c, 0xe7, 0x79, 0x37, 0x30,
|
||||
0xbd, 0x3e, 0x0b, 0x4a, 0xcb, 0x71, 0xab, 0x2a, 0x25, 0xbb, 0x20, 0x50, 0xad, 0x6a, 0x02, 0x25,
|
||||
0x34, 0xc9, 0xe2, 0x0d, 0xb4, 0x30, 0xdd, 0x3d, 0x30, 0xfd, 0x83, 0x12, 0x86, 0x38, 0x85, 0x0c,
|
||||
0x27, 0xe0, 0x47, 0xa6, 0x7f, 0xa0, 0xb6, 0x3d, 0x86, 0x08, 0xd5, 0xe4, 0xbc, 0x81, 0x92, 0xb1,
|
||||
0xc9, 0xac, 0xd2, 0x4d, 0x30, 0x01, 0xae, 0xa0, 0x40, 0xe5, 0x0a, 0x0a, 0x21, 0x34, 0x96, 0xe2,
|
||||
0xaa, 0x6c, 0x44, 0x45, 0xfb, 0x78, 0xe7, 0xa2, 0xdb, 0x5f, 0xa3, 0x13, 0xdd, 0x44, 0xf3, 0xe7,
|
||||
0xbb, 0x9a, 0x45, 0x91, 0xf1, 0x47, 0x89, 0x7e, 0x46, 0x64, 0xfc, 0x91, 0xde, 0xc9, 0xe8, 0x0c,
|
||||
0xfc, 0x13, 0xcd, 0x2d, 0x1d, 0xbf, 0x34, 0x0f, 0x7d, 0xfb, 0x3b, 0xba, 0x1f, 0xb6, 0xfc, 0x0b,
|
||||
0x7e, 0xd8, 0x8a, 0xfb, 0x75, 0x8d, 0x86, 0xf7, 0x91, 0xd8, 0xa5, 0x2e, 0x44, 0xd5, 0x22, 0x98,
|
||||
0xda, 0x7a, 0x15, 0x1a, 0x0b, 0xd4, 0x3c, 0x82, 0xa3, 0xef, 0xd8, 0x3f, 0x63, 0x7c, 0xa3, 0xf6,
|
||||
0xa2, 0x81, 0xda, 0x28, 0x85, 0x44, 0x86, 0x7f, 0xf5, 0xf2, 0x41, 0x42, 0x8d, 0xc6, 0x4a, 0xf8,
|
||||
0x29, 0x2a, 0x8c, 0x06, 0x66, 0xb0, 0xef, 0x7a, 0xc3, 0xd2, 0x12, 0x38, 0xbb, 0xb6, 0x87, 0x3b,
|
||||
0x52, 0x52, 0x37, 0x03, 0xb3, 0x4a, 0xa4, 0x9b, 0x29, 0xbe, 0xf2, 0xdc, 0x08, 0x20, 0x54, 0xc9,
|
||||
0x70, 0x1d, 0xcd, 0x0f, 0xdc, 0x9e, 0x39, 0xe8, 0xee, 0x0f, 0xcc, 0xbe, 0x5f, 0xfa, 0x8f, 0x59,
|
||||
0xd8, 0x54, 0xf0, 0x0e, 0xc0, 0x37, 0x39, 0xac, 0x36, 0x23, 0x86, 0x08, 0xd5, 0xe4, 0xf8, 0x11,
|
||||
0x5a, 0x90, 0x61, 0x24, 0x7c, 0xec, 0x3f, 0x67, 0xc1, 0x43, 0xe0, 0x6c, 0xa4, 0x40, 0x7a, 0xd9,
|
||||
0xb2, 0x1e, 0x7d, 0xc2, 0xcd, 0x74, 0x06, 0xfe, 0x1c, 0xdd, 0xb0, 0x1d, 0xd7, 0x62, 0xdd, 0xde,
|
||||
0x81, 0xe9, 0xf4, 0x19, 0x3f, 0x9f, 0xc9, 0x2c, 0x44, 0x23, 0xf8, 0x3f, 0xc8, 0x6a, 0x20, 0x82,
|
||||
0x33, 0xba, 0x29, 0xab, 0xa7, 0x86, 0x12, 0x9a, 0x64, 0xe1, 0x63, 0xa4, 0x95, 0x95, 0x6e, 0xe0,
|
||||
0x99, 0xf6, 0x80, 0x79, 0xe2, 0xbc, 0xfe, 0x6b, 0x16, 0x0e, 0xec, 0xd3, 0x49, 0x68, 0xdc, 0x8e,
|
||||
0x39, 0xbb, 0x82, 0x22, 0x0f, 0xeb, 0xde, 0xb9, 0x92, 0xa5, 0x49, 0x95, 0x47, 0x5c, 0xae, 0x8c,
|
||||
0x7f, 0xcc, 0xbb, 0x48, 0xde, 0xe9, 0x5a, 0xb2, 0xa5, 0xbd, 0x2f, 0xfa, 0x45, 0x80, 0x54, 0x2a,
|
||||
0x92, 0x63, 0x68, 0x18, 0xe1, 0x17, 0xa6, 0x68, 0xd6, 0x76, 0x0e, 0xcd, 0x81, 0x1d, 0xb5, 0xac,
|
||||
0x1f, 0xbc, 0x0a, 0x0d, 0x44, 0xcd, 0xa3, 0xa6, 0x40, 0x45, 0x07, 0x01, 0x3f, 0xb5, 0x0e, 0x02,
|
||||
0xc6, 0xbc, 0x83, 0xd0, 0x98, 0x34, 0xe2, 0xf1, 0xb4, 0xe2, 0xb8, 0x89, 0x5b, 0x41, 0x01, 0x4c,
|
||||
0xc3, 0xb6, 0x3a, 0x6e, 0xf2, 0x46, 0x20, 0xb6, 0x35, 0x81, 0x12, 0x9a, 0x64, 0x7d, 0x98, 0xfd,
|
||||
0x9b, 0xdf, 0x18, 0x33, 0xe4, 0xab, 0x14, 0x9a, 0x53, 0x29, 0x8e, 0x57, 0x17, 0x38, 0xff, 0x0c,
|
||||
0x1c, 0x3f, 0x44, 0xf3, 0x81, 0x38, 0x77, 0x11, 0xcd, 0x07, 0x70, 0xe0, 0x80, 0xf1, 0xea, 0xe9,
|
||||
0xee, 0xef, 0xfb, 0x2c, 0x80, 0xba, 0x95, 0x11, 0xd5, 0x53, 0x20, 0xaa, 0x7a, 0x8a, 0x21, 0xa1,
|
||||
0x12, 0xc7, 0xef, 0xc9, 0xea, 0x95, 0x86, 0x63, 0x7b, 0xfb, 0xf2, 0xea, 0x15, 0x1d, 0x8a, 0x28,
|
||||
0x62, 0x1f, 0xa1, 0xb9, 0x23, 0x66, 0x3e, 0x17, 0x7e, 0x29, 0x52, 0x06, 0xe4, 0x75, 0x0e, 0x4a,
|
||||
0x9f, 0x14, 0xd1, 0x11, 0x01, 0x84, 0x2a, 0x99, 0xfc, 0xc6, 0x67, 0x28, 0x2f, 0xca, 0x09, 0xde,
|
||||
0x41, 0x85, 0x9e, 0x3b, 0x76, 0x82, 0xf8, 0x52, 0xba, 0xac, 0x77, 0xc3, 0x20, 0xa9, 0xfe, 0x5e,
|
||||
0x14, 0x80, 0x11, 0x55, 0x9d, 0x91, 0x04, 0x78, 0x1b, 0x2b, 0x45, 0xe4, 0xe7, 0x29, 0x34, 0x2b,
|
||||
0x15, 0xf1, 0x23, 0x75, 0x39, 0xc8, 0x56, 0x3f, 0x38, 0x57, 0x25, 0xbf, 0xfe, 0xa2, 0xa9, 0x57,
|
||||
0x48, 0x79, 0xe7, 0x3c, 0x34, 0x07, 0x63, 0xb1, 0x51, 0x59, 0x71, 0xe7, 0x04, 0x40, 0x15, 0x1d,
|
||||
0x18, 0x11, 0x2a, 0x50, 0xf2, 0xf3, 0x2c, 0x5a, 0xd0, 0x93, 0x08, 0x4f, 0xd7, 0x63, 0xc7, 0x3e,
|
||||
0x86, 0xc5, 0x24, 0xba, 0x94, 0x27, 0x8e, 0x7d, 0x0c, 0x69, 0xa6, 0xfc, 0x22, 0x34, 0x52, 0xfc,
|
||||
0x00, 0x38, 0x4f, 0x1d, 0x00, 0x1f, 0x10, 0x0a, 0x18, 0xfe, 0x1c, 0xcd, 0x1e, 0xd9, 0x8e, 0xe5,
|
||||
0x1e, 0xf9, 0xb0, 0x8c, 0x79, 0xfd, 0xe6, 0xf0, 0x85, 0x10, 0x80, 0xa5, 0x8a, 0xb4, 0x14, 0xb1,
|
||||
0xd5, 0x76, 0xc9, 0x31, 0xa1, 0x91, 0x04, 0x6f, 0xa1, 0xdc, 0xc0, 0x76, 0xc6, 0xc7, 0xe0, 0x60,
|
||||
0x89, 0x32, 0xfb, 0x53, 0x33, 0x08, 0x3c, 0x30, 0x77, 0x5f, 0x9a, 0x13, 0xcc, 0xf8, 0x92, 0xcd,
|
||||
0x47, 0xfc, 0x92, 0xcd, 0xff, 0xc5, 0x9f, 0xa1, 0xbc, 0x65, 0x7a, 0x47, 0xb6, 0xb8, 0xd4, 0x5c,
|
||||
0x61, 0x69, 0x45, 0x5a, 0x92, 0xd4, 0xf8, 0x82, 0x07, 0x43, 0x42, 0x25, 0x8e, 0x19, 0x9a, 0xdd,
|
||||
0xf7, 0x18, 0xdb, 0xf3, 0x2d, 0x68, 0x92, 0xae, 0xb0, 0xf6, 0x63, 0x6e, 0x8d, 0x5f, 0x03, 0x36,
|
||||
0x3d, 0xc6, 0xaa, 0x1d, 0xb8, 0x06, 0x48, 0x35, 0xf5, 0xc5, 0x72, 0x0c, 0xd7, 0x00, 0x49, 0xa3,
|
||||
0x11, 0x09, 0x77, 0x51, 0xde, 0x61, 0x01, 0x9f, 0x25, 0x7f, 0xf5, 0x2c, 0xeb, 0x72, 0x96, 0x7c,
|
||||
0x8b, 0x05, 0x62, 0x12, 0xa9, 0xa4, 0x56, 0x2f, 0x86, 0x7c, 0x0a, 0xc9, 0xa1, 0x92, 0x41, 0x7e,
|
||||
0x91, 0x46, 0x85, 0xe8, 0x7c, 0x79, 0xf3, 0xe7, 0x1e, 0x39, 0xcc, 0xd3, 0x9f, 0xee, 0xa0, 0xe2,
|
||||
0x03, 0x2a, 0xaf, 0x67, 0xa2, 0x90, 0x29, 0x84, 0xd0, 0x58, 0xca, 0x0d, 0xf4, 0x3d, 0x77, 0x3c,
|
||||
0xd2, 0x9f, 0xed, 0xc0, 0x00, 0xa0, 0x09, 0x03, 0x0a, 0x21, 0x34, 0x96, 0xe2, 0x8f, 0x50, 0x66,
|
||||
0x6c, 0x5b, 0x70, 0xd4, 0xb9, 0xea, 0x3b, 0xaf, 0x42, 0x23, 0xf3, 0x04, 0x22, 0x80, 0xa3, 0xd3,
|
||||
0xd0, 0x98, 0x13, 0x0e, 0x67, 0x5b, 0x5a, 0xf9, 0xe4, 0x0c, 0xca, 0xe5, 0x5c, 0xb9, 0x6f, 0x5b,
|
||||
0xf2, 0x4d, 0x0e, 0x94, 0xb7, 0x84, 0x72, 0x5f, 0x53, 0xee, 0x27, 0x95, 0xb7, 0xb8, 0x32, 0xc7,
|
||||
0x7e, 0x9d, 0x42, 0xf3, 0x9a, 0x87, 0x7e, 0xfb, 0xbd, 0xd8, 0x46, 0x4b, 0xc2, 0x80, 0xed, 0x77,
|
||||
0xe1, 0x03, 0xe5, 0x1b, 0x14, 0x3c, 0x9b, 0x80, 0xa4, 0xe9, 0x6f, 0x71, 0x5c, 0x3d, 0x9b, 0xe8,
|
||||
0x20, 0xa1, 0x09, 0x0e, 0xe9, 0xa0, 0x39, 0x75, 0xe0, 0x78, 0x13, 0xe5, 0x8f, 0xf9, 0x20, 0x4a,
|
||||
0x48, 0x37, 0xce, 0x79, 0x45, 0xdc, 0x76, 0x0a, 0x9a, 0x0a, 0x08, 0x18, 0x12, 0x2a, 0x61, 0xd2,
|
||||
0x43, 0x39, 0xe0, 0xbf, 0xd1, 0x6d, 0x22, 0x91, 0x67, 0x16, 0xfe, 0xef, 0x3c, 0xf3, 0x17, 0x59,
|
||||
0x34, 0x4b, 0x79, 0xd3, 0xec, 0x07, 0xf8, 0x47, 0x2a, 0xdb, 0xe5, 0xaa, 0xdf, 0xbd, 0x2a, 0xbd,
|
||||
0xc5, 0xa7, 0x13, 0xbd, 0x7e, 0xc4, 0x97, 0xae, 0xf4, 0xb5, 0x2f, 0x5d, 0xd1, 0x27, 0x65, 0xae,
|
||||
0xf1, 0x49, 0x71, 0x59, 0xca, 0xbe, 0x71, 0x59, 0xca, 0x5d, 0xbf, 0x2c, 0x45, 0x95, 0x32, 0x7f,
|
||||
0x8d, 0x4a, 0xd9, 0x46, 0x4b, 0xfb, 0x9e, 0x3b, 0x84, 0x37, 0x32, 0xd7, 0x33, 0xbd, 0x13, 0xd9,
|
||||
0x15, 0x40, 0xe9, 0xe6, 0x92, 0xdd, 0x48, 0xa0, 0x4a, 0x77, 0x02, 0x25, 0x34, 0xc9, 0x4a, 0xd6,
|
||||
0xc4, 0xc2, 0x9b, 0xd5, 0x44, 0xfc, 0x09, 0x2a, 0x88, 0x8e, 0xd7, 0x71, 0xe1, 0xda, 0x95, 0xab,
|
||||
0x7e, 0x87, 0xa7, 0x32, 0xc0, 0x5a, 0xae, 0x4a, 0x65, 0x72, 0xac, 0x3e, 0x3b, 0x22, 0x90, 0x7f,
|
||||
0x4c, 0xa1, 0x02, 0x65, 0xfe, 0xc8, 0x75, 0x7c, 0xf6, 0x4d, 0x9d, 0x60, 0x0d, 0x65, 0x2d, 0x33,
|
||||
0x30, 0xa5, 0xdb, 0xc1, 0xee, 0xf1, 0xb1, 0xda, 0x3d, 0x3e, 0x20, 0x14, 0x30, 0xfc, 0x29, 0xca,
|
||||
0xf6, 0x5c, 0x4b, 0x1c, 0xfe, 0x92, 0x9e, 0x34, 0x1b, 0x9e, 0xe7, 0x7a, 0x35, 0xd7, 0x92, 0xd7,
|
||||
0x0e, 0x4e, 0x52, 0x06, 0xf8, 0x80, 0x50, 0xc0, 0xc8, 0x3f, 0xa4, 0x50, 0xb1, 0xee, 0x1e, 0x39,
|
||||
0x03, 0xd7, 0xb4, 0x76, 0x3c, 0xb7, 0xef, 0x31, 0xdf, 0xff, 0x46, 0x77, 0xff, 0x2e, 0x9a, 0x1d,
|
||||
0xc3, 0xcb, 0x41, 0x74, 0xfb, 0x7f, 0x90, 0xbc, 0x06, 0x9d, 0x9f, 0x44, 0x3c, 0x33, 0xc4, 0x0f,
|
||||
0x8d, 0x52, 0x59, 0xd9, 0x17, 0x63, 0x42, 0x23, 0x01, 0xf9, 0xfb, 0x0c, 0x2a, 0x5f, 0x6d, 0x08,
|
||||
0x0f, 0xd1, 0xbc, 0x60, 0x76, 0xb5, 0xbf, 0x09, 0xac, 0x5e, 0x67, 0x0d, 0x70, 0x39, 0x83, 0x4b,
|
||||
0xc1, 0x58, 0x8d, 0xd5, 0xa5, 0x20, 0x86, 0x08, 0xd5, 0xe4, 0x6f, 0xf4, 0x4e, 0xa9, 0x5d, 0xe5,
|
||||
0x33, 0xdf, 0xfe, 0x2a, 0xdf, 0x41, 0x8b, 0xc2, 0x45, 0xa3, 0x07, 0xe5, 0x6c, 0x25, 0xb3, 0x9a,
|
||||
0xab, 0x3e, 0xe4, 0xd9, 0x76, 0x4f, 0x34, 0xab, 0xd1, 0x53, 0xf2, 0x72, 0xec, 0xac, 0x02, 0x8c,
|
||||
0xbc, 0xad, 0x38, 0x43, 0x13, 0x5c, 0xbc, 0x99, 0xb8, 0xe9, 0x89, 0x50, 0xff, 0x83, 0x6b, 0xde,
|
||||
0xec, 0xb4, 0x9b, 0x1c, 0xc9, 0xa3, 0xec, 0x8e, 0xed, 0xf4, 0xc9, 0x47, 0x28, 0x57, 0x1b, 0xb8,
|
||||
0x3e, 0x64, 0x1c, 0x8f, 0x99, 0xbe, 0xeb, 0xe8, 0xae, 0x24, 0x10, 0x75, 0xd4, 0x62, 0x48, 0xa8,
|
||||
0xc4, 0xd7, 0x7e, 0x9b, 0x41, 0xf3, 0xda, 0x9f, 0x70, 0xf0, 0x9f, 0xa0, 0x7b, 0x8f, 0x1b, 0x9d,
|
||||
0xce, 0xc6, 0x56, 0xa3, 0xbb, 0xfb, 0xe5, 0x4e, 0xa3, 0x5b, 0xdb, 0x7e, 0xd2, 0xd9, 0x6d, 0xd0,
|
||||
0x6e, 0xad, 0xdd, 0xda, 0x6c, 0x6e, 0x15, 0x67, 0xca, 0xf7, 0x4f, 0xcf, 0x2a, 0x25, 0x4d, 0x23,
|
||||
0xf9, 0xb7, 0x96, 0x3f, 0x44, 0x38, 0xa1, 0xde, 0x6c, 0xd5, 0x1b, 0x3f, 0x2d, 0xa6, 0xca, 0xb7,
|
||||
0x4e, 0xcf, 0x2a, 0x45, 0x4d, 0x4b, 0x3c, 0xc1, 0xfd, 0x31, 0x7a, 0xeb, 0x22, 0xbb, 0xfb, 0x64,
|
||||
0xa7, 0xbe, 0xb1, 0xdb, 0x28, 0xa6, 0xcb, 0xe5, 0xd3, 0xb3, 0xca, 0x9d, 0xf3, 0x4a, 0xd2, 0x05,
|
||||
0x7f, 0x80, 0x6e, 0x25, 0x54, 0x69, 0xe3, 0xf3, 0x27, 0x8d, 0xce, 0x6e, 0x31, 0x53, 0xbe, 0x73,
|
||||
0x7a, 0x56, 0xc1, 0x9a, 0x56, 0x54, 0x26, 0xd6, 0xd1, 0xed, 0x73, 0x1a, 0x9d, 0x9d, 0x76, 0xab,
|
||||
0xd3, 0x28, 0x66, 0xcb, 0x77, 0x4f, 0xcf, 0x2a, 0x37, 0x13, 0x2a, 0x32, 0xab, 0xd4, 0xd0, 0x4a,
|
||||
0x42, 0xa7, 0xde, 0xfe, 0xa2, 0xb5, 0xdd, 0xde, 0xa8, 0x77, 0x77, 0x68, 0x7b, 0x8b, 0x36, 0x3a,
|
||||
0x9d, 0x62, 0xae, 0x6c, 0x9c, 0x9e, 0x55, 0xee, 0x69, 0xca, 0x17, 0x22, 0x7c, 0x0d, 0x2d, 0x27,
|
||||
0x8c, 0xec, 0x34, 0x5b, 0x5b, 0xc5, 0x7c, 0xf9, 0xe6, 0xe9, 0x59, 0xe5, 0x86, 0xa6, 0xc7, 0xcf,
|
||||
0xf2, 0xc2, 0xfe, 0xd5, 0xb6, 0xdb, 0x9d, 0x46, 0x71, 0xf6, 0xc2, 0xfe, 0xc1, 0x81, 0xaf, 0xfd,
|
||||
0x5d, 0x0a, 0xe1, 0x8b, 0x7f, 0x35, 0xc3, 0x1f, 0xa0, 0x52, 0x64, 0xa4, 0xd6, 0x7e, 0xbc, 0xc3,
|
||||
0xd7, 0xd9, 0x6c, 0xb7, 0xba, 0xad, 0x76, 0xab, 0x51, 0x9c, 0x49, 0xec, 0xaa, 0xa6, 0xd5, 0x72,
|
||||
0x1d, 0x86, 0xdb, 0xe8, 0xee, 0x65, 0x9a, 0xdb, 0xcf, 0xde, 0x2f, 0xa6, 0xca, 0xeb, 0xa7, 0x67,
|
||||
0x95, 0xdb, 0x17, 0x15, 0xb7, 0x9f, 0xbd, 0xff, 0xbb, 0xbf, 0xfa, 0xee, 0xe5, 0x82, 0x35, 0xde,
|
||||
0x00, 0xe9, 0x4b, 0x7b, 0x0f, 0xdd, 0xd2, 0x0d, 0x3f, 0x6e, 0xec, 0x6e, 0xd4, 0x37, 0x76, 0x37,
|
||||
0x8a, 0x33, 0xe2, 0x0c, 0x34, 0xea, 0x63, 0x16, 0x98, 0x90, 0x76, 0xbf, 0x87, 0x96, 0x13, 0x5f,
|
||||
0xd1, 0x78, 0xda, 0xa0, 0x91, 0x47, 0xe9, 0xeb, 0x67, 0x87, 0xcc, 0xc3, 0xdf, 0x47, 0x58, 0x27,
|
||||
0x6f, 0x6c, 0x7f, 0xb1, 0xf1, 0x65, 0xa7, 0x98, 0x2e, 0xdf, 0x3e, 0x3d, 0xab, 0x2c, 0x6b, 0xec,
|
||||
0x8d, 0xc1, 0x91, 0x79, 0xe2, 0xaf, 0xfd, 0x73, 0x1a, 0x2d, 0xe8, 0xef, 0x46, 0xf8, 0xfb, 0xe8,
|
||||
0xe6, 0x66, 0x73, 0x9b, 0x7b, 0xe2, 0x66, 0x5b, 0x9c, 0x00, 0x1f, 0x16, 0x67, 0xc4, 0x74, 0x3a,
|
||||
0x95, 0xff, 0xc6, 0x7f, 0x84, 0x4a, 0xe7, 0xe8, 0xf5, 0x26, 0x6d, 0xd4, 0x76, 0xdb, 0xf4, 0xcb,
|
||||
0x62, 0xaa, 0xfc, 0x16, 0xdf, 0x30, 0x5d, 0xa7, 0x6e, 0x7b, 0x90, 0x82, 0x4e, 0xf0, 0x27, 0xe8,
|
||||
0xde, 0x39, 0xc5, 0xce, 0x97, 0x8f, 0xb7, 0x9b, 0xad, 0xcf, 0xc4, 0x7c, 0xe9, 0xf2, 0xdb, 0xa7,
|
||||
0x67, 0x95, 0xbb, 0xba, 0x6e, 0x47, 0x3c, 0xc5, 0x71, 0xa8, 0x90, 0xc2, 0x8f, 0x50, 0xe5, 0x0a,
|
||||
0xfd, 0x78, 0x01, 0x99, 0x32, 0x39, 0x3d, 0xab, 0xdc, 0xbf, 0xc4, 0x88, 0x5a, 0x47, 0x21, 0x85,
|
||||
0x7f, 0x88, 0xee, 0x5c, 0x6e, 0x29, 0x8a, 0x8b, 0x4b, 0xf4, 0xd7, 0xfe, 0x35, 0x85, 0xe6, 0x54,
|
||||
0xd5, 0xe3, 0x9b, 0xd6, 0xa0, 0xb4, 0xcd, 0x93, 0x44, 0xbd, 0xd1, 0x6d, 0xb5, 0xbb, 0x30, 0x8a,
|
||||
0x36, 0x4d, 0xf1, 0x5a, 0x2e, 0xfc, 0xe4, 0x3e, 0xae, 0xd1, 0xb7, 0x1a, 0xad, 0x06, 0x6d, 0xd6,
|
||||
0xa2, 0x13, 0x55, 0xec, 0x2d, 0xe6, 0x30, 0xcf, 0xee, 0xe1, 0xf7, 0xd1, 0xdd, 0xa4, 0xf1, 0xce,
|
||||
0x93, 0xda, 0xa3, 0x68, 0x97, 0x60, 0x81, 0xda, 0x04, 0x9d, 0x71, 0xef, 0x00, 0x0e, 0xe6, 0x47,
|
||||
0x09, 0xad, 0x66, 0xeb, 0xe9, 0xc6, 0x76, 0xb3, 0x2e, 0xb4, 0x32, 0xe5, 0xd2, 0xe9, 0x59, 0xe5,
|
||||
0x96, 0xd2, 0x92, 0x0f, 0x1c, 0x5c, 0x6d, 0xed, 0x77, 0x29, 0xb4, 0xf2, 0xf5, 0xc5, 0x0b, 0x7f,
|
||||
0x81, 0xde, 0x81, 0xfd, 0xba, 0x90, 0x0a, 0x64, 0xde, 0x12, 0x7b, 0xb8, 0xb1, 0xb3, 0xd3, 0x68,
|
||||
0xd5, 0x8b, 0x33, 0xe5, 0xd5, 0xd3, 0xb3, 0xca, 0x83, 0xaf, 0x37, 0xb9, 0x31, 0x1a, 0x31, 0xc7,
|
||||
0xba, 0xa6, 0xe1, 0xcd, 0x36, 0xdd, 0x6a, 0xec, 0x16, 0x53, 0xd7, 0x31, 0xbc, 0xe9, 0x7a, 0x7d,
|
||||
0x16, 0x54, 0x1f, 0xbf, 0xf8, 0x6a, 0x65, 0xe6, 0xe5, 0x57, 0x2b, 0x33, 0x2f, 0x5e, 0xad, 0xa4,
|
||||
0x5e, 0xbe, 0x5a, 0x49, 0xfd, 0xf5, 0xeb, 0x95, 0x99, 0xdf, 0xbc, 0x5e, 0x49, 0xbd, 0x7c, 0xbd,
|
||||
0x32, 0xf3, 0x6f, 0xaf, 0x57, 0x66, 0x9e, 0x7d, 0xaf, 0x6f, 0x07, 0x07, 0xe3, 0xbd, 0x87, 0x3d,
|
||||
0x77, 0xf8, 0xae, 0x7f, 0xe2, 0xf4, 0x82, 0x03, 0xdb, 0xe9, 0x6b, 0xbf, 0xf4, 0xff, 0xd9, 0xb1,
|
||||
0x97, 0x87, 0x5f, 0x3f, 0xfc, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x35, 0x55, 0x08, 0x69, 0xf0,
|
||||
0x21, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *Hello) Marshal() (dAtA []byte, err error) {
|
||||
@ -1363,6 +1372,16 @@ func (m *Hello) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.Timestamp != 0 {
|
||||
i = encodeVarintBep(dAtA, i, uint64(m.Timestamp))
|
||||
i--
|
||||
dAtA[i] = 0x28
|
||||
}
|
||||
if m.NumConnections != 0 {
|
||||
i = encodeVarintBep(dAtA, i, uint64(m.NumConnections))
|
||||
i--
|
||||
dAtA[i] = 0x20
|
||||
}
|
||||
if len(m.ClientVersion) > 0 {
|
||||
i -= len(m.ClientVersion)
|
||||
copy(dAtA[i:], m.ClientVersion)
|
||||
@ -1440,6 +1459,16 @@ func (m *ClusterConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.Secondary {
|
||||
i--
|
||||
if m.Secondary {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x10
|
||||
}
|
||||
if len(m.Folders) > 0 {
|
||||
for iNdEx := len(m.Folders) - 1; iNdEx >= 0; iNdEx-- {
|
||||
{
|
||||
@ -2612,6 +2641,12 @@ func (m *Hello) ProtoSize() (n int) {
|
||||
if l > 0 {
|
||||
n += 1 + l + sovBep(uint64(l))
|
||||
}
|
||||
if m.NumConnections != 0 {
|
||||
n += 1 + sovBep(uint64(m.NumConnections))
|
||||
}
|
||||
if m.Timestamp != 0 {
|
||||
n += 1 + sovBep(uint64(m.Timestamp))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@ -2642,6 +2677,9 @@ func (m *ClusterConfig) ProtoSize() (n int) {
|
||||
n += 1 + l + sovBep(uint64(l))
|
||||
}
|
||||
}
|
||||
if m.Secondary {
|
||||
n += 2
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@ -3258,6 +3296,44 @@ func (m *Hello) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
m.ClientVersion = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 4:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field NumConnections", wireType)
|
||||
}
|
||||
m.NumConnections = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowBep
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.NumConnections |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 5:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType)
|
||||
}
|
||||
m.Timestamp = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowBep
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.Timestamp |= int64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipBep(dAtA[iNdEx:])
|
||||
@ -3430,6 +3506,26 @@ func (m *ClusterConfig) Unmarshal(dAtA []byte) error {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Secondary", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowBep
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
v |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
m.Secondary = bool(v != 0)
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipBep(dAtA[iNdEx:])
|
||||
|
@ -43,12 +43,12 @@ const (
|
||||
// receives encrypted metadata and requests from the untrusted device, so it
|
||||
// must decrypt those and answer requests by encrypting the data.
|
||||
type encryptedModel struct {
|
||||
model contextLessModel
|
||||
model rawModel
|
||||
folderKeys *folderKeyRegistry
|
||||
keyGen *KeyGenerator
|
||||
}
|
||||
|
||||
func newEncryptedModel(model contextLessModel, folderKeys *folderKeyRegistry, keyGen *KeyGenerator) encryptedModel {
|
||||
func newEncryptedModel(model rawModel, folderKeys *folderKeyRegistry, keyGen *KeyGenerator) encryptedModel {
|
||||
return encryptedModel{
|
||||
model: model,
|
||||
folderKeys: folderKeys,
|
||||
|
@ -8,14 +8,6 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// The HelloIntf interface is implemented by the version specific hello
|
||||
// message. It knows its magic number and how to serialize itself to a byte
|
||||
// buffer.
|
||||
type HelloIntf interface {
|
||||
Magic() uint32
|
||||
Marshal() ([]byte, error)
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrTooOldVersion is returned by ExchangeHello when the other side
|
||||
// speaks an older, incompatible version of the protocol.
|
||||
@ -25,7 +17,10 @@ var (
|
||||
ErrUnknownMagic = errors.New("the remote device speaks an unknown (newer?) version of the protocol")
|
||||
)
|
||||
|
||||
func ExchangeHello(c io.ReadWriter, h HelloIntf) (Hello, error) {
|
||||
func ExchangeHello(c io.ReadWriter, h Hello) (Hello, error) {
|
||||
if h.Timestamp == 0 {
|
||||
panic("bug: missing timestamp in outgoing hello")
|
||||
}
|
||||
if err := writeHello(c, h); err != nil {
|
||||
return Hello{}, err
|
||||
}
|
||||
@ -80,7 +75,7 @@ func readHello(c io.Reader) (Hello, error) {
|
||||
return Hello{}, ErrUnknownMagic
|
||||
}
|
||||
|
||||
func writeHello(c io.Writer, h HelloIntf) error {
|
||||
func writeHello(c io.Writer, h Hello) error {
|
||||
msg, err := h.Marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -35,10 +35,11 @@ func TestVersion14Hello(t *testing.T) {
|
||||
|
||||
conn := &readWriter{outBuf, inBuf}
|
||||
|
||||
send := &Hello{
|
||||
send := Hello{
|
||||
DeviceName: "this device",
|
||||
ClientName: "other client",
|
||||
ClientVersion: "v0.14.6",
|
||||
Timestamp: 1234567890,
|
||||
}
|
||||
|
||||
res, err := ExchangeHello(conn, send)
|
||||
@ -80,10 +81,11 @@ func TestOldHelloMsgs(t *testing.T) {
|
||||
|
||||
conn := &readWriter{outBuf, inBuf}
|
||||
|
||||
send := &Hello{
|
||||
send := Hello{
|
||||
DeviceName: "this device",
|
||||
ClientName: "other client",
|
||||
ClientVersion: "v1.0.0",
|
||||
Timestamp: 1234567890,
|
||||
}
|
||||
|
||||
_, err := ExchangeHello(conn, send)
|
||||
|
@ -8,6 +8,16 @@ import (
|
||||
)
|
||||
|
||||
type mockedConnectionInfo struct {
|
||||
ConnectionIDStub func() string
|
||||
connectionIDMutex sync.RWMutex
|
||||
connectionIDArgsForCall []struct {
|
||||
}
|
||||
connectionIDReturns struct {
|
||||
result1 string
|
||||
}
|
||||
connectionIDReturnsOnCall map[int]struct {
|
||||
result1 string
|
||||
}
|
||||
CryptoStub func() string
|
||||
cryptoMutex sync.RWMutex
|
||||
cryptoArgsForCall []struct {
|
||||
@ -92,6 +102,59 @@ type mockedConnectionInfo struct {
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *mockedConnectionInfo) ConnectionID() string {
|
||||
fake.connectionIDMutex.Lock()
|
||||
ret, specificReturn := fake.connectionIDReturnsOnCall[len(fake.connectionIDArgsForCall)]
|
||||
fake.connectionIDArgsForCall = append(fake.connectionIDArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.ConnectionIDStub
|
||||
fakeReturns := fake.connectionIDReturns
|
||||
fake.recordInvocation("ConnectionID", []interface{}{})
|
||||
fake.connectionIDMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *mockedConnectionInfo) ConnectionIDCallCount() int {
|
||||
fake.connectionIDMutex.RLock()
|
||||
defer fake.connectionIDMutex.RUnlock()
|
||||
return len(fake.connectionIDArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *mockedConnectionInfo) ConnectionIDCalls(stub func() string) {
|
||||
fake.connectionIDMutex.Lock()
|
||||
defer fake.connectionIDMutex.Unlock()
|
||||
fake.ConnectionIDStub = stub
|
||||
}
|
||||
|
||||
func (fake *mockedConnectionInfo) ConnectionIDReturns(result1 string) {
|
||||
fake.connectionIDMutex.Lock()
|
||||
defer fake.connectionIDMutex.Unlock()
|
||||
fake.ConnectionIDStub = nil
|
||||
fake.connectionIDReturns = struct {
|
||||
result1 string
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *mockedConnectionInfo) ConnectionIDReturnsOnCall(i int, result1 string) {
|
||||
fake.connectionIDMutex.Lock()
|
||||
defer fake.connectionIDMutex.Unlock()
|
||||
fake.ConnectionIDStub = nil
|
||||
if fake.connectionIDReturnsOnCall == nil {
|
||||
fake.connectionIDReturnsOnCall = make(map[int]struct {
|
||||
result1 string
|
||||
})
|
||||
}
|
||||
fake.connectionIDReturnsOnCall[i] = struct {
|
||||
result1 string
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *mockedConnectionInfo) Crypto() string {
|
||||
fake.cryptoMutex.Lock()
|
||||
ret, specificReturn := fake.cryptoReturnsOnCall[len(fake.cryptoArgsForCall)]
|
||||
@ -519,6 +582,8 @@ func (fake *mockedConnectionInfo) TypeReturnsOnCall(i int, result1 string) {
|
||||
func (fake *mockedConnectionInfo) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
fake.connectionIDMutex.RLock()
|
||||
defer fake.connectionIDMutex.RUnlock()
|
||||
fake.cryptoMutex.RLock()
|
||||
defer fake.cryptoMutex.RUnlock()
|
||||
fake.establishedAtMutex.RLock()
|
||||
|
@ -31,6 +31,16 @@ type Connection struct {
|
||||
clusterConfigArgsForCall []struct {
|
||||
arg1 protocol.ClusterConfig
|
||||
}
|
||||
ConnectionIDStub func() string
|
||||
connectionIDMutex sync.RWMutex
|
||||
connectionIDArgsForCall []struct {
|
||||
}
|
||||
connectionIDReturns struct {
|
||||
result1 string
|
||||
}
|
||||
connectionIDReturnsOnCall map[int]struct {
|
||||
result1 string
|
||||
}
|
||||
CryptoStub func() string
|
||||
cryptoMutex sync.RWMutex
|
||||
cryptoArgsForCall []struct {
|
||||
@ -315,6 +325,59 @@ func (fake *Connection) ClusterConfigArgsForCall(i int) protocol.ClusterConfig {
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *Connection) ConnectionID() string {
|
||||
fake.connectionIDMutex.Lock()
|
||||
ret, specificReturn := fake.connectionIDReturnsOnCall[len(fake.connectionIDArgsForCall)]
|
||||
fake.connectionIDArgsForCall = append(fake.connectionIDArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.ConnectionIDStub
|
||||
fakeReturns := fake.connectionIDReturns
|
||||
fake.recordInvocation("ConnectionID", []interface{}{})
|
||||
fake.connectionIDMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *Connection) ConnectionIDCallCount() int {
|
||||
fake.connectionIDMutex.RLock()
|
||||
defer fake.connectionIDMutex.RUnlock()
|
||||
return len(fake.connectionIDArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *Connection) ConnectionIDCalls(stub func() string) {
|
||||
fake.connectionIDMutex.Lock()
|
||||
defer fake.connectionIDMutex.Unlock()
|
||||
fake.ConnectionIDStub = stub
|
||||
}
|
||||
|
||||
func (fake *Connection) ConnectionIDReturns(result1 string) {
|
||||
fake.connectionIDMutex.Lock()
|
||||
defer fake.connectionIDMutex.Unlock()
|
||||
fake.ConnectionIDStub = nil
|
||||
fake.connectionIDReturns = struct {
|
||||
result1 string
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *Connection) ConnectionIDReturnsOnCall(i int, result1 string) {
|
||||
fake.connectionIDMutex.Lock()
|
||||
defer fake.connectionIDMutex.Unlock()
|
||||
fake.ConnectionIDStub = nil
|
||||
if fake.connectionIDReturnsOnCall == nil {
|
||||
fake.connectionIDReturnsOnCall = make(map[int]struct {
|
||||
result1 string
|
||||
})
|
||||
}
|
||||
fake.connectionIDReturnsOnCall[i] = struct {
|
||||
result1 string
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *Connection) Crypto() string {
|
||||
fake.cryptoMutex.Lock()
|
||||
ret, specificReturn := fake.cryptoReturnsOnCall[len(fake.cryptoArgsForCall)]
|
||||
@ -1162,6 +1225,8 @@ func (fake *Connection) Invocations() map[string][][]interface{} {
|
||||
defer fake.closedMutex.RUnlock()
|
||||
fake.clusterConfigMutex.RLock()
|
||||
defer fake.clusterConfigMutex.RUnlock()
|
||||
fake.connectionIDMutex.RLock()
|
||||
defer fake.connectionIDMutex.RUnlock()
|
||||
fake.cryptoMutex.RLock()
|
||||
defer fake.cryptoMutex.RUnlock()
|
||||
fake.deviceIDMutex.RLock()
|
||||
|
@ -10,6 +10,16 @@ import (
|
||||
)
|
||||
|
||||
type ConnectionInfo struct {
|
||||
ConnectionIDStub func() string
|
||||
connectionIDMutex sync.RWMutex
|
||||
connectionIDArgsForCall []struct {
|
||||
}
|
||||
connectionIDReturns struct {
|
||||
result1 string
|
||||
}
|
||||
connectionIDReturnsOnCall map[int]struct {
|
||||
result1 string
|
||||
}
|
||||
CryptoStub func() string
|
||||
cryptoMutex sync.RWMutex
|
||||
cryptoArgsForCall []struct {
|
||||
@ -94,6 +104,59 @@ type ConnectionInfo struct {
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *ConnectionInfo) ConnectionID() string {
|
||||
fake.connectionIDMutex.Lock()
|
||||
ret, specificReturn := fake.connectionIDReturnsOnCall[len(fake.connectionIDArgsForCall)]
|
||||
fake.connectionIDArgsForCall = append(fake.connectionIDArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.ConnectionIDStub
|
||||
fakeReturns := fake.connectionIDReturns
|
||||
fake.recordInvocation("ConnectionID", []interface{}{})
|
||||
fake.connectionIDMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *ConnectionInfo) ConnectionIDCallCount() int {
|
||||
fake.connectionIDMutex.RLock()
|
||||
defer fake.connectionIDMutex.RUnlock()
|
||||
return len(fake.connectionIDArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *ConnectionInfo) ConnectionIDCalls(stub func() string) {
|
||||
fake.connectionIDMutex.Lock()
|
||||
defer fake.connectionIDMutex.Unlock()
|
||||
fake.ConnectionIDStub = stub
|
||||
}
|
||||
|
||||
func (fake *ConnectionInfo) ConnectionIDReturns(result1 string) {
|
||||
fake.connectionIDMutex.Lock()
|
||||
defer fake.connectionIDMutex.Unlock()
|
||||
fake.ConnectionIDStub = nil
|
||||
fake.connectionIDReturns = struct {
|
||||
result1 string
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *ConnectionInfo) ConnectionIDReturnsOnCall(i int, result1 string) {
|
||||
fake.connectionIDMutex.Lock()
|
||||
defer fake.connectionIDMutex.Unlock()
|
||||
fake.ConnectionIDStub = nil
|
||||
if fake.connectionIDReturnsOnCall == nil {
|
||||
fake.connectionIDReturnsOnCall = make(map[int]struct {
|
||||
result1 string
|
||||
})
|
||||
}
|
||||
fake.connectionIDReturnsOnCall[i] = struct {
|
||||
result1 string
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *ConnectionInfo) Crypto() string {
|
||||
fake.cryptoMutex.Lock()
|
||||
ret, specificReturn := fake.cryptoReturnsOnCall[len(fake.cryptoArgsForCall)]
|
||||
@ -521,6 +584,8 @@ func (fake *ConnectionInfo) TypeReturnsOnCall(i int, result1 string) {
|
||||
func (fake *ConnectionInfo) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
fake.connectionIDMutex.RLock()
|
||||
defer fake.connectionIDMutex.RUnlock()
|
||||
fake.cryptoMutex.RLock()
|
||||
defer fake.cryptoMutex.RUnlock()
|
||||
fake.establishedAtMutex.RLock()
|
||||
|
@ -9,27 +9,27 @@ package protocol
|
||||
|
||||
import "golang.org/x/text/unicode/norm"
|
||||
|
||||
func makeNative(m contextLessModel) contextLessModel { return nativeModel{m} }
|
||||
func makeNative(m rawModel) rawModel { return nativeModel{m} }
|
||||
|
||||
type nativeModel struct {
|
||||
contextLessModel
|
||||
rawModel
|
||||
}
|
||||
|
||||
func (m nativeModel) Index(folder string, files []FileInfo) error {
|
||||
for i := range files {
|
||||
files[i].Name = norm.NFD.String(files[i].Name)
|
||||
}
|
||||
return m.contextLessModel.Index(folder, files)
|
||||
return m.rawModel.Index(folder, files)
|
||||
}
|
||||
|
||||
func (m nativeModel) IndexUpdate(folder string, files []FileInfo) error {
|
||||
for i := range files {
|
||||
files[i].Name = norm.NFD.String(files[i].Name)
|
||||
}
|
||||
return m.contextLessModel.IndexUpdate(folder, files)
|
||||
return m.rawModel.IndexUpdate(folder, files)
|
||||
}
|
||||
|
||||
func (m nativeModel) Request(folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) {
|
||||
name = norm.NFD.String(name)
|
||||
return m.contextLessModel.Request(folder, name, blockNo, size, offset, hash, weakHash, fromTemporary)
|
||||
return m.rawModel.Request(folder, name, blockNo, size, offset, hash, weakHash, fromTemporary)
|
||||
}
|
||||
|
@ -7,4 +7,4 @@ package protocol
|
||||
|
||||
// Normal Unixes uses NFC and slashes, which is the wire format.
|
||||
|
||||
func makeNative(m contextLessModel) contextLessModel { return m }
|
||||
func makeNative(m rawModel) rawModel { return m }
|
||||
|
@ -13,20 +13,20 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func makeNative(m contextLessModel) contextLessModel { return nativeModel{m} }
|
||||
func makeNative(m rawModel) rawModel { return nativeModel{m} }
|
||||
|
||||
type nativeModel struct {
|
||||
contextLessModel
|
||||
rawModel
|
||||
}
|
||||
|
||||
func (m nativeModel) Index(folder string, files []FileInfo) error {
|
||||
files = fixupFiles(files)
|
||||
return m.contextLessModel.Index(folder, files)
|
||||
return m.rawModel.Index(folder, files)
|
||||
}
|
||||
|
||||
func (m nativeModel) IndexUpdate(folder string, files []FileInfo) error {
|
||||
files = fixupFiles(files)
|
||||
return m.contextLessModel.IndexUpdate(folder, files)
|
||||
return m.rawModel.IndexUpdate(folder, files)
|
||||
}
|
||||
|
||||
func (m nativeModel) Request(folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) {
|
||||
@ -36,7 +36,7 @@ func (m nativeModel) Request(folder, name string, blockNo, size int32, offset in
|
||||
}
|
||||
|
||||
name = filepath.FromSlash(name)
|
||||
return m.contextLessModel.Request(folder, name, blockNo, size, offset, hash, weakHash, fromTemporary)
|
||||
return m.rawModel.Request(folder, name, blockNo, size, offset, hash, weakHash, fromTemporary)
|
||||
}
|
||||
|
||||
func fixupFiles(files []FileInfo) []FileInfo {
|
||||
|
@ -136,9 +136,9 @@ type Model interface {
|
||||
DownloadProgress(conn Connection, folder string, updates []FileDownloadProgressUpdate) error
|
||||
}
|
||||
|
||||
// contextLessModel is the Model interface, but without the initial
|
||||
// Connection parameter. Internal use only.
|
||||
type contextLessModel interface {
|
||||
// rawModel is the Model interface, but without the initial Connection
|
||||
// parameter. Internal use only.
|
||||
type rawModel interface {
|
||||
Index(folder string, files []FileInfo) error
|
||||
IndexUpdate(folder string, files []FileInfo) error
|
||||
Request(folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error)
|
||||
@ -177,6 +177,7 @@ type ConnectionInfo interface {
|
||||
String() string
|
||||
Crypto() string
|
||||
EstablishedAt() time.Time
|
||||
ConnectionID() string
|
||||
}
|
||||
|
||||
type rawConnection struct {
|
||||
@ -184,8 +185,9 @@ type rawConnection struct {
|
||||
|
||||
deviceID DeviceID
|
||||
idString string
|
||||
model contextLessModel
|
||||
model rawModel
|
||||
startTime time.Time
|
||||
started chan struct{}
|
||||
|
||||
cr *countingReader
|
||||
cw *countingWriter
|
||||
@ -263,7 +265,7 @@ func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer
|
||||
return wc
|
||||
}
|
||||
|
||||
func newRawConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer io.Closer, receiver contextLessModel, connInfo ConnectionInfo, compress Compression) *rawConnection {
|
||||
func newRawConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer io.Closer, receiver rawModel, connInfo ConnectionInfo, compress Compression) *rawConnection {
|
||||
idString := deviceID.String()
|
||||
cr := &countingReader{Reader: reader, idString: idString}
|
||||
cw := &countingWriter{Writer: writer, idString: idString}
|
||||
@ -274,6 +276,7 @@ func newRawConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, clo
|
||||
deviceID: deviceID,
|
||||
idString: deviceID.String(),
|
||||
model: receiver,
|
||||
started: make(chan struct{}),
|
||||
cr: cr,
|
||||
cw: cw,
|
||||
closer: closer,
|
||||
@ -315,6 +318,7 @@ func (c *rawConnection) Start() {
|
||||
c.loopWG.Done()
|
||||
}()
|
||||
c.startTime = time.Now().Truncate(time.Second)
|
||||
close(c.started)
|
||||
}
|
||||
|
||||
func (c *rawConnection) DeviceID() DeviceID {
|
||||
@ -960,9 +964,9 @@ func (c *rawConnection) Close(err error) {
|
||||
// internalClose is called if there is an unexpected error during normal operation.
|
||||
func (c *rawConnection) internalClose(err error) {
|
||||
c.closeOnce.Do(func() {
|
||||
l.Debugln("close due to", err)
|
||||
l.Debugf("close connection to %s at %s due to %v", c.deviceID.Short(), c.ConnectionInfo, err)
|
||||
if cerr := c.closer.Close(); cerr != nil {
|
||||
l.Debugln(c.deviceID, "failed to close underlying conn:", cerr)
|
||||
l.Debugf("failed to close underlying conn %s at %s %v:", c.deviceID.Short(), c.ConnectionInfo, cerr)
|
||||
}
|
||||
close(c.closed)
|
||||
|
||||
@ -975,7 +979,11 @@ func (c *rawConnection) internalClose(err error) {
|
||||
}
|
||||
c.awaitingMut.Unlock()
|
||||
|
||||
<-c.dispatcherLoopStopped
|
||||
if !c.startTime.IsZero() {
|
||||
// Wait for the dispatcher loop to exit, if it was started to
|
||||
// begin with.
|
||||
<-c.dispatcherLoopStopped
|
||||
}
|
||||
|
||||
c.model.Closed(err)
|
||||
})
|
||||
@ -1108,7 +1116,7 @@ func messageContext(msg message) (string, error) {
|
||||
|
||||
// connectionWrappingModel takes the Model interface from the model package,
|
||||
// which expects the Connection as the first parameter in all methods, and
|
||||
// wraps it to conform to the protocol.contextLessModel interface.
|
||||
// wraps it to conform to the rawModel interface.
|
||||
type connectionWrappingModel struct {
|
||||
conn Connection
|
||||
model Model
|
||||
|
16
lib/sliceutil/sliceutil.go
Normal file
16
lib/sliceutil/sliceutil.go
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright (C) 2023 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package sliceutil
|
||||
|
||||
// RemoveAndZero removes the element at index i from slice s and returns the
|
||||
// resulting slice. The slice ordering is preserved; the last slice element
|
||||
// is zeroed before shrinking.
|
||||
func RemoveAndZero[E any, S ~[]E](s S, i int) S {
|
||||
copy(s[i:], s[i+1:])
|
||||
s[len(s)-1] = *new(E)
|
||||
return s[:len(s)-1]
|
||||
}
|
28
lib/sliceutil/sliceutil_test.go
Normal file
28
lib/sliceutil/sliceutil_test.go
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2023 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package sliceutil_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/sliceutil"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
func TestRemoveAndZero(t *testing.T) {
|
||||
a := []int{1, 2, 3, 4, 5}
|
||||
b := sliceutil.RemoveAndZero(a, 2)
|
||||
exp := []int{1, 2, 4, 5}
|
||||
if !slices.Equal(b, exp) {
|
||||
t.Errorf("got %v, expected %v", b, exp)
|
||||
}
|
||||
for _, e := range a {
|
||||
if e == 3 {
|
||||
t.Errorf("element should have been zeroed")
|
||||
}
|
||||
}
|
||||
}
|
@ -249,7 +249,7 @@ func (a *App) startup() error {
|
||||
}
|
||||
|
||||
keyGen := protocol.NewKeyGenerator()
|
||||
m := model.NewModel(a.cfg, a.myID, "syncthing", build.Version, a.ll, protectedFiles, a.evLogger, keyGen)
|
||||
m := model.NewModel(a.cfg, a.myID, a.ll, protectedFiles, a.evLogger, keyGen)
|
||||
|
||||
if a.opts.DeadlockTimeoutS > 0 {
|
||||
m.StartDeadlockDetector(time.Duration(a.opts.DeadlockTimeoutS) * time.Second)
|
||||
|
@ -10,7 +10,7 @@ import "ext.proto";
|
||||
message DeviceConfiguration {
|
||||
bytes device_id = 1 [(ext.goname) = "DeviceID", (ext.xml) = "id,attr", (ext.json) = "deviceID", (ext.device_id) = true, (ext.nodefault) = true];
|
||||
string name = 2 [(ext.xml) = "name,attr,omitempty"];
|
||||
repeated string addresses = 3 [(ext.xml) = "address,omitempty", (ext.default) = "dynamic"];
|
||||
repeated string addresses = 3 [(ext.xml) = "address,omitempty"];
|
||||
protocol.Compression compression = 4 [(ext.xml) = "compression,attr"];
|
||||
string cert_name = 5 [(ext.xml) = "certName,attr,omitempty"];
|
||||
bool introducer = 6 [(ext.xml) = "introducer,attr"];
|
||||
@ -26,4 +26,5 @@ message DeviceConfiguration {
|
||||
int32 max_request_kib = 16 [(ext.goname) = "MaxRequestKiB", (ext.xml) = "maxRequestKiB", (ext.json) = "maxRequestKiB"];
|
||||
bool untrusted = 17;
|
||||
int32 remote_gui_port = 18 [(ext.goname) = "RemoteGUIPort", (ext.xml) = "remoteGUIPort", (ext.json) = "remoteGUIPort"];
|
||||
int32 num_connections = 19 [(ext.goname) = "RawNumConnections"]; // attempt to establish this many connections to the device
|
||||
}
|
||||
|
@ -8,9 +8,11 @@ import "repos/protobuf/gogoproto/gogo.proto";
|
||||
// --- Pre-auth ---
|
||||
|
||||
message Hello {
|
||||
string device_name = 1;
|
||||
string client_name = 2;
|
||||
string client_version = 3;
|
||||
string device_name = 1;
|
||||
string client_name = 2;
|
||||
string client_version = 3;
|
||||
int32 num_connections = 4;
|
||||
int64 timestamp = 5;
|
||||
}
|
||||
|
||||
// --- Header ---
|
||||
@ -41,7 +43,8 @@ enum MessageCompression {
|
||||
// Cluster Config
|
||||
|
||||
message ClusterConfig {
|
||||
repeated Folder folders = 1;
|
||||
repeated Folder folders = 1;
|
||||
bool secondary = 2;
|
||||
}
|
||||
|
||||
message Folder {
|
||||
|
@ -1,13 +1,17 @@
|
||||
<configuration version="32">
|
||||
<folder id="default" label="" path="s1/" type="sendreceive" rescanIntervalS="10" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
|
||||
<filesystemType>basic</filesystemType>
|
||||
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" introducedBy=""></device>
|
||||
<device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC" introducedBy=""></device>
|
||||
<device id="373HSRP-QLPNLIE-JYKZVQF-P4PKZ63-R2ZE6K3-YD442U2-JHBGBQG-WWXAHAU" introducedBy=""></device>
|
||||
<device id="7PBCTLL-JJRYBSA-MOWZRKL-MSDMN4N-4US4OMX-SYEXUS4-HSBGNRY-CZXRXAT" introducedBy=""></device>
|
||||
<configuration version="37">
|
||||
<folder id="default" label="" path="s1?files=10000" type="sendreceive" rescanIntervalS="3600" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
|
||||
<filesystemType>fake</filesystemType>
|
||||
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" introducedBy="">
|
||||
<encryptionPassword></encryptionPassword>
|
||||
</device>
|
||||
<device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC" introducedBy="">
|
||||
<encryptionPassword></encryptionPassword>
|
||||
</device>
|
||||
<minDiskFree unit="%">1</minDiskFree>
|
||||
<versioning>
|
||||
<cleanupIntervalS>3600</cleanupIntervalS>
|
||||
<fsPath></fsPath>
|
||||
<fsType>basic</fsType>
|
||||
</versioning>
|
||||
<copiers>1</copiers>
|
||||
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
|
||||
@ -24,51 +28,21 @@
|
||||
<markerName>.stfolder</markerName>
|
||||
<copyOwnershipFromParent>false</copyOwnershipFromParent>
|
||||
<modTimeWindowS>0</modTimeWindowS>
|
||||
<maxConcurrentWrites>0</maxConcurrentWrites>
|
||||
<maxConcurrentWrites>2</maxConcurrentWrites>
|
||||
<disableFsync>false</disableFsync>
|
||||
<blockPullOrder>standard</blockPullOrder>
|
||||
<copyRangeMethod>standard</copyRangeMethod>
|
||||
<caseSensitiveFS>false</caseSensitiveFS>
|
||||
<junctionsAsDirs>true</junctionsAsDirs>
|
||||
<syncOwnership>false</syncOwnership>
|
||||
<sendOwnership>false</sendOwnership>
|
||||
<syncXattrs>false</syncXattrs>
|
||||
<sendXattrs>false</sendXattrs>
|
||||
<xattrFilter>
|
||||
<maxSingleEntrySize>0</maxSingleEntrySize>
|
||||
<maxTotalSize>0</maxTotalSize>
|
||||
</xattrFilter>
|
||||
</folder>
|
||||
<folder id="¯\_(ツ)_/¯ Räksmörgås 动作 Адрес" label="" path="s12-1/" type="sendreceive" rescanIntervalS="10" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
|
||||
<filesystemType>basic</filesystemType>
|
||||
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" introducedBy=""></device>
|
||||
<device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC" introducedBy=""></device>
|
||||
<minDiskFree unit="%">1</minDiskFree>
|
||||
<versioning>
|
||||
<cleanupIntervalS>3600</cleanupIntervalS>
|
||||
</versioning>
|
||||
<copiers>1</copiers>
|
||||
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
|
||||
<hashers>0</hashers>
|
||||
<order>random</order>
|
||||
<ignoreDelete>false</ignoreDelete>
|
||||
<scanProgressIntervalS>0</scanProgressIntervalS>
|
||||
<pullerPauseS>0</pullerPauseS>
|
||||
<maxConflicts>-1</maxConflicts>
|
||||
<disableSparseFiles>false</disableSparseFiles>
|
||||
<disableTempIndexes>false</disableTempIndexes>
|
||||
<paused>false</paused>
|
||||
<weakHashThresholdPct>25</weakHashThresholdPct>
|
||||
<markerName>.stfolder</markerName>
|
||||
<copyOwnershipFromParent>false</copyOwnershipFromParent>
|
||||
<modTimeWindowS>0</modTimeWindowS>
|
||||
<maxConcurrentWrites>0</maxConcurrentWrites>
|
||||
<disableFsync>false</disableFsync>
|
||||
<blockPullOrder>standard</blockPullOrder>
|
||||
<copyRangeMethod>standard</copyRangeMethod>
|
||||
<caseSensitiveFS>false</caseSensitiveFS>
|
||||
<junctionsAsDirs>true</junctionsAsDirs>
|
||||
</folder>
|
||||
<device id="EJHMPAQ-OGCVORE-ISB4IS3-SYYVJXF-TKJGLTU-66DIQPF-GJ5D2GX-GQ3OWQK" name="s4" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
|
||||
<address>tcp://127.0.0.1:22004</address>
|
||||
<paused>false</paused>
|
||||
<autoAcceptFolders>false</autoAcceptFolders>
|
||||
<maxSendKbps>0</maxSendKbps>
|
||||
<maxRecvKbps>0</maxRecvKbps>
|
||||
<maxRequestKiB>0</maxRequestKiB>
|
||||
</device>
|
||||
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" name="s1" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
|
||||
<address>tcp://127.0.0.1:22001</address>
|
||||
<paused>false</paused>
|
||||
@ -76,30 +50,21 @@
|
||||
<maxSendKbps>0</maxSendKbps>
|
||||
<maxRecvKbps>0</maxRecvKbps>
|
||||
<maxRequestKiB>0</maxRequestKiB>
|
||||
<untrusted>false</untrusted>
|
||||
<remoteGUIPort>0</remoteGUIPort>
|
||||
<numConnections>3</numConnections>
|
||||
</device>
|
||||
<device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC" name="s2" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
|
||||
<address>tcp://127.0.0.1:22002</address>
|
||||
<address>quic://127.0.0.1:22002</address>
|
||||
<paused>false</paused>
|
||||
<autoAcceptFolders>false</autoAcceptFolders>
|
||||
<maxSendKbps>0</maxSendKbps>
|
||||
<maxRecvKbps>0</maxRecvKbps>
|
||||
<maxRequestKiB>0</maxRequestKiB>
|
||||
</device>
|
||||
<device id="373HSRP-QLPNLIE-JYKZVQF-P4PKZ63-R2ZE6K3-YD442U2-JHBGBQG-WWXAHAU" name="s3" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
|
||||
<address>tcp://127.0.0.1:22003</address>
|
||||
<paused>false</paused>
|
||||
<autoAcceptFolders>false</autoAcceptFolders>
|
||||
<maxSendKbps>0</maxSendKbps>
|
||||
<maxRecvKbps>0</maxRecvKbps>
|
||||
<maxRequestKiB>0</maxRequestKiB>
|
||||
</device>
|
||||
<device id="7PBCTLL-JJRYBSA-MOWZRKL-MSDMN4N-4US4OMX-SYEXUS4-HSBGNRY-CZXRXAT" name="s4" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
|
||||
<address>tcp://127.0.0.1:22004</address>
|
||||
<paused>false</paused>
|
||||
<autoAcceptFolders>false</autoAcceptFolders>
|
||||
<maxSendKbps>0</maxSendKbps>
|
||||
<maxRecvKbps>0</maxRecvKbps>
|
||||
<maxRequestKiB>0</maxRequestKiB>
|
||||
<untrusted>false</untrusted>
|
||||
<remoteGUIPort>0</remoteGUIPort>
|
||||
<numConnections>3</numConnections>
|
||||
</device>
|
||||
<gui enabled="true" tls="false" debugging="true">
|
||||
<address>127.0.0.1:8081</address>
|
||||
@ -111,6 +76,7 @@
|
||||
<ldap></ldap>
|
||||
<options>
|
||||
<listenAddress>tcp://127.0.0.1:22001</listenAddress>
|
||||
<listenAddress>quic://127.0.0.1:22001</listenAddress>
|
||||
<globalAnnounceServer>default</globalAnnounceServer>
|
||||
<globalAnnounceEnabled>false</globalAnnounceEnabled>
|
||||
<localAnnounceEnabled>true</localAnnounceEnabled>
|
||||
@ -132,7 +98,6 @@
|
||||
<urURL>https://data.syncthing.net/newdata</urURL>
|
||||
<urPostInsecurely>false</urPostInsecurely>
|
||||
<urInitialDelayS>1800</urInitialDelayS>
|
||||
<restartOnWakeup>true</restartOnWakeup>
|
||||
<autoUpgradeIntervalH>12</autoUpgradeIntervalH>
|
||||
<upgradeToPreReleases>false</upgradeToPreReleases>
|
||||
<keepTemporariesH>24</keepTemporariesH>
|
||||
@ -144,7 +109,6 @@
|
||||
<overwriteRemoteDeviceNamesOnConnect>false</overwriteRemoteDeviceNamesOnConnect>
|
||||
<tempIndexMinBlocks>10</tempIndexMinBlocks>
|
||||
<trafficClass>0</trafficClass>
|
||||
<defaultFolderPath>~</defaultFolderPath>
|
||||
<setLowPriority>true</setLowPriority>
|
||||
<maxFolderConcurrency>0</maxFolderConcurrency>
|
||||
<crashReportingURL>https://crash.syncthing.net/newcrash</crashReportingURL>
|
||||
@ -155,5 +119,70 @@
|
||||
<databaseTuning>auto</databaseTuning>
|
||||
<maxConcurrentIncomingRequestKiB>0</maxConcurrentIncomingRequestKiB>
|
||||
<announceLANAddresses>true</announceLANAddresses>
|
||||
<sendFullIndexOnUpgrade>false</sendFullIndexOnUpgrade>
|
||||
<connectionLimitEnough>0</connectionLimitEnough>
|
||||
<connectionLimitMax>0</connectionLimitMax>
|
||||
<insecureAllowOldTLSVersions>false</insecureAllowOldTLSVersions>
|
||||
<connectionPriorityTcpLan>10</connectionPriorityTcpLan>
|
||||
<connectionPriorityQuicLan>20</connectionPriorityQuicLan>
|
||||
<connectionPriorityTcpWan>30</connectionPriorityTcpWan>
|
||||
<connectionPriorityQuicWan>40</connectionPriorityQuicWan>
|
||||
<connectionPriorityRelay>50</connectionPriorityRelay>
|
||||
<connectionPriorityUpgradeThreshold>0</connectionPriorityUpgradeThreshold>
|
||||
</options>
|
||||
<defaults>
|
||||
<folder id="" label="" path="~" type="sendreceive" rescanIntervalS="3600" fsWatcherEnabled="true" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
|
||||
<filesystemType>basic</filesystemType>
|
||||
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" introducedBy="">
|
||||
<encryptionPassword></encryptionPassword>
|
||||
</device>
|
||||
<minDiskFree unit="%">1</minDiskFree>
|
||||
<versioning>
|
||||
<cleanupIntervalS>3600</cleanupIntervalS>
|
||||
<fsPath></fsPath>
|
||||
<fsType>basic</fsType>
|
||||
</versioning>
|
||||
<copiers>0</copiers>
|
||||
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
|
||||
<hashers>0</hashers>
|
||||
<order>random</order>
|
||||
<ignoreDelete>false</ignoreDelete>
|
||||
<scanProgressIntervalS>0</scanProgressIntervalS>
|
||||
<pullerPauseS>0</pullerPauseS>
|
||||
<maxConflicts>10</maxConflicts>
|
||||
<disableSparseFiles>false</disableSparseFiles>
|
||||
<disableTempIndexes>false</disableTempIndexes>
|
||||
<paused>false</paused>
|
||||
<weakHashThresholdPct>25</weakHashThresholdPct>
|
||||
<markerName>.stfolder</markerName>
|
||||
<copyOwnershipFromParent>false</copyOwnershipFromParent>
|
||||
<modTimeWindowS>0</modTimeWindowS>
|
||||
<maxConcurrentWrites>2</maxConcurrentWrites>
|
||||
<disableFsync>false</disableFsync>
|
||||
<blockPullOrder>standard</blockPullOrder>
|
||||
<copyRangeMethod>standard</copyRangeMethod>
|
||||
<caseSensitiveFS>false</caseSensitiveFS>
|
||||
<junctionsAsDirs>false</junctionsAsDirs>
|
||||
<syncOwnership>false</syncOwnership>
|
||||
<sendOwnership>false</sendOwnership>
|
||||
<syncXattrs>false</syncXattrs>
|
||||
<sendXattrs>false</sendXattrs>
|
||||
<xattrFilter>
|
||||
<maxSingleEntrySize>1024</maxSingleEntrySize>
|
||||
<maxTotalSize>4096</maxTotalSize>
|
||||
</xattrFilter>
|
||||
</folder>
|
||||
<device id="" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
|
||||
<address>dynamic</address>
|
||||
<paused>false</paused>
|
||||
<autoAcceptFolders>false</autoAcceptFolders>
|
||||
<maxSendKbps>0</maxSendKbps>
|
||||
<maxRecvKbps>0</maxRecvKbps>
|
||||
<maxRequestKiB>0</maxRequestKiB>
|
||||
<untrusted>false</untrusted>
|
||||
<remoteGUIPort>0</remoteGUIPort>
|
||||
<numConnections>3</numConnections>
|
||||
</device>
|
||||
<ignores></ignores>
|
||||
</defaults>
|
||||
</configuration>
|
||||
|
@ -1,14 +1,19 @@
|
||||
<configuration version="32">
|
||||
<folder id="default" label="" path="s2" type="sendreceive" rescanIntervalS="15" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
|
||||
<filesystemType>basic</filesystemType>
|
||||
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" introducedBy=""></device>
|
||||
<device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC" introducedBy=""></device>
|
||||
<device id="373HSRP-QLPNLIE-JYKZVQF-P4PKZ63-R2ZE6K3-YD442U2-JHBGBQG-WWXAHAU" introducedBy=""></device>
|
||||
<configuration version="37">
|
||||
<folder id="default" label="" path="s2" type="sendreceive" rescanIntervalS="3600" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
|
||||
<filesystemType>fake</filesystemType>
|
||||
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" introducedBy="">
|
||||
<encryptionPassword></encryptionPassword>
|
||||
</device>
|
||||
<device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC" introducedBy="">
|
||||
<encryptionPassword></encryptionPassword>
|
||||
</device>
|
||||
<minDiskFree unit="%">1</minDiskFree>
|
||||
<versioning>
|
||||
<cleanupIntervalS>3600</cleanupIntervalS>
|
||||
<fsPath></fsPath>
|
||||
<fsType>basic</fsType>
|
||||
</versioning>
|
||||
<copiers>1</copiers>
|
||||
<copiers>8</copiers>
|
||||
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
|
||||
<hashers>0</hashers>
|
||||
<order>random</order>
|
||||
@ -23,80 +28,32 @@
|
||||
<markerName>.stfolder</markerName>
|
||||
<copyOwnershipFromParent>false</copyOwnershipFromParent>
|
||||
<modTimeWindowS>0</modTimeWindowS>
|
||||
<maxConcurrentWrites>0</maxConcurrentWrites>
|
||||
<disableFsync>false</disableFsync>
|
||||
<blockPullOrder>standard</blockPullOrder>
|
||||
<copyRangeMethod>standard</copyRangeMethod>
|
||||
<caseSensitiveFS>false</caseSensitiveFS>
|
||||
<junctionsAsDirs>true</junctionsAsDirs>
|
||||
</folder>
|
||||
<folder id="s23" label="" path="s23-2" type="sendreceive" rescanIntervalS="15" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
|
||||
<filesystemType>basic</filesystemType>
|
||||
<device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC" introducedBy=""></device>
|
||||
<device id="373HSRP-QLPNLIE-JYKZVQF-P4PKZ63-R2ZE6K3-YD442U2-JHBGBQG-WWXAHAU" introducedBy=""></device>
|
||||
<minDiskFree unit="%">1</minDiskFree>
|
||||
<versioning>
|
||||
<cleanupIntervalS>3600</cleanupIntervalS>
|
||||
</versioning>
|
||||
<copiers>1</copiers>
|
||||
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
|
||||
<hashers>0</hashers>
|
||||
<order>random</order>
|
||||
<ignoreDelete>false</ignoreDelete>
|
||||
<scanProgressIntervalS>0</scanProgressIntervalS>
|
||||
<pullerPauseS>0</pullerPauseS>
|
||||
<maxConflicts>-1</maxConflicts>
|
||||
<disableSparseFiles>false</disableSparseFiles>
|
||||
<disableTempIndexes>false</disableTempIndexes>
|
||||
<paused>false</paused>
|
||||
<weakHashThresholdPct>25</weakHashThresholdPct>
|
||||
<markerName>.stfolder</markerName>
|
||||
<copyOwnershipFromParent>false</copyOwnershipFromParent>
|
||||
<modTimeWindowS>0</modTimeWindowS>
|
||||
<maxConcurrentWrites>0</maxConcurrentWrites>
|
||||
<disableFsync>false</disableFsync>
|
||||
<blockPullOrder>standard</blockPullOrder>
|
||||
<copyRangeMethod>standard</copyRangeMethod>
|
||||
<caseSensitiveFS>false</caseSensitiveFS>
|
||||
<junctionsAsDirs>true</junctionsAsDirs>
|
||||
</folder>
|
||||
<folder id="¯\_(ツ)_/¯ Räksmörgås 动作 Адрес" label="" path="s12-2" type="sendreceive" rescanIntervalS="15" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
|
||||
<filesystemType>basic</filesystemType>
|
||||
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" introducedBy=""></device>
|
||||
<device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC" introducedBy=""></device>
|
||||
<minDiskFree unit="%">1</minDiskFree>
|
||||
<versioning>
|
||||
<cleanupIntervalS>3600</cleanupIntervalS>
|
||||
</versioning>
|
||||
<copiers>1</copiers>
|
||||
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
|
||||
<hashers>0</hashers>
|
||||
<order>random</order>
|
||||
<ignoreDelete>false</ignoreDelete>
|
||||
<scanProgressIntervalS>0</scanProgressIntervalS>
|
||||
<pullerPauseS>0</pullerPauseS>
|
||||
<maxConflicts>-1</maxConflicts>
|
||||
<disableSparseFiles>false</disableSparseFiles>
|
||||
<disableTempIndexes>false</disableTempIndexes>
|
||||
<paused>false</paused>
|
||||
<weakHashThresholdPct>25</weakHashThresholdPct>
|
||||
<markerName>.stfolder</markerName>
|
||||
<copyOwnershipFromParent>false</copyOwnershipFromParent>
|
||||
<modTimeWindowS>0</modTimeWindowS>
|
||||
<maxConcurrentWrites>0</maxConcurrentWrites>
|
||||
<maxConcurrentWrites>8</maxConcurrentWrites>
|
||||
<disableFsync>false</disableFsync>
|
||||
<blockPullOrder>standard</blockPullOrder>
|
||||
<copyRangeMethod>standard</copyRangeMethod>
|
||||
<caseSensitiveFS>false</caseSensitiveFS>
|
||||
<junctionsAsDirs>true</junctionsAsDirs>
|
||||
<syncOwnership>false</syncOwnership>
|
||||
<sendOwnership>false</sendOwnership>
|
||||
<syncXattrs>false</syncXattrs>
|
||||
<sendXattrs>false</sendXattrs>
|
||||
<xattrFilter>
|
||||
<maxSingleEntrySize>0</maxSingleEntrySize>
|
||||
<maxTotalSize>0</maxTotalSize>
|
||||
</xattrFilter>
|
||||
</folder>
|
||||
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" name="s1" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
|
||||
<address>tcp://127.0.0.1:22001</address>
|
||||
<address>quic://127.0.0.1:22001</address>
|
||||
<paused>false</paused>
|
||||
<autoAcceptFolders>false</autoAcceptFolders>
|
||||
<maxSendKbps>0</maxSendKbps>
|
||||
<maxRecvKbps>0</maxRecvKbps>
|
||||
<maxSendKbps>800</maxSendKbps>
|
||||
<maxRecvKbps>800</maxRecvKbps>
|
||||
<maxRequestKiB>0</maxRequestKiB>
|
||||
<untrusted>false</untrusted>
|
||||
<remoteGUIPort>0</remoteGUIPort>
|
||||
<numConnections>3</numConnections>
|
||||
</device>
|
||||
<device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC" name="s2" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
|
||||
<address>tcp://127.0.0.1:22002</address>
|
||||
@ -105,14 +62,9 @@
|
||||
<maxSendKbps>0</maxSendKbps>
|
||||
<maxRecvKbps>0</maxRecvKbps>
|
||||
<maxRequestKiB>0</maxRequestKiB>
|
||||
</device>
|
||||
<device id="373HSRP-QLPNLIE-JYKZVQF-P4PKZ63-R2ZE6K3-YD442U2-JHBGBQG-WWXAHAU" name="s3" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
|
||||
<address>tcp://127.0.0.1:22003</address>
|
||||
<paused>false</paused>
|
||||
<autoAcceptFolders>false</autoAcceptFolders>
|
||||
<maxSendKbps>0</maxSendKbps>
|
||||
<maxRecvKbps>0</maxRecvKbps>
|
||||
<maxRequestKiB>0</maxRequestKiB>
|
||||
<untrusted>false</untrusted>
|
||||
<remoteGUIPort>0</remoteGUIPort>
|
||||
<numConnections>3</numConnections>
|
||||
</device>
|
||||
<gui enabled="true" tls="false" debugging="true">
|
||||
<address>127.0.0.1:8082</address>
|
||||
@ -121,15 +73,15 @@
|
||||
</gui>
|
||||
<ldap></ldap>
|
||||
<options>
|
||||
<listenAddress>dynamic+https://relays.syncthing.net/endpoint</listenAddress>
|
||||
<listenAddress>tcp://127.0.0.1:22002</listenAddress>
|
||||
<listenAddress>quic://127.0.0.1:22002</listenAddress>
|
||||
<globalAnnounceServer>default</globalAnnounceServer>
|
||||
<globalAnnounceEnabled>false</globalAnnounceEnabled>
|
||||
<localAnnounceEnabled>true</localAnnounceEnabled>
|
||||
<localAnnouncePort>21027</localAnnouncePort>
|
||||
<localAnnounceMCAddr>[ff12::8384]:21027</localAnnounceMCAddr>
|
||||
<maxSendKbps>0</maxSendKbps>
|
||||
<maxRecvKbps>0</maxRecvKbps>
|
||||
<maxSendKbps>1000</maxSendKbps>
|
||||
<maxRecvKbps>1000</maxRecvKbps>
|
||||
<reconnectionIntervalS>5</reconnectionIntervalS>
|
||||
<relaysEnabled>true</relaysEnabled>
|
||||
<relayReconnectIntervalM>10</relayReconnectIntervalM>
|
||||
@ -144,19 +96,17 @@
|
||||
<urURL>https://data.syncthing.net/newdata</urURL>
|
||||
<urPostInsecurely>false</urPostInsecurely>
|
||||
<urInitialDelayS>1800</urInitialDelayS>
|
||||
<restartOnWakeup>true</restartOnWakeup>
|
||||
<autoUpgradeIntervalH>12</autoUpgradeIntervalH>
|
||||
<upgradeToPreReleases>false</upgradeToPreReleases>
|
||||
<keepTemporariesH>24</keepTemporariesH>
|
||||
<cacheIgnoredFiles>false</cacheIgnoredFiles>
|
||||
<progressUpdateIntervalS>5</progressUpdateIntervalS>
|
||||
<limitBandwidthInLan>false</limitBandwidthInLan>
|
||||
<limitBandwidthInLan>true</limitBandwidthInLan>
|
||||
<minHomeDiskFree unit="%">1</minHomeDiskFree>
|
||||
<releasesURL>https://upgrades.syncthing.net/meta.json</releasesURL>
|
||||
<overwriteRemoteDeviceNamesOnConnect>false</overwriteRemoteDeviceNamesOnConnect>
|
||||
<tempIndexMinBlocks>10</tempIndexMinBlocks>
|
||||
<trafficClass>0</trafficClass>
|
||||
<defaultFolderPath>~</defaultFolderPath>
|
||||
<setLowPriority>true</setLowPriority>
|
||||
<maxFolderConcurrency>0</maxFolderConcurrency>
|
||||
<crashReportingURL>https://crash.syncthing.net/newcrash</crashReportingURL>
|
||||
@ -167,5 +117,70 @@
|
||||
<databaseTuning>auto</databaseTuning>
|
||||
<maxConcurrentIncomingRequestKiB>0</maxConcurrentIncomingRequestKiB>
|
||||
<announceLANAddresses>true</announceLANAddresses>
|
||||
<sendFullIndexOnUpgrade>false</sendFullIndexOnUpgrade>
|
||||
<connectionLimitEnough>0</connectionLimitEnough>
|
||||
<connectionLimitMax>0</connectionLimitMax>
|
||||
<insecureAllowOldTLSVersions>false</insecureAllowOldTLSVersions>
|
||||
<connectionPriorityTcpLan>10</connectionPriorityTcpLan>
|
||||
<connectionPriorityQuicLan>20</connectionPriorityQuicLan>
|
||||
<connectionPriorityTcpWan>30</connectionPriorityTcpWan>
|
||||
<connectionPriorityQuicWan>40</connectionPriorityQuicWan>
|
||||
<connectionPriorityRelay>50</connectionPriorityRelay>
|
||||
<connectionPriorityUpgradeThreshold>0</connectionPriorityUpgradeThreshold>
|
||||
</options>
|
||||
<defaults>
|
||||
<folder id="" label="" path="~" type="sendreceive" rescanIntervalS="3600" fsWatcherEnabled="true" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
|
||||
<filesystemType>basic</filesystemType>
|
||||
<device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC" introducedBy="">
|
||||
<encryptionPassword></encryptionPassword>
|
||||
</device>
|
||||
<minDiskFree unit="%">1</minDiskFree>
|
||||
<versioning>
|
||||
<cleanupIntervalS>3600</cleanupIntervalS>
|
||||
<fsPath></fsPath>
|
||||
<fsType>basic</fsType>
|
||||
</versioning>
|
||||
<copiers>0</copiers>
|
||||
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
|
||||
<hashers>0</hashers>
|
||||
<order>random</order>
|
||||
<ignoreDelete>false</ignoreDelete>
|
||||
<scanProgressIntervalS>0</scanProgressIntervalS>
|
||||
<pullerPauseS>0</pullerPauseS>
|
||||
<maxConflicts>10</maxConflicts>
|
||||
<disableSparseFiles>false</disableSparseFiles>
|
||||
<disableTempIndexes>false</disableTempIndexes>
|
||||
<paused>false</paused>
|
||||
<weakHashThresholdPct>25</weakHashThresholdPct>
|
||||
<markerName>.stfolder</markerName>
|
||||
<copyOwnershipFromParent>false</copyOwnershipFromParent>
|
||||
<modTimeWindowS>0</modTimeWindowS>
|
||||
<maxConcurrentWrites>2</maxConcurrentWrites>
|
||||
<disableFsync>false</disableFsync>
|
||||
<blockPullOrder>standard</blockPullOrder>
|
||||
<copyRangeMethod>standard</copyRangeMethod>
|
||||
<caseSensitiveFS>false</caseSensitiveFS>
|
||||
<junctionsAsDirs>false</junctionsAsDirs>
|
||||
<syncOwnership>false</syncOwnership>
|
||||
<sendOwnership>false</sendOwnership>
|
||||
<syncXattrs>false</syncXattrs>
|
||||
<sendXattrs>false</sendXattrs>
|
||||
<xattrFilter>
|
||||
<maxSingleEntrySize>1024</maxSingleEntrySize>
|
||||
<maxTotalSize>4096</maxTotalSize>
|
||||
</xattrFilter>
|
||||
</folder>
|
||||
<device id="" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
|
||||
<address>dynamic</address>
|
||||
<paused>false</paused>
|
||||
<autoAcceptFolders>false</autoAcceptFolders>
|
||||
<maxSendKbps>0</maxSendKbps>
|
||||
<maxRecvKbps>0</maxRecvKbps>
|
||||
<maxRequestKiB>0</maxRequestKiB>
|
||||
<untrusted>false</untrusted>
|
||||
<remoteGUIPort>0</remoteGUIPort>
|
||||
<numConnections>3</numConnections>
|
||||
</device>
|
||||
<ignores></ignores>
|
||||
</defaults>
|
||||
</configuration>
|
||||
|
Loading…
Reference in New Issue
Block a user