summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndré Colomb <src@andre.colomb.de>2022-11-28 09:28:33 +0100
committerGitHub <noreply@github.com>2022-11-28 09:28:33 +0100
commitab0eb909a2b3edafdc551737cc79f0088bd35571 (patch)
treecffbadb8c3df86a6e49ac700b5971ae04735fb31
parentd16c0652f7ea160b4aed0cdbcb9216573baf0107 (diff)
gui, lib/connections: Let the backend decide whether connection is local (fixes #8686) (#8694)
* lib/connections: Cache isLAN decision for later external access. The check whether a remote device's address is on a local network currently happens when handling the Hello message, to configure the limiters. Save the result to the ConnectionInfo and pass it out as part of the model's ConnectionInfo struct in ConnectionStats(). * gui: Use provided connection attribute to distinguish LAN / WAN. Replace the dumb IP address check which didn't catch common cases and actually could contradict what the backend decided. That could have been confusing if the GUI says WAN, but the limiter is not actually applied because the backend thinks it's a LAN. Add strings for QUIC and relay connections to also differentiate between LAN and WAN. * gui: Redefine reception level icons for all connection types. Move the mapping to the JS code, as it is much easier to handle multiple switch cases by fall-through there. QUIC is regarded no less than TCP anymore. LAN and WAN make the difference between levels 4 / 3 and 2 / 1: {TCP,QUIC} LAN --> {TCP,QUIC} WAN --> Relay LAN --> Relay WAN --> Disconnected.
-rw-r--r--gui/default/index.html10
-rwxr-xr-xgui/default/syncthing/core/syncthingController.js54
-rw-r--r--lib/connections/service.go6
-rw-r--r--lib/connections/structs.go5
-rw-r--r--lib/model/model.go2
-rw-r--r--lib/protocol/mocked_connection_info_test.go65
-rw-r--r--lib/protocol/mocks/connection.go65
-rw-r--r--lib/protocol/mocks/connection_info.go65
-rw-r--r--lib/protocol/protocol.go1
9 files changed, 248 insertions, 25 deletions
diff --git a/gui/default/index.html b/gui/default/index.html
index 81157d04d1..7f26b0adf4 100644
--- a/gui/default/index.html
+++ b/gui/default/index.html
@@ -751,12 +751,8 @@
<span ng-switch-when="disconnected-inactive"><span class="hidden-xs" translate>Disconnected (Inactive)</span><span class="visible-xs" aria-label="{{'Disconnected (Inactive)' | translate}}"><i class="fas fa-fw fa-power-off"></i></span></span>
<span ng-switch-when="unused-disconnected"><span class="hidden-xs" translate>Disconnected (Unused)</span><span class="visible-xs" aria-label="{{'Disconnected (Unused)' | translate}}"><i class="fas fa-fw fa-unlink"></i></span></span>
</span>
- <span ng-switch="rdConnType(deviceCfg.deviceID)" class="remote-devices-panel">
- <span ng-switch-when="tcplan" class="reception reception-4 reception-theme"></span>
- <span ng-switch-when="tcpwan" class="reception reception-3 reception-theme"></span>
- <span ng-switch-when="quic" class="reception reception-2 reception-theme"></span>
- <span ng-switch-when="relay" class="reception reception-1 reception-theme"></span>
- <span ng-switch-when="disconnected" class="reception reception-0 reception-theme"></span>
+ <span class="remote-devices-panel">
+ <span ng-class="rdConnTypeIcon(rdConnType(deviceCfg.deviceID))" class="reception reception-theme"></span>
</span>
</span>
<div class="panel-title-text">{{deviceName(deviceCfg)}}</div>
@@ -849,7 +845,7 @@
</tr>
<tr ng-if="connections[deviceCfg.deviceID].connected">
<th><span class="reception reception-4 reception-theme"></span>&nbsp;<span translate>Connection Type</span></th>
- <td ng-if="connections[deviceCfg.deviceID].connected" class="text-right">
+ <td class="text-right">
<span tooltip data-original-title="{{rdConnDetails(rdConnType(deviceCfg.deviceID))}}">
{{rdConnTypeString(rdConnType(deviceCfg.deviceID))}}
</span>
diff --git a/gui/default/syncthing/core/syncthingController.js b/gui/default/syncthing/core/syncthingController.js
index aa301d081e..f43bdea8e8 100755
--- a/gui/default/syncthing/core/syncthingController.js
+++ b/gui/default/syncthing/core/syncthingController.js
@@ -1203,24 +1203,27 @@ angular.module('syncthing.core')
$scope.rdConnType = function (deviceID) {
var conn = $scope.connections[deviceID];
if (!conn) return "-1";
- if (conn.type.indexOf('relay') === 0) return "relay";
- if (conn.type.indexOf('quic') === 0) return "quic";
- if (conn.type.indexOf('tcp') === 0) return "tcp" + rdAddrType(conn.address);
- return "disconnected";
- }
-
- function rdAddrType(address) {
- var re = /(^(?:127\.|0?10\.|172\.0?1[6-9]\.|172\.0?2[0-9]\.|172\.0?3[01]\.|192\.168\.|169\.254\.|::1|[fF][cCdD][0-9a-fA-F]{2}:|[fF][eE][89aAbB][0-9a-fA-F]:))/
- if (re.test(address)) return "lan";
- return "wan";
+ var type = "disconnected";
+ if (conn.type.indexOf('relay') === 0) type = "relay";
+ else if (conn.type.indexOf('quic') === 0) type = "quic";
+ else if (conn.type.indexOf('tcp') === 0) type = "tcp";
+ else return type;
+
+ if (conn.isLocal) type += "lan";
+ else type += "wan";
+ return type;
}
$scope.rdConnTypeString = function (type) {
switch (type) {
- case "relay":
- return $translate.instant('Relay');
- case "quic":
- return $translate.instant('QUIC');
+ case "relaywan":
+ return $translate.instant('Relay WAN');
+ case "relaylan":
+ return $translate.instant('Relay LAN');
+ case "quicwan":
+ return $translate.instant('QUIC WAN');
+ case "quiclan":
+ return $translate.instant('QUIC LAN');
case "tcpwan":
return $translate.instant('TCP WAN');
case "tcplan":
@@ -1230,11 +1233,30 @@ angular.module('syncthing.core')
}
}
+ $scope.rdConnTypeIcon = function (type) {
+ switch (type) {
+ case "tcplan":
+ case "quiclan":
+ return "reception-4";
+ case "tcpwan":
+ case "quicwan":
+ return "reception-3";
+ case "relaylan":
+ return "reception-2";
+ case "relaywan":
+ return "reception-1";
+ case "disconnected":
+ return "reception-0";
+ }
+ }
+
$scope.rdConnDetails = function (type) {
switch (type) {
- case "relay":
+ case "relaylan":
+ case "relaywan":
return $translate.instant('Connections via relays might be rate limited by the relay');
- case "quic":
+ case "quiclan":
+ case "quicwan":
return $translate.instant('QUIC connections are in most cases considered suboptimal');
case "tcpwan":
return $translate.instant('Using a direct TCP connection over WAN');
diff --git a/lib/connections/service.go b/lib/connections/service.go
index 127377d3b3..e504c8d202 100644
--- a/lib/connections/service.go
+++ b/lib/connections/service.go
@@ -403,11 +403,13 @@ func (s *service) handleHellos(ctx context.Context) error {
continue
}
+ // Determine only once whether a connection is considered local
+ // according to our configuration, then cache the decision.
+ c.isLocal = s.isLAN(c.RemoteAddr())
// Wrap the connection in rate limiters. The limiter itself will
// keep up with config changes to the rate and whether or not LAN
// connections are limited.
- isLAN := s.isLAN(c.RemoteAddr())
- rd, wr := s.limiter.getLimiters(remoteID, c, isLAN)
+ 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))
go func() {
diff --git a/lib/connections/structs.go b/lib/connections/structs.go
index 238ab7fb01..1a98fc7207 100644
--- a/lib/connections/structs.go
+++ b/lib/connections/structs.go
@@ -39,6 +39,7 @@ type tlsConn interface {
type internalConn struct {
tlsConn
connType connType
+ isLocal bool
priority int
establishedAt time.Time
}
@@ -107,6 +108,10 @@ func (c internalConn) Type() string {
return c.connType.String()
}
+func (c internalConn) IsLocal() bool {
+ return c.isLocal
+}
+
func (c internalConn) Priority() int {
return c.priority
}
diff --git a/lib/model/model.go b/lib/model/model.go
index 24b37ece83..33f5e61fd3 100644
--- a/lib/model/model.go
+++ b/lib/model/model.go
@@ -709,6 +709,7 @@ type ConnectionInfo struct {
Address string `json:"address"`
ClientVersion string `json:"clientVersion"`
Type string `json:"type"`
+ IsLocal bool `json:"isLocal"`
Crypto string `json:"crypto"`
}
@@ -739,6 +740,7 @@ func (m *model) ConnectionStats() map[string]interface{} {
}
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()
diff --git a/lib/protocol/mocked_connection_info_test.go b/lib/protocol/mocked_connection_info_test.go
index b336a7440b..aab43306ff 100644
--- a/lib/protocol/mocked_connection_info_test.go
+++ b/lib/protocol/mocked_connection_info_test.go
@@ -28,6 +28,16 @@ type mockedConnectionInfo struct {
establishedAtReturnsOnCall map[int]struct {
result1 time.Time
}
+ IsLocalStub func() bool
+ isLocalMutex sync.RWMutex
+ isLocalArgsForCall []struct {
+ }
+ isLocalReturns struct {
+ result1 bool
+ }
+ isLocalReturnsOnCall map[int]struct {
+ result1 bool
+ }
PriorityStub func() int
priorityMutex sync.RWMutex
priorityArgsForCall []struct {
@@ -188,6 +198,59 @@ func (fake *mockedConnectionInfo) EstablishedAtReturnsOnCall(i int, result1 time
}{result1}
}
+func (fake *mockedConnectionInfo) IsLocal() bool {
+ fake.isLocalMutex.Lock()
+ ret, specificReturn := fake.isLocalReturnsOnCall[len(fake.isLocalArgsForCall)]
+ fake.isLocalArgsForCall = append(fake.isLocalArgsForCall, struct {
+ }{})
+ stub := fake.IsLocalStub
+ fakeReturns := fake.isLocalReturns
+ fake.recordInvocation("IsLocal", []interface{}{})
+ fake.isLocalMutex.Unlock()
+ if stub != nil {
+ return stub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ return fakeReturns.result1
+}
+
+func (fake *mockedConnectionInfo) IsLocalCallCount() int {
+ fake.isLocalMutex.RLock()
+ defer fake.isLocalMutex.RUnlock()
+ return len(fake.isLocalArgsForCall)
+}
+
+func (fake *mockedConnectionInfo) IsLocalCalls(stub func() bool) {
+ fake.isLocalMutex.Lock()
+ defer fake.isLocalMutex.Unlock()
+ fake.IsLocalStub = stub
+}
+
+func (fake *mockedConnectionInfo) IsLocalReturns(result1 bool) {
+ fake.isLocalMutex.Lock()
+ defer fake.isLocalMutex.Unlock()
+ fake.IsLocalStub = nil
+ fake.isLocalReturns = struct {
+ result1 bool
+ }{result1}
+}
+
+func (fake *mockedConnectionInfo) IsLocalReturnsOnCall(i int, result1 bool) {
+ fake.isLocalMutex.Lock()
+ defer fake.isLocalMutex.Unlock()
+ fake.IsLocalStub = nil
+ if fake.isLocalReturnsOnCall == nil {
+ fake.isLocalReturnsOnCall = make(map[int]struct {
+ result1 bool
+ })
+ }
+ fake.isLocalReturnsOnCall[i] = struct {
+ result1 bool
+ }{result1}
+}
+
func (fake *mockedConnectionInfo) Priority() int {
fake.priorityMutex.Lock()
ret, specificReturn := fake.priorityReturnsOnCall[len(fake.priorityArgsForCall)]
@@ -460,6 +523,8 @@ func (fake *mockedConnectionInfo) Invocations() map[string][][]interface{} {
defer fake.cryptoMutex.RUnlock()
fake.establishedAtMutex.RLock()
defer fake.establishedAtMutex.RUnlock()
+ fake.isLocalMutex.RLock()
+ defer fake.isLocalMutex.RUnlock()
fake.priorityMutex.RLock()
defer fake.priorityMutex.RUnlock()
fake.remoteAddrMutex.RLock()
diff --git a/lib/protocol/mocks/connection.go b/lib/protocol/mocks/connection.go
index 3aab4b8b98..3fddab239a 100644
--- a/lib/protocol/mocks/connection.go
+++ b/lib/protocol/mocks/connection.go
@@ -94,6 +94,16 @@ type Connection struct {
indexUpdateReturnsOnCall map[int]struct {
result1 error
}
+ IsLocalStub func() bool
+ isLocalMutex sync.RWMutex
+ isLocalArgsForCall []struct {
+ }
+ isLocalReturns struct {
+ result1 bool
+ }
+ isLocalReturnsOnCall map[int]struct {
+ result1 bool
+ }
PriorityStub func() int
priorityMutex sync.RWMutex
priorityArgsForCall []struct {
@@ -639,6 +649,59 @@ func (fake *Connection) IndexUpdateReturnsOnCall(i int, result1 error) {
}{result1}
}
+func (fake *Connection) IsLocal() bool {
+ fake.isLocalMutex.Lock()
+ ret, specificReturn := fake.isLocalReturnsOnCall[len(fake.isLocalArgsForCall)]
+ fake.isLocalArgsForCall = append(fake.isLocalArgsForCall, struct {
+ }{})
+ stub := fake.IsLocalStub
+ fakeReturns := fake.isLocalReturns
+ fake.recordInvocation("IsLocal", []interface{}{})
+ fake.isLocalMutex.Unlock()
+ if stub != nil {
+ return stub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ return fakeReturns.result1
+}
+
+func (fake *Connection) IsLocalCallCount() int {
+ fake.isLocalMutex.RLock()
+ defer fake.isLocalMutex.RUnlock()
+ return len(fake.isLocalArgsForCall)
+}
+
+func (fake *Connection) IsLocalCalls(stub func() bool) {
+ fake.isLocalMutex.Lock()
+ defer fake.isLocalMutex.Unlock()
+ fake.IsLocalStub = stub
+}
+
+func (fake *Connection) IsLocalReturns(result1 bool) {
+ fake.isLocalMutex.Lock()
+ defer fake.isLocalMutex.Unlock()
+ fake.IsLocalStub = nil
+ fake.isLocalReturns = struct {
+ result1 bool
+ }{result1}
+}
+
+func (fake *Connection) IsLocalReturnsOnCall(i int, result1 bool) {
+ fake.isLocalMutex.Lock()
+ defer fake.isLocalMutex.Unlock()
+ fake.IsLocalStub = nil
+ if fake.isLocalReturnsOnCall == nil {
+ fake.isLocalReturnsOnCall = make(map[int]struct {
+ result1 bool
+ })
+ }
+ fake.isLocalReturnsOnCall[i] = struct {
+ result1 bool
+ }{result1}
+}
+
func (fake *Connection) Priority() int {
fake.priorityMutex.Lock()
ret, specificReturn := fake.priorityReturnsOnCall[len(fake.priorityArgsForCall)]
@@ -1111,6 +1174,8 @@ func (fake *Connection) Invocations() map[string][][]interface{} {
defer fake.indexMutex.RUnlock()
fake.indexUpdateMutex.RLock()
defer fake.indexUpdateMutex.RUnlock()
+ fake.isLocalMutex.RLock()
+ defer fake.isLocalMutex.RUnlock()
fake.priorityMutex.RLock()
defer fake.priorityMutex.RUnlock()
fake.remoteAddrMutex.RLock()
diff --git a/lib/protocol/mocks/connection_info.go b/lib/protocol/mocks/connection_info.go
index 8612b63832..9e9dd47282 100644
--- a/lib/protocol/mocks/connection_info.go
+++ b/lib/protocol/mocks/connection_info.go
@@ -30,6 +30,16 @@ type ConnectionInfo struct {
establishedAtReturnsOnCall map[int]struct {
result1 time.Time
}
+ IsLocalStub func() bool
+ isLocalMutex sync.RWMutex
+ isLocalArgsForCall []struct {
+ }
+ isLocalReturns struct {
+ result1 bool
+ }
+ isLocalReturnsOnCall map[int]struct {
+ result1 bool
+ }
PriorityStub func() int
priorityMutex sync.RWMutex
priorityArgsForCall []struct {
@@ -190,6 +200,59 @@ func (fake *ConnectionInfo) EstablishedAtReturnsOnCall(i int, result1 time.Time)
}{result1}
}
+func (fake *ConnectionInfo) IsLocal() bool {
+ fake.isLocalMutex.Lock()
+ ret, specificReturn := fake.isLocalReturnsOnCall[len(fake.isLocalArgsForCall)]
+ fake.isLocalArgsForCall = append(fake.isLocalArgsForCall, struct {
+ }{})
+ stub := fake.IsLocalStub
+ fakeReturns := fake.isLocalReturns
+ fake.recordInvocation("IsLocal", []interface{}{})
+ fake.isLocalMutex.Unlock()
+ if stub != nil {
+ return stub()
+ }
+ if specificReturn {
+ return ret.result1
+ }
+ return fakeReturns.result1
+}
+
+func (fake *ConnectionInfo) IsLocalCallCount() int {
+ fake.isLocalMutex.RLock()
+ defer fake.isLocalMutex.RUnlock()
+ return len(fake.isLocalArgsForCall)
+}
+
+func (fake *ConnectionInfo) IsLocalCalls(stub func() bool) {
+ fake.isLocalMutex.Lock()
+ defer fake.isLocalMutex.Unlock()
+ fake.IsLocalStub = stub
+}
+
+func (fake *ConnectionInfo) IsLocalReturns(result1 bool) {
+ fake.isLocalMutex.Lock()
+ defer fake.isLocalMutex.Unlock()
+ fake.IsLocalStub = nil
+ fake.isLocalReturns = struct {
+ result1 bool
+ }{result1}
+}
+
+func (fake *ConnectionInfo) IsLocalReturnsOnCall(i int, result1 bool) {
+ fake.isLocalMutex.Lock()
+ defer fake.isLocalMutex.Unlock()
+ fake.IsLocalStub = nil
+ if fake.isLocalReturnsOnCall == nil {
+ fake.isLocalReturnsOnCall = make(map[int]struct {
+ result1 bool
+ })
+ }
+ fake.isLocalReturnsOnCall[i] = struct {
+ result1 bool
+ }{result1}
+}
+
func (fake *ConnectionInfo) Priority() int {
fake.priorityMutex.Lock()
ret, specificReturn := fake.priorityReturnsOnCall[len(fake.priorityArgsForCall)]
@@ -462,6 +525,8 @@ func (fake *ConnectionInfo) Invocations() map[string][][]interface{} {
defer fake.cryptoMutex.RUnlock()
fake.establishedAtMutex.RLock()
defer fake.establishedAtMutex.RUnlock()
+ fake.isLocalMutex.RLock()
+ defer fake.isLocalMutex.RUnlock()
fake.priorityMutex.RLock()
defer fake.priorityMutex.RUnlock()
fake.remoteAddrMutex.RLock()
diff --git a/lib/protocol/protocol.go b/lib/protocol/protocol.go
index 2fba29c6b8..48dfde2d30 100644
--- a/lib/protocol/protocol.go
+++ b/lib/protocol/protocol.go
@@ -162,6 +162,7 @@ type Connection interface {
type ConnectionInfo interface {
Type() string
Transport() string
+ IsLocal() bool
RemoteAddr() net.Addr
Priority() int
String() string