mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-09 23:00:58 +00:00
lib/connections: Parallel dials in the same priority (fixes #4456)
Well Tested(TM) Introduces a potential issue where we always pick some connectable but dodgy connection that breaks soon after the TLS handshake. GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4489
This commit is contained in:
parent
783dd612f7
commit
aecd7c64ce
@ -12,6 +12,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -318,7 +319,6 @@ func (s *Service) connect() {
|
|||||||
now := time.Now()
|
now := time.Now()
|
||||||
var seen []string
|
var seen []string
|
||||||
|
|
||||||
nextDevice:
|
|
||||||
for _, deviceCfg := range cfg.Devices {
|
for _, deviceCfg := range cfg.Devices {
|
||||||
deviceID := deviceCfg.DeviceID
|
deviceID := deviceCfg.DeviceID
|
||||||
if deviceID == s.myID {
|
if deviceID == s.myID {
|
||||||
@ -357,6 +357,8 @@ func (s *Service) connect() {
|
|||||||
|
|
||||||
seen = append(seen, addrs...)
|
seen = append(seen, addrs...)
|
||||||
|
|
||||||
|
dialTargets := make([]dialTarget, 0)
|
||||||
|
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
nextDialAt, ok := nextDial[addr]
|
nextDialAt, ok := nextDial[addr]
|
||||||
if ok && initialRampup >= sleep && nextDialAt.After(now) {
|
if ok && initialRampup >= sleep && nextDialAt.After(now) {
|
||||||
@ -390,23 +392,27 @@ func (s *Service) connect() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if priorityKnown && dialerFactory.Priority() >= ct.internalConn.priority {
|
prio := dialerFactory.Priority()
|
||||||
|
|
||||||
|
if priorityKnown && prio >= ct.internalConn.priority {
|
||||||
l.Debugf("Not dialing using %s as priority is less than current connection (%d >= %d)", dialerFactory, dialerFactory.Priority(), ct.internalConn.priority)
|
l.Debugf("Not dialing using %s as priority is less than current connection (%d >= %d)", dialerFactory, dialerFactory.Priority(), ct.internalConn.priority)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
dialer := dialerFactory.New(s.cfg, s.tlsCfg)
|
dialer := dialerFactory.New(s.cfg, s.tlsCfg)
|
||||||
l.Debugln("dial", deviceCfg.DeviceID, uri)
|
|
||||||
nextDial[addr] = now.Add(dialer.RedialFrequency())
|
nextDial[addr] = now.Add(dialer.RedialFrequency())
|
||||||
|
|
||||||
conn, err := dialer.Dial(deviceID, uri)
|
dialTargets = append(dialTargets, dialTarget{
|
||||||
if err != nil {
|
dialer: dialer,
|
||||||
l.Debugf("%v for %v at %v: %v", dialerFactory, deviceCfg.DeviceID, uri, err)
|
priority: prio,
|
||||||
continue
|
deviceID: deviceID,
|
||||||
}
|
uri: uri,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, ok := dialParallel(deviceCfg.DeviceID, dialTargets)
|
||||||
|
if ok {
|
||||||
s.conns <- conn
|
s.conns <- conn
|
||||||
continue nextDevice
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -710,3 +716,65 @@ func IsAllowedNetwork(host string, allowed []string) bool {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dialParallel(deviceID protocol.DeviceID, dialTargets []dialTarget) (internalConn, bool) {
|
||||||
|
// Group targets into buckets by priority
|
||||||
|
dialTargetBuckets := make(map[int][]dialTarget, len(dialTargets))
|
||||||
|
for _, tgt := range dialTargets {
|
||||||
|
dialTargetBuckets[tgt.priority] = append(dialTargetBuckets[tgt.priority], tgt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all available priorities
|
||||||
|
priorities := make([]int, 0, len(dialTargetBuckets))
|
||||||
|
for prio := range dialTargetBuckets {
|
||||||
|
priorities = append(priorities, prio)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the priorities so that we dial lowest first (which means highest...)
|
||||||
|
sort.Ints(priorities)
|
||||||
|
|
||||||
|
for _, prio := range priorities {
|
||||||
|
tgts := dialTargetBuckets[prio]
|
||||||
|
res := make(chan internalConn, len(tgts))
|
||||||
|
wg := sync.NewWaitGroup()
|
||||||
|
for _, tgt := range tgts {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
conn, err := tgt.Dial()
|
||||||
|
if err == nil {
|
||||||
|
res <- conn
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spawn a routine which will unblock main routine in case we fail
|
||||||
|
// to connect to anyone.
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(res)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait for the first connection, or for channel closure.
|
||||||
|
conn, ok := <-res
|
||||||
|
|
||||||
|
// Got a connection, means more might come back, hence spawn a
|
||||||
|
// routine that will do the discarding.
|
||||||
|
if ok {
|
||||||
|
l.Debugln("connected to", deviceID, prio, "using", conn, conn.priority)
|
||||||
|
go func(deviceID protocol.DeviceID, prio int) {
|
||||||
|
wg.Wait()
|
||||||
|
l.Debugln("discarding", len(res), "connections while connecting to", deviceID, prio)
|
||||||
|
for conn := range res {
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
}(deviceID, prio)
|
||||||
|
} else {
|
||||||
|
// Failed to connect, report that fact.
|
||||||
|
l.Debugln("failed to connect to", deviceID, prio)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, ok
|
||||||
|
}
|
||||||
|
return internalConn{}, false
|
||||||
|
}
|
||||||
|
@ -177,3 +177,17 @@ func (o *onAddressesChangedNotifier) notifyAddressesChanged(l genericListener) {
|
|||||||
callback(l)
|
callback(l)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type dialTarget struct {
|
||||||
|
dialer genericDialer
|
||||||
|
priority int
|
||||||
|
uri *url.URL
|
||||||
|
deviceID protocol.DeviceID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t dialTarget) Dial() (internalConn, error) {
|
||||||
|
l.Debugln("dialing", t.deviceID, t.uri, "prio", t.priority)
|
||||||
|
conn, err := t.dialer.Dial(t.deviceID, t.uri)
|
||||||
|
l.Debugln("dialing", t.deviceID, t.uri, "outcome", conn, err)
|
||||||
|
return conn, err
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user