| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283 |
- // 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 {
- candidates[i] = candidates[len(candidates)-1]
- candidates[len(candidates)-1] = nil
- r.available[scheme] = candidates[:len(candidates)-1]
- break
- }
- }
- }
- // 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{} {
- r.mut.Lock()
- defer r.mut.Unlock()
- var (
- best interface{}
- bestPref bool
- bestScheme string
- )
- for availableScheme, items := range r.available {
- // quic:// should be considered ok for both quic4:// and quic6://
- 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
- }
- }
- return best
- }
|