lib/connections: Un-deprecate relaysEnabled (fixes #3074)

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3098
This commit is contained in:
Jakob Borg 2016-05-17 00:05:38 +00:00
parent adb7fb43cb
commit 2c1323ece6
13 changed files with 371 additions and 128 deletions

View File

@ -56,11 +56,24 @@
</div> </div>
</div> </div>
<div class="form-group"> <div class="row">
<div class="checkbox"> <div class="col-md-6">
<label> <div class="form-group">
<input id="GlobalAnnEnabled" type="checkbox" ng-model="tmpOptions.globalAnnounceEnabled"> <span translate>Global Discovery</span> <div class="checkbox">
</label> <label>
<input id="GlobalAnnEnabled" type="checkbox" ng-model="tmpOptions.globalAnnounceEnabled"> <span translate>Global Discovery</span>
</label>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<div class="checkbox">
<label>
<input id="RelaysEnabled" type="checkbox" ng-model="tmpOptions.relaysEnabled"> <span translate>Enable Relaying</span>
</label>
</div>
</div>
</div> </div>
</div> </div>

View File

@ -29,9 +29,9 @@ const (
var ( var (
// DefaultListenAddresses should be substituted when the configuration // DefaultListenAddresses should be substituted when the configuration
// contains <listenAddress>default</listenAddress>. This is // contains <listenAddress>default</listenAddress>. This is done by the
// done by the "consumer" of the configuration, as we don't want these // "consumer" of the configuration as we don't want these saved to the
// saved to the config. // config.
DefaultListenAddresses = []string{ DefaultListenAddresses = []string{
"tcp://0.0.0.0:22000", "tcp://0.0.0.0:22000",
"dynamic+https://relays.syncthing.net/endpoint", "dynamic+https://relays.syncthing.net/endpoint",
@ -258,27 +258,53 @@ func convertV13V14(cfg *Configuration) {
// Not using the ignore cache is the new default. Disable it on existing // Not using the ignore cache is the new default. Disable it on existing
// configurations. // configurations.
cfg.Options.CacheIgnoredFiles = false cfg.Options.CacheIgnoredFiles = false
cfg.Options.NATEnabled = cfg.Options.DeprecatedUPnPEnabled
cfg.Options.NATLeaseM = cfg.Options.DeprecatedUPnPLeaseM
cfg.Options.NATRenewalM = cfg.Options.DeprecatedUPnPRenewalM
cfg.Options.NATTimeoutS = cfg.Options.DeprecatedUPnPTimeoutS
if cfg.Options.DeprecatedRelaysEnabled {
cfg.Options.ListenAddresses = append(cfg.Options.ListenAddresses, cfg.Options.DeprecatedRelayServers...)
// Replace our two fairly long addresses with 'default' if both exist.
var newAddresses []string
for _, addr := range cfg.Options.ListenAddresses {
if addr != "tcp://0.0.0.0:22000" && addr != "dynamic+https://relays.syncthing.net/endpoint" {
newAddresses = append(newAddresses, addr)
}
}
if len(newAddresses)+2 == len(cfg.Options.ListenAddresses) { // Migrate UPnP -> NAT options
cfg.Options.ListenAddresses = append([]string{"default"}, newAddresses...) cfg.Options.NATEnabled = cfg.Options.DeprecatedUPnPEnabled
cfg.Options.DeprecatedUPnPEnabled = false
cfg.Options.NATLeaseM = cfg.Options.DeprecatedUPnPLeaseM
cfg.Options.DeprecatedUPnPLeaseM = 0
cfg.Options.NATRenewalM = cfg.Options.DeprecatedUPnPRenewalM
cfg.Options.DeprecatedUPnPRenewalM = 0
cfg.Options.NATTimeoutS = cfg.Options.DeprecatedUPnPTimeoutS
cfg.Options.DeprecatedUPnPTimeoutS = 0
// Replace the default listen address "tcp://0.0.0.0:22000" with the
// string "default", but only if we also have the default relay pool
// among the relay servers as this is implied by the new "default"
// entry.
hasDefault := false
for _, raddr := range cfg.Options.DeprecatedRelayServers {
if raddr == "dynamic+https://relays.syncthing.net/endpoint" {
for i, addr := range cfg.Options.ListenAddresses {
if addr == "tcp://0.0.0.0:22000" {
cfg.Options.ListenAddresses[i] = "default"
hasDefault = true
break
}
}
break
} }
} }
cfg.Options.DeprecatedRelaysEnabled = false
// Copy relay addresses into listen addresses.
for _, addr := range cfg.Options.DeprecatedRelayServers {
if hasDefault && addr == "dynamic+https://relays.syncthing.net/endpoint" {
// Skip the default relay address if we already have the
// "default" entry in the list.
continue
}
if addr == "" {
continue
}
cfg.Options.ListenAddresses = append(cfg.Options.ListenAddresses, addr)
}
cfg.Options.DeprecatedRelayServers = nil cfg.Options.DeprecatedRelayServers = nil
// For consistency
sort.Strings(cfg.Options.ListenAddresses)
var newAddrs []string var newAddrs []string
for _, addr := range cfg.Options.GlobalAnnServers { for _, addr := range cfg.Options.GlobalAnnServers {
if addr != "default" { if addr != "default" {

View File

@ -12,7 +12,9 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"runtime" "runtime"
"sort"
"strings" "strings"
"testing" "testing"
@ -40,6 +42,7 @@ func TestDefaultValues(t *testing.T) {
MaxSendKbps: 0, MaxSendKbps: 0,
MaxRecvKbps: 0, MaxRecvKbps: 0,
ReconnectIntervalS: 60, ReconnectIntervalS: 60,
RelaysEnabled: true,
RelayReconnectIntervalM: 10, RelayReconnectIntervalM: 10,
StartBrowser: true, StartBrowser: true,
NATEnabled: true, NATEnabled: true,
@ -169,6 +172,7 @@ func TestOverriddenValues(t *testing.T) {
MaxSendKbps: 1234, MaxSendKbps: 1234,
MaxRecvKbps: 2341, MaxRecvKbps: 2341,
ReconnectIntervalS: 6000, ReconnectIntervalS: 6000,
RelaysEnabled: false,
RelayReconnectIntervalM: 20, RelayReconnectIntervalM: 20,
StartBrowser: false, StartBrowser: false,
NATEnabled: false, NATEnabled: false,
@ -616,3 +620,61 @@ func TestRemoveDuplicateDevicesFolders(t *testing.T) {
t.Errorf("Incorrect number of folder devices, %d != 2", l) t.Errorf("Incorrect number of folder devices, %d != 2", l)
} }
} }
func TestV14ListenAddressesMigration(t *testing.T) {
tcs := [][3][]string{
// Default listen plus default relays is now "default"
{
{"tcp://0.0.0.0:22000"},
{"dynamic+https://relays.syncthing.net/endpoint"},
{"default"},
},
// Default listen address without any relay addresses gets converted
// to just the listen address. It's easier this way, and frankly the
// user has gone to some trouble to get the empty string in the
// config to start with...
{
{"tcp://0.0.0.0:22000"}, // old listen addrs
{""}, // old relay addrs
{"tcp://0.0.0.0:22000"}, // new listen addrs
},
// Default listen plus non-default relays gets copied verbatim
{
{"tcp://0.0.0.0:22000"},
{"dynamic+https://other.example.com"},
{"tcp://0.0.0.0:22000", "dynamic+https://other.example.com"},
},
// Non-default listen plus default relays gets copied verbatim
{
{"tcp://1.2.3.4:22000"},
{"dynamic+https://relays.syncthing.net/endpoint"},
{"tcp://1.2.3.4:22000", "dynamic+https://relays.syncthing.net/endpoint"},
},
// Default stuff gets sucked into "default", the rest gets copied
{
{"tcp://0.0.0.0:22000", "tcp://1.2.3.4:22000"},
{"dynamic+https://relays.syncthing.net/endpoint", "relay://other.example.com"},
{"default", "tcp://1.2.3.4:22000", "relay://other.example.com"},
},
}
for _, tc := range tcs {
cfg := Configuration{
Version: 13,
Options: OptionsConfiguration{
ListenAddresses: tc[0],
DeprecatedRelayServers: tc[1],
},
}
convertV13V14(&cfg)
if cfg.Version != 14 {
t.Error("Configuration was not converted")
}
sort.Strings(tc[2])
if !reflect.DeepEqual(cfg.Options.ListenAddresses, tc[2]) {
t.Errorf("Migration error; actual %#v != expected %#v", cfg.Options.ListenAddresses, tc[2])
}
}
}

View File

@ -16,6 +16,7 @@ type OptionsConfiguration struct {
MaxSendKbps int `xml:"maxSendKbps" json:"maxSendKbps"` MaxSendKbps int `xml:"maxSendKbps" json:"maxSendKbps"`
MaxRecvKbps int `xml:"maxRecvKbps" json:"maxRecvKbps"` MaxRecvKbps int `xml:"maxRecvKbps" json:"maxRecvKbps"`
ReconnectIntervalS int `xml:"reconnectionIntervalS" json:"reconnectionIntervalS" default:"60"` ReconnectIntervalS int `xml:"reconnectionIntervalS" json:"reconnectionIntervalS" default:"60"`
RelaysEnabled bool `xml:"relaysEnabled" json:"relaysEnabled" default:"true"`
RelayReconnectIntervalM int `xml:"relayReconnectIntervalM" json:"relayReconnectIntervalM" default:"10"` RelayReconnectIntervalM int `xml:"relayReconnectIntervalM" json:"relayReconnectIntervalM" default:"10"`
StartBrowser bool `xml:"startBrowser" json:"startBrowser" default:"true"` StartBrowser bool `xml:"startBrowser" json:"startBrowser" default:"true"`
NATEnabled bool `xml:"natEnabled" json:"natEnabled" default:"true"` NATEnabled bool `xml:"natEnabled" json:"natEnabled" default:"true"`
@ -40,12 +41,11 @@ type OptionsConfiguration struct {
OverwriteRemoteDevNames bool `xml:"overwriteRemoteDeviceNamesOnConnect" json:"overwriteRemoteDeviceNamesOnConnect" default:"false"` OverwriteRemoteDevNames bool `xml:"overwriteRemoteDeviceNamesOnConnect" json:"overwriteRemoteDeviceNamesOnConnect" default:"false"`
TempIndexMinBlocks int `xml:"tempIndexMinBlocks" json:"tempIndexMinBlocks" default:"10"` TempIndexMinBlocks int `xml:"tempIndexMinBlocks" json:"tempIndexMinBlocks" default:"10"`
DeprecatedUPnPEnabled bool `xml:"upnpEnabled" json:"-"` DeprecatedUPnPEnabled bool `xml:"upnpEnabled,omitempty" json:"-"`
DeprecatedUPnPLeaseM int `xml:"upnpLeaseMinutes" json:"-"` DeprecatedUPnPLeaseM int `xml:"upnpLeaseMinutes,omitempty" json:"-"`
DeprecatedUPnPRenewalM int `xml:"upnpRenewalMinutes" json:"-"` DeprecatedUPnPRenewalM int `xml:"upnpRenewalMinutes,omitempty" json:"-"`
DeprecatedUPnPTimeoutS int `xml:"upnpTimeoutSeconds" json:"-"` DeprecatedUPnPTimeoutS int `xml:"upnpTimeoutSeconds,omitempty" json:"-"`
DeprecatedRelaysEnabled bool `xml:"relaysEnabled" json:"-"` DeprecatedRelayServers []string `xml:"relayServer,omitempty" json:"-"`
DeprecatedRelayServers []string `xml:"relayServer" json:"-"`
} }
func (orig OptionsConfiguration) Copy() OptionsConfiguration { func (orig OptionsConfiguration) Copy() OptionsConfiguration {

View File

@ -70,10 +70,6 @@ func (d *relayDialer) RedialFrequency() time.Duration {
return time.Duration(d.cfg.Options().RelayReconnectIntervalM) * time.Minute return time.Duration(d.cfg.Options().RelayReconnectIntervalM) * time.Minute
} }
func (d *relayDialer) String() string {
return "Relay Dialer"
}
type relayDialerFactory struct{} type relayDialerFactory struct{}
func (relayDialerFactory) New(cfg *config.Wrapper, tlsCfg *tls.Config) genericDialer { func (relayDialerFactory) New(cfg *config.Wrapper, tlsCfg *tls.Config) genericDialer {
@ -86,3 +82,11 @@ func (relayDialerFactory) New(cfg *config.Wrapper, tlsCfg *tls.Config) genericDi
func (relayDialerFactory) Priority() int { func (relayDialerFactory) Priority() int {
return relayPriority return relayPriority
} }
func (relayDialerFactory) Enabled(cfg config.Configuration) bool {
return cfg.Options.RelaysEnabled
}
func (relayDialerFactory) String() string {
return "Relay Dialer"
}

View File

@ -13,23 +13,26 @@ import (
"sync" "sync"
"time" "time"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/dialer" "github.com/syncthing/syncthing/lib/dialer"
"github.com/syncthing/syncthing/lib/nat" "github.com/syncthing/syncthing/lib/nat"
"github.com/syncthing/syncthing/lib/relay/client" "github.com/syncthing/syncthing/lib/relay/client"
) )
func init() { func init() {
listeners["relay"] = newRelayListener factory := &relayListenerFactory{}
listeners["dynamic+http"] = newRelayListener listeners["relay"] = factory
listeners["dynamic+https"] = newRelayListener listeners["dynamic+http"] = factory
listeners["dynamic+https"] = factory
} }
type relayListener struct { type relayListener struct {
onAddressesChangedNotifier onAddressesChangedNotifier
uri *url.URL uri *url.URL
tlsCfg *tls.Config tlsCfg *tls.Config
conns chan IntermediateConnection conns chan IntermediateConnection
factory listenerFactory
err error err error
client client.RelayClient client client.RelayClient
@ -154,14 +157,25 @@ func (t *relayListener) Error() error {
return cerr return cerr
} }
func (t *relayListener) Factory() listenerFactory {
return t.factory
}
func (t *relayListener) String() string { func (t *relayListener) String() string {
return t.uri.String() return t.uri.String()
} }
func newRelayListener(uri *url.URL, tlsCfg *tls.Config, conns chan IntermediateConnection, natService *nat.Service) genericListener { type relayListenerFactory struct{}
func (f *relayListenerFactory) New(uri *url.URL, cfg *config.Wrapper, tlsCfg *tls.Config, conns chan IntermediateConnection, natService *nat.Service) genericListener {
return &relayListener{ return &relayListener{
uri: uri, uri: uri,
tlsCfg: tlsCfg, tlsCfg: tlsCfg,
conns: conns, conns: conns,
factory: f,
} }
} }
func (relayListenerFactory) Enabled(cfg config.Configuration) bool {
return cfg.Options.RelaysEnabled
}

View File

@ -9,6 +9,7 @@ package connections
import ( import (
"crypto/tls" "crypto/tls"
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"io" "io"
"net" "net"
@ -112,6 +113,10 @@ func NewService(cfg *config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *
return service return service
} }
var (
errDisabled = errors.New("disabled by configuration")
)
func (s *Service) handle() { func (s *Service) handle() {
next: next:
for c := range s.conns { for c := range s.conns {
@ -237,24 +242,35 @@ next:
func (s *Service) connect() { func (s *Service) connect() {
nextDial := make(map[string]time.Time) nextDial := make(map[string]time.Time)
delay := time.Second
sleep := time.Second
bestDialerPrio := 1<<31 - 1 // worse prio won't build on 32 bit // Used as delay for the first few connection attempts, increases
for _, df := range dialers { // exponentially
if prio := df.Priority(); prio < bestDialerPrio { initialRampup := time.Second
bestDialerPrio = prio
} // Calculated from actual dialers reconnectInterval
} var sleep time.Duration
for { for {
cfg := s.cfg.Raw()
bestDialerPrio := 1<<31 - 1 // worse prio won't build on 32 bit
for _, df := range dialers {
if !df.Enabled(cfg) {
continue
}
if prio := df.Priority(); prio < bestDialerPrio {
bestDialerPrio = prio
}
}
l.Debugln("Reconnect loop") l.Debugln("Reconnect loop")
now := time.Now() now := time.Now()
var seen []string var seen []string
nextDevice: nextDevice:
for deviceID, deviceCfg := range s.cfg.Devices() { for _, deviceCfg := range cfg.Devices {
deviceID := deviceCfg.DeviceID
if deviceID == s.myID { if deviceID == s.myID {
continue continue
} }
@ -292,35 +308,40 @@ func (s *Service) connect() {
seen = append(seen, addrs...) seen = append(seen, addrs...)
for _, addr := range addrs { for _, addr := range addrs {
nextDialAt, ok := nextDial[addr]
if ok && initialRampup >= sleep && nextDialAt.After(now) {
l.Debugf("Not dialing %v as sleep is %v, next dial is at %s and current time is %s", addr, sleep, nextDialAt, now)
continue
}
// If we fail at any step before actually getting the dialer
// retry in a minute
nextDial[addr] = now.Add(time.Minute)
uri, err := url.Parse(addr) uri, err := url.Parse(addr)
if err != nil { if err != nil {
l.Infoln("Failed to parse connection url:", addr, err) l.Infof("Dialer for %s: %v", addr, err)
continue continue
} }
dialerFactory, ok := dialers[uri.Scheme] dialerFactory, err := s.getDialerFactory(cfg, uri)
if !ok { if err == errDisabled {
l.Debugln("Unknown address schema", uri) l.Debugln("Dialer for", uri, "is disabled")
continue
}
if err != nil {
l.Infof("Dialer for %v: %v", uri, err)
continue
}
if connected && dialerFactory.Priority() >= ct.Priority {
l.Debugf("Not dialing using %s as priorty is less than current connection (%d >= %d)", dialerFactory, dialerFactory.Priority(), ct.Priority)
continue continue
} }
dialer := dialerFactory.New(s.cfg, s.tlsCfg) dialer := dialerFactory.New(s.cfg, s.tlsCfg)
nextDialAt, ok := nextDial[uri.String()]
// See below for comments on this delay >= sleep check
if delay >= sleep && ok && nextDialAt.After(now) {
l.Debugf("Not dialing as next dial is at %s and current time is %s", nextDialAt, now)
continue
}
nextDial[uri.String()] = now.Add(dialer.RedialFrequency())
if connected && dialer.Priority() >= ct.Priority {
l.Debugf("Not dialing using %s as priorty is less than current connection (%d >= %d)", dialer, dialer.Priority(), ct.Priority)
continue
}
l.Debugln("dial", deviceCfg.DeviceID, uri) l.Debugln("dial", deviceCfg.DeviceID, uri)
nextDial[addr] = now.Add(dialer.RedialFrequency())
conn, err := dialer.Dial(deviceID, uri) conn, err := dialer.Dial(deviceID, uri)
if err != nil { if err != nil {
l.Debugln("dial failed", deviceCfg.DeviceID, uri, err) l.Debugln("dial failed", deviceCfg.DeviceID, uri, err)
@ -338,12 +359,12 @@ func (s *Service) connect() {
nextDial, sleep = filterAndFindSleepDuration(nextDial, seen, now) nextDial, sleep = filterAndFindSleepDuration(nextDial, seen, now)
// delay variable is used to trigger much more frequent dialing after if initialRampup < sleep {
// initial startup, essentially causing redials every 1, 2, 4, 8... seconds l.Debugln("initial rampup; sleep", initialRampup, "and update to", initialRampup*2)
if delay < sleep { time.Sleep(initialRampup)
time.Sleep(delay) initialRampup *= 2
delay *= 2
} else { } else {
l.Debugln("sleep until next dial", sleep)
time.Sleep(sleep) time.Sleep(sleep)
} }
} }
@ -366,24 +387,16 @@ func (s *Service) shouldLimit(addr net.Addr) bool {
return !tcpaddr.IP.IsLoopback() return !tcpaddr.IP.IsLoopback()
} }
func (s *Service) createListener(addr string) { func (s *Service) createListener(factory listenerFactory, uri *url.URL) bool {
// must be called with listenerMut held // must be called with listenerMut held
uri, err := url.Parse(addr)
if err != nil {
l.Infoln("Failed to parse listen address:", addr, err)
return
}
listenerFactory, ok := listeners[uri.Scheme] l.Debugln("Starting listener", uri)
if !ok {
l.Infoln("Unknown listen address scheme:", uri.String())
return
}
listener := listenerFactory(uri, s.tlsCfg, s.conns, s.natService) listener := factory.New(uri, s.cfg, s.tlsCfg, s.conns, s.natService)
listener.OnAddressesChanged(s.logListenAddressesChangedEvent) listener.OnAddressesChanged(s.logListenAddressesChangedEvent)
s.listeners[addr] = listener s.listeners[uri.String()] = listener
s.listenerTokens[addr] = s.Add(listener) s.listenerTokens[uri.String()] = s.Add(listener)
return true
} }
func (s *Service) logListenAddressesChangedEvent(l genericListener) { func (s *Service) logListenAddressesChangedEvent(l genericListener) {
@ -417,15 +430,33 @@ func (s *Service) CommitConfiguration(from, to config.Configuration) bool {
s.listenersMut.Lock() s.listenersMut.Lock()
seen := make(map[string]struct{}) seen := make(map[string]struct{})
for _, addr := range config.Wrap("", to).ListenAddresses() { for _, addr := range config.Wrap("", to).ListenAddresses() {
if _, ok := s.listeners[addr]; !ok { if _, ok := s.listeners[addr]; ok {
l.Debugln("Staring listener", addr) seen[addr] = struct{}{}
s.createListener(addr) continue
} }
uri, err := url.Parse(addr)
if err != nil {
l.Infof("Listener for %s: %v", addr, err)
continue
}
factory, err := s.getListenerFactory(to, uri)
if err == errDisabled {
l.Debugln("Listener for", uri, "is disabled")
continue
}
if err != nil {
l.Infof("Listener for %v: %v", uri, err)
continue
}
s.createListener(factory, uri)
seen[addr] = struct{}{} seen[addr] = struct{}{}
} }
for addr := range s.listeners { for addr, listener := range s.listeners {
if _, ok := seen[addr]; !ok { if _, ok := seen[addr]; !ok || !listener.Factory().Enabled(to) {
l.Debugln("Stopping listener", addr) l.Debugln("Stopping listener", addr)
s.Remove(s.listenerTokens[addr]) s.Remove(s.listenerTokens[addr])
delete(s.listenerTokens, addr) delete(s.listenerTokens, addr)
@ -494,6 +525,32 @@ func (s *Service) Status() map[string]interface{} {
return result return result
} }
func (s *Service) getDialerFactory(cfg config.Configuration, uri *url.URL) (dialerFactory, error) {
dialerFactory, ok := dialers[uri.Scheme]
if !ok {
return nil, fmt.Errorf("unknown address scheme %q", uri.Scheme)
}
if !dialerFactory.Enabled(cfg) {
return nil, errDisabled
}
return dialerFactory, nil
}
func (s *Service) getListenerFactory(cfg config.Configuration, uri *url.URL) (listenerFactory, error) {
listenerFactory, ok := listeners[uri.Scheme]
if !ok {
return nil, fmt.Errorf("unknown address scheme %q", uri.Scheme)
}
if !listenerFactory.Enabled(cfg) {
return nil, errDisabled
}
return listenerFactory, nil
}
func exchangeHello(c net.Conn, h protocol.HelloMessage) (protocol.HelloMessage, error) { func exchangeHello(c net.Conn, h protocol.HelloMessage) (protocol.HelloMessage, error) {
if err := c.SetDeadline(time.Now().Add(2 * time.Second)); err != nil { if err := c.SetDeadline(time.Now().Add(2 * time.Second)); err != nil {
return protocol.HelloMessage{}, err return protocol.HelloMessage{}, err

View File

@ -31,16 +31,19 @@ type Connection struct {
type dialerFactory interface { type dialerFactory interface {
New(*config.Wrapper, *tls.Config) genericDialer New(*config.Wrapper, *tls.Config) genericDialer
Priority() int Priority() int
Enabled(config.Configuration) bool
String() string
} }
type genericDialer interface { type genericDialer interface {
Dial(protocol.DeviceID, *url.URL) (IntermediateConnection, error) Dial(protocol.DeviceID, *url.URL) (IntermediateConnection, error)
Priority() int
RedialFrequency() time.Duration RedialFrequency() time.Duration
String() string
} }
type listenerFactory func(*url.URL, *tls.Config, chan IntermediateConnection, *nat.Service) genericListener type listenerFactory interface {
New(*url.URL, *config.Wrapper, *tls.Config, chan IntermediateConnection, *nat.Service) genericListener
Enabled(config.Configuration) bool
}
type genericListener interface { type genericListener interface {
Serve() Serve()
@ -58,6 +61,7 @@ type genericListener interface {
Error() error Error() error
OnAddressesChanged(func(genericListener)) OnAddressesChanged(func(genericListener))
String() string String() string
Factory() listenerFactory
} }
type Model interface { type Model interface {

View File

@ -20,8 +20,9 @@ import (
const tcpPriority = 10 const tcpPriority = 10
func init() { func init() {
factory := &tcpDialerFactory{}
for _, scheme := range []string{"tcp", "tcp4", "tcp6"} { for _, scheme := range []string{"tcp", "tcp4", "tcp6"} {
dialers[scheme] = tcpDialerFactory{} dialers[scheme] = factory
} }
} }
@ -55,18 +56,10 @@ func (d *tcpDialer) Dial(id protocol.DeviceID, uri *url.URL) (IntermediateConnec
return IntermediateConnection{tc, "TCP (Client)", tcpPriority}, nil return IntermediateConnection{tc, "TCP (Client)", tcpPriority}, nil
} }
func (tcpDialer) Priority() int {
return tcpPriority
}
func (d *tcpDialer) RedialFrequency() time.Duration { func (d *tcpDialer) RedialFrequency() time.Duration {
return time.Duration(d.cfg.Options().ReconnectIntervalS) * time.Second return time.Duration(d.cfg.Options().ReconnectIntervalS) * time.Second
} }
func (d *tcpDialer) String() string {
return "TCP Dialer"
}
type tcpDialerFactory struct{} type tcpDialerFactory struct{}
func (tcpDialerFactory) New(cfg *config.Wrapper, tlsCfg *tls.Config) genericDialer { func (tcpDialerFactory) New(cfg *config.Wrapper, tlsCfg *tls.Config) genericDialer {
@ -79,3 +72,11 @@ func (tcpDialerFactory) New(cfg *config.Wrapper, tlsCfg *tls.Config) genericDial
func (tcpDialerFactory) Priority() int { func (tcpDialerFactory) Priority() int {
return tcpPriority return tcpPriority
} }
func (tcpDialerFactory) Enabled(cfg config.Configuration) bool {
return true
}
func (tcpDialerFactory) String() string {
return "TCP Dialer"
}

View File

@ -14,23 +14,26 @@ import (
"sync" "sync"
"time" "time"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/dialer" "github.com/syncthing/syncthing/lib/dialer"
"github.com/syncthing/syncthing/lib/nat" "github.com/syncthing/syncthing/lib/nat"
) )
func init() { func init() {
factory := &tcpListenerFactory{}
for _, scheme := range []string{"tcp", "tcp4", "tcp6"} { for _, scheme := range []string{"tcp", "tcp4", "tcp6"} {
listeners[scheme] = newTCPListener listeners[scheme] = factory
} }
} }
type tcpListener struct { type tcpListener struct {
onAddressesChangedNotifier onAddressesChangedNotifier
uri *url.URL uri *url.URL
tlsCfg *tls.Config tlsCfg *tls.Config
stop chan struct{} stop chan struct{}
conns chan IntermediateConnection conns chan IntermediateConnection
factory listenerFactory
natService *nat.Service natService *nat.Service
mapping *nat.Mapping mapping *nat.Mapping
@ -63,6 +66,9 @@ func (t *tcpListener) Serve() {
} }
defer listener.Close() defer listener.Close()
l.Infof("TCP listener (%v) starting", listener.Addr())
defer l.Infof("TCP listener (%v) shutting down", listener.Addr())
mapping := t.natService.NewMapping(nat.TCP, tcaddr.IP, tcaddr.Port) mapping := t.natService.NewMapping(nat.TCP, tcaddr.IP, tcaddr.Port)
mapping.OnChanged(func(_ *nat.Mapping, _, _ []nat.Address) { mapping.OnChanged(func(_ *nat.Mapping, _, _ []nat.Address) {
t.notifyAddressesChanged(t) t.notifyAddressesChanged(t)
@ -152,16 +158,61 @@ func (t *tcpListener) String() string {
return t.uri.String() return t.uri.String()
} }
func newTCPListener(uri *url.URL, tlsCfg *tls.Config, conns chan IntermediateConnection, natService *nat.Service) genericListener { func (t *tcpListener) Factory() listenerFactory {
return t.factory
}
type tcpListenerFactory struct{}
func (f *tcpListenerFactory) New(uri *url.URL, cfg *config.Wrapper, tlsCfg *tls.Config, conns chan IntermediateConnection, natService *nat.Service) genericListener {
return &tcpListener{ return &tcpListener{
uri: fixupPort(uri), uri: fixupPort(uri),
tlsCfg: tlsCfg, tlsCfg: tlsCfg,
conns: conns, conns: conns,
natService: natService, natService: natService,
stop: make(chan struct{}), stop: make(chan struct{}),
factory: f,
} }
} }
func (tcpListenerFactory) Enabled(cfg config.Configuration) bool {
return true
}
func isPublicIPv4(ip net.IP) bool {
ip = ip.To4()
if ip == nil {
// Not an IPv4 address (IPv6)
return false
}
// IsGlobalUnicast below only checks that it's not link local or
// multicast, and we want to exclude private (NAT:ed) addresses as well.
rfc1918 := []net.IPNet{
{IP: net.IP{10, 0, 0, 0}, Mask: net.IPMask{255, 0, 0, 0}},
{IP: net.IP{172, 16, 0, 0}, Mask: net.IPMask{255, 240, 0, 0}},
{IP: net.IP{192, 168, 0, 0}, Mask: net.IPMask{255, 255, 0, 0}},
}
for _, n := range rfc1918 {
if n.Contains(ip) {
return false
}
}
return ip.IsGlobalUnicast()
}
func isPublicIPv6(ip net.IP) bool {
if ip.To4() != nil {
// Not an IPv6 address (IPv4)
// (To16() returns a v6 mapped v4 address so can't be used to check
// that it's an actual v6 address)
return false
}
return ip.IsGlobalUnicast()
}
func fixupPort(uri *url.URL) *url.URL { func fixupPort(uri *url.URL) *url.URL {
copyURI := *uri copyURI := *uri

View File

@ -2018,6 +2018,7 @@ func (m *Model) CommitConfiguration(from, to config.Configuration) bool {
from.Options.URAccepted = to.Options.URAccepted from.Options.URAccepted = to.Options.URAccepted
from.Options.URUniqueID = to.Options.URUniqueID from.Options.URUniqueID = to.Options.URUniqueID
from.Options.ListenAddresses = to.Options.ListenAddresses from.Options.ListenAddresses = to.Options.ListenAddresses
from.Options.RelaysEnabled = to.Options.RelaysEnabled
// All of the other generic options require restart. Or at least they may; // All of the other generic options require restart. Or at least they may;
// removing this check requires going through those options carefully and // removing this check requires going through those options carefully and
// making sure there are individual services that handle them correctly. // making sure there are individual services that handle them correctly.

View File

@ -89,7 +89,8 @@ func (c *staticClient) Serve() {
return return
} }
l.Infoln("Joined relay", c.uri) l.Infof("Joined relay %s://%s", c.uri.Scheme, c.uri.Host)
defer l.Infof("Disconnected from relay %s://%s", c.uri.Scheme, c.uri.Host)
c.mut.Lock() c.mut.Lock()
c.connected = true c.connected = true

View File

@ -1,5 +1,5 @@
<configuration version="12"> <configuration version="14">
<folder id="default" path="s1/" ro="false" rescanIntervalS="10" ignorePerms="false" autoNormalize="true"> <folder id="default" label="" path="s1/" type="readwrite" rescanIntervalS="10" ignorePerms="false" autoNormalize="true">
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU"></device> <device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU"></device>
<device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC"></device> <device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC"></device>
<device id="373HSRP-QLPNLIE-JYKZVQF-P4PKZ63-R2ZE6K3-YD442U2-JHBGBQG-WWXAHAU"></device> <device id="373HSRP-QLPNLIE-JYKZVQF-P4PKZ63-R2ZE6K3-YD442U2-JHBGBQG-WWXAHAU"></device>
@ -15,8 +15,10 @@
<pullerSleepS>0</pullerSleepS> <pullerSleepS>0</pullerSleepS>
<pullerPauseS>0</pullerPauseS> <pullerPauseS>0</pullerPauseS>
<maxConflicts>-1</maxConflicts> <maxConflicts>-1</maxConflicts>
<disableSparseFiles>false</disableSparseFiles>
<disableTempIndexes>false</disableTempIndexes>
</folder> </folder>
<folder id="¯\_(ツ)_/¯ Räksmörgås 动作 Адрес" path="s12-1/" ro="false" rescanIntervalS="10" ignorePerms="false" autoNormalize="true"> <folder id="¯\_(ツ)_/¯ Räksmörgås 动作 Адрес" label="" path="s12-1/" type="readwrite" rescanIntervalS="10" ignorePerms="false" autoNormalize="true">
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU"></device> <device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU"></device>
<device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC"></device> <device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC"></device>
<minDiskFreePct>1</minDiskFreePct> <minDiskFreePct>1</minDiskFreePct>
@ -30,6 +32,8 @@
<pullerSleepS>0</pullerSleepS> <pullerSleepS>0</pullerSleepS>
<pullerPauseS>0</pullerPauseS> <pullerPauseS>0</pullerPauseS>
<maxConflicts>-1</maxConflicts> <maxConflicts>-1</maxConflicts>
<disableSparseFiles>false</disableSparseFiles>
<disableTempIndexes>false</disableTempIndexes>
</folder> </folder>
<device id="EJHMPAQ-OGCVORE-ISB4IS3-SYYVJXF-TKJGLTU-66DIQPF-GJ5D2GX-GQ3OWQK" name="s4" compression="metadata" introducer="false"> <device id="EJHMPAQ-OGCVORE-ISB4IS3-SYYVJXF-TKJGLTU-66DIQPF-GJ5D2GX-GQ3OWQK" name="s4" compression="metadata" introducer="false">
<address>tcp://127.0.0.1:22004</address> <address>tcp://127.0.0.1:22004</address>
@ -51,26 +55,25 @@
<user>testuser</user> <user>testuser</user>
<password>$2a$10$7tKL5uvLDGn5s2VLPM2yWOK/II45az0mTel8hxAUJDRQN1Tk2QYwu</password> <password>$2a$10$7tKL5uvLDGn5s2VLPM2yWOK/II45az0mTel8hxAUJDRQN1Tk2QYwu</password>
<apikey>abc123</apikey> <apikey>abc123</apikey>
<theme>default</theme>
</gui> </gui>
<options> <options>
<listenAddress>tcp://127.0.0.1:22001</listenAddress> <listenAddress>tcp://127.0.0.1:22001</listenAddress>
<listenAddress>dynamic+https://relays.syncthing.net/endpoint</listenAddress>
<globalAnnounceServer>default</globalAnnounceServer> <globalAnnounceServer>default</globalAnnounceServer>
<globalAnnounceEnabled>false</globalAnnounceEnabled> <globalAnnounceEnabled>false</globalAnnounceEnabled>
<localAnnounceEnabled>true</localAnnounceEnabled> <localAnnounceEnabled>true</localAnnounceEnabled>
<localAnnouncePort>21027</localAnnouncePort> <localAnnouncePort>21027</localAnnouncePort>
<localAnnounceMCAddr>[ff12::8384]:21027</localAnnounceMCAddr> <localAnnounceMCAddr>[ff12::8384]:21027</localAnnounceMCAddr>
<relayServer>dynamic+https://relays.syncthing.net/endpoint</relayServer>
<maxSendKbps>0</maxSendKbps> <maxSendKbps>0</maxSendKbps>
<maxRecvKbps>0</maxRecvKbps> <maxRecvKbps>0</maxRecvKbps>
<reconnectionIntervalS>5</reconnectionIntervalS> <reconnectionIntervalS>5</reconnectionIntervalS>
<relaysEnabled>true</relaysEnabled>
<relayReconnectIntervalM>10</relayReconnectIntervalM> <relayReconnectIntervalM>10</relayReconnectIntervalM>
<relayWithoutGlobalAnn>false</relayWithoutGlobalAnn>
<startBrowser>false</startBrowser> <startBrowser>false</startBrowser>
<upnpEnabled>true</upnpEnabled> <natEnabled>true</natEnabled>
<upnpLeaseMinutes>0</upnpLeaseMinutes> <natLeaseMinutes>0</natLeaseMinutes>
<upnpRenewalMinutes>30</upnpRenewalMinutes> <natRenewalMinutes>30</natRenewalMinutes>
<upnpTimeoutSeconds>10</upnpTimeoutSeconds> <natTimeoutSeconds>10</natTimeoutSeconds>
<urAccepted>-1</urAccepted> <urAccepted>-1</urAccepted>
<urUniqueID></urUniqueID> <urUniqueID></urUniqueID>
<urURL>https://data.syncthing.net/newdata</urURL> <urURL>https://data.syncthing.net/newdata</urURL>
@ -79,12 +82,18 @@
<restartOnWakeup>true</restartOnWakeup> <restartOnWakeup>true</restartOnWakeup>
<autoUpgradeIntervalH>12</autoUpgradeIntervalH> <autoUpgradeIntervalH>12</autoUpgradeIntervalH>
<keepTemporariesH>24</keepTemporariesH> <keepTemporariesH>24</keepTemporariesH>
<cacheIgnoredFiles>true</cacheIgnoredFiles> <cacheIgnoredFiles>false</cacheIgnoredFiles>
<progressUpdateIntervalS>5</progressUpdateIntervalS> <progressUpdateIntervalS>5</progressUpdateIntervalS>
<symlinksEnabled>true</symlinksEnabled> <symlinksEnabled>true</symlinksEnabled>
<limitBandwidthInLan>false</limitBandwidthInLan> <limitBandwidthInLan>false</limitBandwidthInLan>
<databaseBlockCacheMiB>0</databaseBlockCacheMiB>
<minHomeDiskFreePct>1</minHomeDiskFreePct> <minHomeDiskFreePct>1</minHomeDiskFreePct>
<releasesURL>https://api.github.com/repos/syncthing/syncthing/releases?per_page=30</releasesURL> <releasesURL>https://upgrades.syncthing.net/meta.json</releasesURL>
<overwriteRemoteDeviceNamesOnConnect>false</overwriteRemoteDeviceNamesOnConnect>
<tempIndexMinBlocks>10</tempIndexMinBlocks>
<upnpEnabled>true</upnpEnabled>
<upnpLeaseMinutes>0</upnpLeaseMinutes>
<upnpRenewalMinutes>30</upnpRenewalMinutes>
<upnpTimeoutSeconds>10</upnpTimeoutSeconds>
<relaysEnabled>false</relaysEnabled>
</options> </options>
</configuration> </configuration>