2016-05-04 19:38:12 +00:00
|
|
|
// Copyright (C) 2016 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,
|
2017-02-09 06:52:18 +00:00
|
|
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
2016-05-04 19:38:12 +00:00
|
|
|
|
|
|
|
package connections
|
|
|
|
|
|
|
|
import (
|
2019-11-26 07:39:51 +00:00
|
|
|
"context"
|
2016-05-04 19:38:12 +00:00
|
|
|
"crypto/tls"
|
2016-08-10 09:37:32 +00:00
|
|
|
"fmt"
|
2019-05-29 07:56:40 +00:00
|
|
|
"io"
|
2016-05-04 19:38:12 +00:00
|
|
|
"net"
|
|
|
|
"net/url"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/syncthing/syncthing/lib/config"
|
2022-04-09 14:04:56 +00:00
|
|
|
"github.com/syncthing/syncthing/lib/connections/registry"
|
2016-05-04 19:38:12 +00:00
|
|
|
"github.com/syncthing/syncthing/lib/nat"
|
2022-09-14 06:44:46 +00:00
|
|
|
"github.com/syncthing/syncthing/lib/osutil"
|
2016-05-04 19:38:12 +00:00
|
|
|
"github.com/syncthing/syncthing/lib/protocol"
|
2021-01-11 14:14:44 +00:00
|
|
|
"github.com/syncthing/syncthing/lib/stats"
|
2020-11-17 12:19:04 +00:00
|
|
|
|
|
|
|
"github.com/thejerf/suture/v4"
|
2016-05-04 19:38:12 +00:00
|
|
|
)
|
|
|
|
|
2019-05-29 07:56:40 +00:00
|
|
|
type tlsConn interface {
|
|
|
|
io.ReadWriteCloser
|
|
|
|
ConnectionState() tls.ConnectionState
|
|
|
|
RemoteAddr() net.Addr
|
|
|
|
SetDeadline(time.Time) error
|
|
|
|
SetWriteDeadline(time.Time) error
|
|
|
|
LocalAddr() net.Addr
|
|
|
|
}
|
|
|
|
|
2016-11-30 07:54:20 +00:00
|
|
|
// internalConn is the raw TLS connection plus some metadata on where it
|
|
|
|
// came from (type, priority).
|
|
|
|
type internalConn struct {
|
2019-05-29 07:56:40 +00:00
|
|
|
tlsConn
|
2021-01-05 16:45:07 +00:00
|
|
|
connType connType
|
2022-11-28 08:28:33 +00:00
|
|
|
isLocal bool
|
2021-01-05 16:45:07 +00:00
|
|
|
priority int
|
|
|
|
establishedAt time.Time
|
2023-05-22 06:55:24 +00:00
|
|
|
connectionID string
|
2016-11-30 07:54:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type connType int
|
|
|
|
|
|
|
|
const (
|
|
|
|
connTypeRelayClient connType = iota
|
|
|
|
connTypeRelayServer
|
|
|
|
connTypeTCPClient
|
|
|
|
connTypeTCPServer
|
2019-05-29 07:56:40 +00:00
|
|
|
connTypeQUICClient
|
|
|
|
connTypeQUICServer
|
2016-11-30 07:54:20 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func (t connType) String() string {
|
|
|
|
switch t {
|
|
|
|
case connTypeRelayClient:
|
|
|
|
return "relay-client"
|
|
|
|
case connTypeRelayServer:
|
|
|
|
return "relay-server"
|
|
|
|
case connTypeTCPClient:
|
|
|
|
return "tcp-client"
|
|
|
|
case connTypeTCPServer:
|
|
|
|
return "tcp-server"
|
2019-05-29 07:56:40 +00:00
|
|
|
case connTypeQUICClient:
|
|
|
|
return "quic-client"
|
|
|
|
case connTypeQUICServer:
|
|
|
|
return "quic-server"
|
2016-11-30 07:54:20 +00:00
|
|
|
default:
|
|
|
|
return "unknown-type"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-12 06:16:46 +00:00
|
|
|
func (t connType) Transport() string {
|
|
|
|
switch t {
|
|
|
|
case connTypeRelayClient, connTypeRelayServer:
|
|
|
|
return "relay"
|
|
|
|
case connTypeTCPClient, connTypeTCPServer:
|
|
|
|
return "tcp"
|
2019-05-29 07:56:40 +00:00
|
|
|
case connTypeQUICClient, connTypeQUICServer:
|
|
|
|
return "quic"
|
2017-10-12 06:16:46 +00:00
|
|
|
default:
|
|
|
|
return "unknown"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-16 12:54:28 +00:00
|
|
|
func newInternalConn(tc tlsConn, connType connType, isLocal bool, priority int) internalConn {
|
2021-01-05 16:45:07 +00:00
|
|
|
return internalConn{
|
|
|
|
tlsConn: tc,
|
|
|
|
connType: connType,
|
2023-04-16 12:54:28 +00:00
|
|
|
isLocal: isLocal,
|
2021-01-05 16:45:07 +00:00
|
|
|
priority: priority,
|
2023-05-24 10:34:17 +00:00
|
|
|
establishedAt: time.Now().Truncate(time.Second),
|
|
|
|
connectionID: fmt.Sprintf("%v-%v-%v", tc.LocalAddr(), connType.Transport(), tc.RemoteAddr()),
|
2021-01-05 16:45:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-21 10:40:51 +00:00
|
|
|
func (c internalConn) Close() error {
|
2019-01-09 16:31:09 +00:00
|
|
|
// *tls.Conn.Close() does more than it says on the tin. Specifically, it
|
|
|
|
// sends a TLS alert message, which might block forever if the
|
|
|
|
// connection is dead and we don't have a deadline set.
|
2019-05-29 07:56:40 +00:00
|
|
|
_ = c.SetWriteDeadline(time.Now().Add(250 * time.Millisecond))
|
2020-12-21 10:40:51 +00:00
|
|
|
return c.tlsConn.Close()
|
2019-01-09 16:31:09 +00:00
|
|
|
}
|
|
|
|
|
2016-11-30 07:54:20 +00:00
|
|
|
func (c internalConn) Type() string {
|
|
|
|
return c.connType.String()
|
|
|
|
}
|
|
|
|
|
2022-11-28 08:28:33 +00:00
|
|
|
func (c internalConn) IsLocal() bool {
|
|
|
|
return c.isLocal
|
|
|
|
}
|
|
|
|
|
2017-11-21 07:25:38 +00:00
|
|
|
func (c internalConn) Priority() int {
|
|
|
|
return c.priority
|
|
|
|
}
|
|
|
|
|
2019-02-26 10:49:02 +00:00
|
|
|
func (c internalConn) Crypto() string {
|
|
|
|
cs := c.ConnectionState()
|
|
|
|
return fmt.Sprintf("%s-%s", tlsVersionNames[cs.Version], tlsCipherSuiteNames[cs.CipherSuite])
|
|
|
|
}
|
|
|
|
|
2017-10-12 06:16:46 +00:00
|
|
|
func (c internalConn) Transport() string {
|
2017-11-07 07:25:05 +00:00
|
|
|
transport := c.connType.Transport()
|
2022-09-14 06:44:46 +00:00
|
|
|
ip, err := osutil.IPFromAddr(c.LocalAddr())
|
2017-11-07 07:25:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return transport
|
|
|
|
}
|
|
|
|
if ip.To4() != nil {
|
|
|
|
return transport + "4"
|
|
|
|
}
|
|
|
|
return transport + "6"
|
2017-10-12 06:16:46 +00:00
|
|
|
}
|
|
|
|
|
2021-01-05 16:45:07 +00:00
|
|
|
func (c internalConn) EstablishedAt() time.Time {
|
|
|
|
return c.establishedAt
|
|
|
|
}
|
|
|
|
|
2023-05-22 06:55:24 +00:00
|
|
|
func (c internalConn) ConnectionID() string {
|
|
|
|
return c.connectionID
|
|
|
|
}
|
|
|
|
|
2016-11-30 07:54:20 +00:00
|
|
|
func (c internalConn) String() string {
|
2023-04-16 12:54:28 +00:00
|
|
|
t := "WAN"
|
|
|
|
if c.isLocal {
|
|
|
|
t = "LAN"
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%s-%s/%s/%s/%s-P%d", c.LocalAddr(), c.RemoteAddr(), c.Type(), c.Crypto(), t, c.Priority())
|
2016-08-10 09:37:32 +00:00
|
|
|
}
|
|
|
|
|
2016-05-09 15:33:25 +00:00
|
|
|
type dialerFactory interface {
|
2023-04-16 12:54:28 +00:00
|
|
|
New(config.OptionsConfiguration, *tls.Config, *registry.Registry, *lanChecker) genericDialer
|
2017-11-22 07:05:49 +00:00
|
|
|
AlwaysWAN() bool
|
2018-02-09 10:40:57 +00:00
|
|
|
Valid(config.Configuration) error
|
2016-05-17 00:05:38 +00:00
|
|
|
String() string
|
2016-05-09 15:33:25 +00:00
|
|
|
}
|
2016-05-04 19:38:12 +00:00
|
|
|
|
2019-11-26 07:36:58 +00:00
|
|
|
type commonDialer struct {
|
|
|
|
trafficClass int
|
|
|
|
reconnectInterval time.Duration
|
|
|
|
tlsCfg *tls.Config
|
2023-04-16 12:54:28 +00:00
|
|
|
lanChecker *lanChecker
|
|
|
|
lanPriority int
|
|
|
|
wanPriority int
|
2019-11-26 07:36:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (d *commonDialer) RedialFrequency() time.Duration {
|
|
|
|
return d.reconnectInterval
|
|
|
|
}
|
|
|
|
|
2023-04-16 12:54:28 +00:00
|
|
|
func (d *commonDialer) Priority(host string) int {
|
|
|
|
if d.lanChecker.isLANHost(host) {
|
|
|
|
return d.lanPriority
|
|
|
|
}
|
|
|
|
return d.wanPriority
|
|
|
|
}
|
|
|
|
|
2016-05-04 19:38:12 +00:00
|
|
|
type genericDialer interface {
|
2019-11-26 07:39:51 +00:00
|
|
|
Dial(context.Context, protocol.DeviceID, *url.URL) (internalConn, error)
|
2016-05-04 19:38:12 +00:00
|
|
|
RedialFrequency() time.Duration
|
2023-04-16 12:54:28 +00:00
|
|
|
Priority(host string) int
|
2016-05-04 19:38:12 +00:00
|
|
|
}
|
|
|
|
|
2016-05-17 00:05:38 +00:00
|
|
|
type listenerFactory interface {
|
2023-04-16 12:54:28 +00:00
|
|
|
New(*url.URL, config.Wrapper, *tls.Config, chan internalConn, *nat.Service, *registry.Registry, *lanChecker) genericListener
|
2018-02-09 10:40:57 +00:00
|
|
|
Valid(config.Configuration) error
|
2016-05-17 00:05:38 +00:00
|
|
|
}
|
2016-05-04 19:38:12 +00:00
|
|
|
|
2020-05-11 13:02:22 +00:00
|
|
|
type ListenerAddresses struct {
|
|
|
|
URI *url.URL
|
|
|
|
WANAddresses []*url.URL
|
|
|
|
LANAddresses []*url.URL
|
|
|
|
}
|
|
|
|
|
2016-05-04 19:38:12 +00:00
|
|
|
type genericListener interface {
|
2020-11-17 12:19:04 +00:00
|
|
|
suture.Service
|
2016-05-04 19:38:12 +00:00
|
|
|
URI() *url.URL
|
|
|
|
// A given address can potentially be mutated by the listener.
|
|
|
|
// For example we bind to tcp://0.0.0.0, but that for example might return
|
|
|
|
// tcp://gateway1.ip and tcp://gateway2.ip as WAN addresses due to there
|
|
|
|
// being multiple gateways, and us managing to get a UPnP mapping on both
|
|
|
|
// and tcp://192.168.0.1 and tcp://10.0.0.1 due to there being multiple
|
|
|
|
// network interfaces. (The later case for LAN addresses is made up just
|
|
|
|
// to provide an example)
|
|
|
|
WANAddresses() []*url.URL
|
|
|
|
LANAddresses() []*url.URL
|
|
|
|
Error() error
|
2020-05-11 13:02:22 +00:00
|
|
|
OnAddressesChanged(func(ListenerAddresses))
|
2016-05-04 19:38:12 +00:00
|
|
|
String() string
|
2016-05-17 00:05:38 +00:00
|
|
|
Factory() listenerFactory
|
2017-10-12 06:16:46 +00:00
|
|
|
NATType() string
|
2016-05-04 19:38:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Model interface {
|
|
|
|
protocol.Model
|
2020-12-21 10:40:51 +00:00
|
|
|
AddConnection(conn protocol.Connection, hello protocol.Hello)
|
2021-01-11 14:14:44 +00:00
|
|
|
NumConnections() int
|
2020-12-21 10:40:51 +00:00
|
|
|
Connection(remoteID protocol.DeviceID) (protocol.Connection, bool)
|
2020-09-29 11:17:38 +00:00
|
|
|
OnHello(protocol.DeviceID, net.Addr, protocol.Hello) error
|
2016-07-04 10:40:29 +00:00
|
|
|
GetHello(protocol.DeviceID) protocol.HelloIntf
|
2021-01-11 14:14:44 +00:00
|
|
|
DeviceStatistics() (map[protocol.DeviceID]stats.DeviceStatistics, error)
|
2016-05-04 19:38:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type onAddressesChangedNotifier struct {
|
2020-05-11 13:02:22 +00:00
|
|
|
callbacks []func(ListenerAddresses)
|
2016-05-04 19:38:12 +00:00
|
|
|
}
|
|
|
|
|
2020-05-11 13:02:22 +00:00
|
|
|
func (o *onAddressesChangedNotifier) OnAddressesChanged(callback func(ListenerAddresses)) {
|
2016-05-04 19:38:12 +00:00
|
|
|
o.callbacks = append(o.callbacks, callback)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *onAddressesChangedNotifier) notifyAddressesChanged(l genericListener) {
|
2020-05-11 13:02:22 +00:00
|
|
|
o.notifyAddresses(ListenerAddresses{
|
|
|
|
URI: l.URI(),
|
|
|
|
WANAddresses: l.WANAddresses(),
|
|
|
|
LANAddresses: l.LANAddresses(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *onAddressesChangedNotifier) clearAddresses(l genericListener) {
|
|
|
|
o.notifyAddresses(ListenerAddresses{
|
|
|
|
URI: l.URI(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *onAddressesChangedNotifier) notifyAddresses(l ListenerAddresses) {
|
2016-05-04 19:38:12 +00:00
|
|
|
for _, callback := range o.callbacks {
|
|
|
|
callback(l)
|
|
|
|
}
|
|
|
|
}
|
2017-11-15 09:36:33 +00:00
|
|
|
|
|
|
|
type dialTarget struct {
|
2019-05-16 21:11:46 +00:00
|
|
|
addr string
|
2017-11-15 09:36:33 +00:00
|
|
|
dialer genericDialer
|
|
|
|
priority int
|
|
|
|
uri *url.URL
|
|
|
|
deviceID protocol.DeviceID
|
|
|
|
}
|
|
|
|
|
2019-11-26 07:39:51 +00:00
|
|
|
func (t dialTarget) Dial(ctx context.Context) (internalConn, error) {
|
2017-11-15 09:36:33 +00:00
|
|
|
l.Debugln("dialing", t.deviceID, t.uri, "prio", t.priority)
|
2019-11-26 07:39:51 +00:00
|
|
|
return t.dialer.Dial(ctx, t.deviceID, t.uri)
|
2017-11-15 09:36:33 +00:00
|
|
|
}
|