2019-05-29 07:56:40 +00:00
|
|
|
// Copyright (C) 2019 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/.
|
|
|
|
|
|
|
|
// Registry tracks connections/addresses on which we are listening on, to allow us to pick a connection/address that
|
|
|
|
// has a NAT port mapping. This also makes our outgoing port stable and same as incoming port which should allow
|
|
|
|
// better probability of punching through.
|
|
|
|
package registry
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/syncthing/syncthing/lib/sync"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Registry struct {
|
|
|
|
mut sync.Mutex
|
|
|
|
available map[string][]interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func New() *Registry {
|
|
|
|
return &Registry{
|
|
|
|
mut: sync.NewMutex(),
|
|
|
|
available: make(map[string][]interface{}),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Registry) Register(scheme string, item interface{}) {
|
|
|
|
r.mut.Lock()
|
|
|
|
defer r.mut.Unlock()
|
|
|
|
|
|
|
|
r.available[scheme] = append(r.available[scheme], item)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Registry) Unregister(scheme string, item interface{}) {
|
|
|
|
r.mut.Lock()
|
|
|
|
defer r.mut.Unlock()
|
|
|
|
|
|
|
|
candidates := r.available[scheme]
|
|
|
|
for i, existingItem := range candidates {
|
|
|
|
if existingItem == item {
|
2021-10-06 08:52:51 +00:00
|
|
|
candidates[i] = candidates[len(candidates)-1]
|
2019-05-29 07:56:40 +00:00
|
|
|
candidates[len(candidates)-1] = nil
|
|
|
|
r.available[scheme] = candidates[:len(candidates)-1]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-06 08:52:51 +00:00
|
|
|
// Get returns an item for a schema compatible with the given scheme.
|
|
|
|
// If any item satisfies preferred, that has precedence over other items.
|
|
|
|
func (r *Registry) Get(scheme string, preferred func(interface{}) bool) interface{} {
|
2019-05-29 07:56:40 +00:00
|
|
|
r.mut.Lock()
|
|
|
|
defer r.mut.Unlock()
|
|
|
|
|
2021-10-06 08:52:51 +00:00
|
|
|
var (
|
|
|
|
best interface{}
|
|
|
|
bestPref bool
|
|
|
|
bestScheme string
|
|
|
|
)
|
2019-05-29 07:56:40 +00:00
|
|
|
for availableScheme, items := range r.available {
|
|
|
|
// quic:// should be considered ok for both quic4:// and quic6://
|
2021-10-06 08:52:51 +00:00
|
|
|
if !strings.HasPrefix(scheme, availableScheme) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for _, item := range items {
|
|
|
|
better := best == nil
|
|
|
|
pref := preferred(item)
|
|
|
|
if !better {
|
|
|
|
// In case of a tie, prefer "quic" to "quic[46]" etc.
|
|
|
|
better = pref &&
|
|
|
|
(!bestPref || len(availableScheme) < len(bestScheme))
|
|
|
|
}
|
|
|
|
if !better {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
best, bestPref, bestScheme = item, pref, availableScheme
|
2019-05-29 07:56:40 +00:00
|
|
|
}
|
|
|
|
}
|
2021-10-06 08:52:51 +00:00
|
|
|
return best
|
2019-05-29 07:56:40 +00:00
|
|
|
}
|