mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-09 14:50:56 +00:00
Merge remote-tracking branch 'syncthing/pr/1995'
* syncthing/pr/1995: Add switch to disable relays Do not start relay service unless explicitly asked for, or global announcement server is running Add dynamic relay lookup (DDoS relays.syncthing.net!) Discovery clients now take an announcer, global discovery is delayed Expose connection type and relay status in the UI Add dependencies (fixes #1364) Check relays for available devices Add incoming connection relay service Add unsubscribe to config Connections have types Large refactoring/feature commit
This commit is contained in:
commit
c87faace6b
8
Godeps/Godeps.json
generated
8
Godeps/Godeps.json
generated
@ -41,6 +41,14 @@
|
||||
"ImportPath": "github.com/syncthing/protocol",
|
||||
"Rev": "ebcdea63c07327a342f65415bbadc497462b8f1f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/syncthing/relaysrv/client",
|
||||
"Rev": "7c6a31017968e7c1a69148db1ca3dea71eba8236"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/syncthing/relaysrv/protocol",
|
||||
"Rev": "7c6a31017968e7c1a69148db1ca3dea71eba8236"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/syndtr/goleveldb/leveldb",
|
||||
"Rev": "b743d92d3215f11c9b5ce8830fafe1f16786adf4"
|
||||
|
280
Godeps/_workspace/src/github.com/syncthing/relaysrv/client/client.go
generated
vendored
Normal file
280
Godeps/_workspace/src/github.com/syncthing/relaysrv/client/client.go
generated
vendored
Normal file
@ -0,0 +1,280 @@
|
||||
// Copyright (C) 2015 Audrius Butkevicius and Contributors (see the CONTRIBUTORS file).
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
syncthingprotocol "github.com/syncthing/protocol"
|
||||
"github.com/syncthing/relaysrv/protocol"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
)
|
||||
|
||||
type ProtocolClient struct {
|
||||
URI *url.URL
|
||||
Invitations chan protocol.SessionInvitation
|
||||
|
||||
closeInvitationsOnFinish bool
|
||||
|
||||
config *tls.Config
|
||||
|
||||
timeout time.Duration
|
||||
|
||||
stop chan struct{}
|
||||
stopped chan struct{}
|
||||
|
||||
conn *tls.Conn
|
||||
|
||||
mut sync.RWMutex
|
||||
connected bool
|
||||
}
|
||||
|
||||
func NewProtocolClient(uri *url.URL, certs []tls.Certificate, invitations chan protocol.SessionInvitation) *ProtocolClient {
|
||||
closeInvitationsOnFinish := false
|
||||
if invitations == nil {
|
||||
closeInvitationsOnFinish = true
|
||||
invitations = make(chan protocol.SessionInvitation)
|
||||
}
|
||||
|
||||
return &ProtocolClient{
|
||||
URI: uri,
|
||||
Invitations: invitations,
|
||||
|
||||
closeInvitationsOnFinish: closeInvitationsOnFinish,
|
||||
|
||||
config: configForCerts(certs),
|
||||
|
||||
timeout: time.Minute * 2,
|
||||
|
||||
stop: make(chan struct{}),
|
||||
stopped: make(chan struct{}),
|
||||
|
||||
mut: sync.NewRWMutex(),
|
||||
connected: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ProtocolClient) Serve() {
|
||||
c.stop = make(chan struct{})
|
||||
c.stopped = make(chan struct{})
|
||||
defer close(c.stopped)
|
||||
|
||||
if err := c.connect(); err != nil {
|
||||
if debug {
|
||||
l.Debugln("Relay connect:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if debug {
|
||||
l.Debugln(c, "connected", c.conn.RemoteAddr())
|
||||
}
|
||||
|
||||
if err := c.join(); err != nil {
|
||||
c.conn.Close()
|
||||
l.Infoln("Relay join:", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.conn.SetDeadline(time.Time{}); err != nil {
|
||||
l.Infoln("Relay set deadline:", err)
|
||||
return
|
||||
}
|
||||
|
||||
if debug {
|
||||
l.Debugln(c, "joined", c.conn.RemoteAddr(), "via", c.conn.LocalAddr())
|
||||
}
|
||||
|
||||
defer c.cleanup()
|
||||
c.mut.Lock()
|
||||
c.connected = true
|
||||
c.mut.Unlock()
|
||||
|
||||
messages := make(chan interface{})
|
||||
errors := make(chan error, 1)
|
||||
|
||||
go messageReader(c.conn, messages, errors)
|
||||
|
||||
timeout := time.NewTimer(c.timeout)
|
||||
|
||||
for {
|
||||
select {
|
||||
case message := <-messages:
|
||||
timeout.Reset(c.timeout)
|
||||
if debug {
|
||||
log.Printf("%s received message %T", c, message)
|
||||
}
|
||||
|
||||
switch msg := message.(type) {
|
||||
case protocol.Ping:
|
||||
if err := protocol.WriteMessage(c.conn, protocol.Pong{}); err != nil {
|
||||
l.Infoln("Relay write:", err)
|
||||
return
|
||||
|
||||
}
|
||||
if debug {
|
||||
l.Debugln(c, "sent pong")
|
||||
}
|
||||
|
||||
case protocol.SessionInvitation:
|
||||
ip := net.IP(msg.Address)
|
||||
if len(ip) == 0 || ip.IsUnspecified() {
|
||||
msg.Address = c.conn.RemoteAddr().(*net.TCPAddr).IP[:]
|
||||
}
|
||||
c.Invitations <- msg
|
||||
|
||||
default:
|
||||
l.Infoln("Relay: protocol error: unexpected message %v", msg)
|
||||
return
|
||||
}
|
||||
|
||||
case <-c.stop:
|
||||
if debug {
|
||||
l.Debugln(c, "stopping")
|
||||
}
|
||||
return
|
||||
|
||||
case err := <-errors:
|
||||
l.Infoln("Relay received:", err)
|
||||
return
|
||||
|
||||
case <-timeout.C:
|
||||
if debug {
|
||||
l.Debugln(c, "timed out")
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ProtocolClient) Stop() {
|
||||
if c.stop == nil {
|
||||
return
|
||||
}
|
||||
|
||||
close(c.stop)
|
||||
<-c.stopped
|
||||
}
|
||||
|
||||
func (c *ProtocolClient) StatusOK() bool {
|
||||
c.mut.RLock()
|
||||
con := c.connected
|
||||
c.mut.RUnlock()
|
||||
return con
|
||||
}
|
||||
|
||||
func (c *ProtocolClient) String() string {
|
||||
return fmt.Sprintf("ProtocolClient@%p", c)
|
||||
}
|
||||
|
||||
func (c *ProtocolClient) connect() error {
|
||||
if c.URI.Scheme != "relay" {
|
||||
return fmt.Errorf("Unsupported relay schema:", c.URI.Scheme)
|
||||
}
|
||||
|
||||
conn, err := tls.Dial("tcp", c.URI.Host, c.config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := conn.SetDeadline(time.Now().Add(10 * time.Second)); err != nil {
|
||||
conn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
if err := performHandshakeAndValidation(conn, c.URI); err != nil {
|
||||
conn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
c.conn = conn
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ProtocolClient) cleanup() {
|
||||
if c.closeInvitationsOnFinish {
|
||||
close(c.Invitations)
|
||||
c.Invitations = make(chan protocol.SessionInvitation)
|
||||
}
|
||||
|
||||
if debug {
|
||||
l.Debugln(c, "cleaning up")
|
||||
}
|
||||
|
||||
c.mut.Lock()
|
||||
c.connected = false
|
||||
c.mut.Unlock()
|
||||
|
||||
c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *ProtocolClient) join() error {
|
||||
if err := protocol.WriteMessage(c.conn, protocol.JoinRelayRequest{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
message, err := protocol.ReadMessage(c.conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch msg := message.(type) {
|
||||
case protocol.Response:
|
||||
if msg.Code != 0 {
|
||||
return fmt.Errorf("Incorrect response code %d: %s", msg.Code, msg.Message)
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("protocol error: expecting response got %v", msg)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func performHandshakeAndValidation(conn *tls.Conn, uri *url.URL) error {
|
||||
if err := conn.Handshake(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cs := conn.ConnectionState()
|
||||
if !cs.NegotiatedProtocolIsMutual || cs.NegotiatedProtocol != protocol.ProtocolName {
|
||||
return fmt.Errorf("protocol negotiation error")
|
||||
}
|
||||
|
||||
q := uri.Query()
|
||||
relayIDs := q.Get("id")
|
||||
if relayIDs != "" {
|
||||
relayID, err := syncthingprotocol.DeviceIDFromString(relayIDs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("relay address contains invalid verification id: %s", err)
|
||||
}
|
||||
|
||||
certs := cs.PeerCertificates
|
||||
if cl := len(certs); cl != 1 {
|
||||
return fmt.Errorf("unexpected certificate count: %d", cl)
|
||||
}
|
||||
|
||||
remoteID := syncthingprotocol.NewDeviceID(certs[0].Raw)
|
||||
if remoteID != relayID {
|
||||
return fmt.Errorf("relay id does not match. Expected %v got %v", relayID, remoteID)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func messageReader(conn net.Conn, messages chan<- interface{}, errors chan<- error) {
|
||||
for {
|
||||
msg, err := protocol.ReadMessage(conn)
|
||||
if err != nil {
|
||||
errors <- err
|
||||
return
|
||||
}
|
||||
messages <- msg
|
||||
}
|
||||
}
|
15
Godeps/_workspace/src/github.com/syncthing/relaysrv/client/debug.go
generated
vendored
Normal file
15
Godeps/_workspace/src/github.com/syncthing/relaysrv/client/debug.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright (C) 2015 Audrius Butkevicius and Contributors (see the CONTRIBUTORS file).
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/calmh/logger"
|
||||
)
|
||||
|
||||
var (
|
||||
debug = strings.Contains(os.Getenv("STTRACE"), "relay") || os.Getenv("STTRACE") == "all"
|
||||
l = logger.DefaultLogger
|
||||
)
|
117
Godeps/_workspace/src/github.com/syncthing/relaysrv/client/methods.go
generated
vendored
Normal file
117
Godeps/_workspace/src/github.com/syncthing/relaysrv/client/methods.go
generated
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
// Copyright (C) 2015 Audrius Butkevicius and Contributors (see the CONTRIBUTORS file).
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
syncthingprotocol "github.com/syncthing/protocol"
|
||||
"github.com/syncthing/relaysrv/protocol"
|
||||
)
|
||||
|
||||
func GetInvitationFromRelay(uri *url.URL, id syncthingprotocol.DeviceID, certs []tls.Certificate) (protocol.SessionInvitation, error) {
|
||||
if uri.Scheme != "relay" {
|
||||
return protocol.SessionInvitation{}, fmt.Errorf("Unsupported relay scheme:", uri.Scheme)
|
||||
}
|
||||
|
||||
conn, err := tls.Dial("tcp", uri.Host, configForCerts(certs))
|
||||
conn.SetDeadline(time.Now().Add(10 * time.Second))
|
||||
if err != nil {
|
||||
return protocol.SessionInvitation{}, err
|
||||
}
|
||||
|
||||
if err := performHandshakeAndValidation(conn, uri); err != nil {
|
||||
return protocol.SessionInvitation{}, err
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
request := protocol.ConnectRequest{
|
||||
ID: id[:],
|
||||
}
|
||||
|
||||
if err := protocol.WriteMessage(conn, request); err != nil {
|
||||
return protocol.SessionInvitation{}, err
|
||||
}
|
||||
|
||||
message, err := protocol.ReadMessage(conn)
|
||||
if err != nil {
|
||||
return protocol.SessionInvitation{}, err
|
||||
}
|
||||
|
||||
switch msg := message.(type) {
|
||||
case protocol.Response:
|
||||
return protocol.SessionInvitation{}, fmt.Errorf("Incorrect response code %d: %s", msg.Code, msg.Message)
|
||||
case protocol.SessionInvitation:
|
||||
if debug {
|
||||
l.Debugln("Received invitation", msg, "via", conn.LocalAddr())
|
||||
}
|
||||
ip := net.IP(msg.Address)
|
||||
if len(ip) == 0 || ip.IsUnspecified() {
|
||||
msg.Address = conn.RemoteAddr().(*net.TCPAddr).IP[:]
|
||||
}
|
||||
return msg, nil
|
||||
default:
|
||||
return protocol.SessionInvitation{}, fmt.Errorf("protocol error: unexpected message %v", msg)
|
||||
}
|
||||
}
|
||||
|
||||
func JoinSession(invitation protocol.SessionInvitation) (net.Conn, error) {
|
||||
addr := net.JoinHostPort(net.IP(invitation.Address).String(), strconv.Itoa(int(invitation.Port)))
|
||||
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
request := protocol.JoinSessionRequest{
|
||||
Key: invitation.Key,
|
||||
}
|
||||
|
||||
conn.SetDeadline(time.Now().Add(10 * time.Second))
|
||||
err = protocol.WriteMessage(conn, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
message, err := protocol.ReadMessage(conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn.SetDeadline(time.Time{})
|
||||
|
||||
switch msg := message.(type) {
|
||||
case protocol.Response:
|
||||
if msg.Code != 0 {
|
||||
return nil, fmt.Errorf("Incorrect response code %d: %s", msg.Code, msg.Message)
|
||||
}
|
||||
return conn, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("protocol error: expecting response got %v", msg)
|
||||
}
|
||||
}
|
||||
|
||||
func configForCerts(certs []tls.Certificate) *tls.Config {
|
||||
return &tls.Config{
|
||||
Certificates: certs,
|
||||
NextProtos: []string{protocol.ProtocolName},
|
||||
ClientAuth: tls.RequestClientCert,
|
||||
SessionTicketsDisabled: true,
|
||||
InsecureSkipVerify: true,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
CipherSuites: []uint16{
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
},
|
||||
}
|
||||
}
|
65
Godeps/_workspace/src/github.com/syncthing/relaysrv/protocol/packets.go
generated
vendored
Normal file
65
Godeps/_workspace/src/github.com/syncthing/relaysrv/protocol/packets.go
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
// Copyright (C) 2015 Audrius Butkevicius and Contributors (see the CONTRIBUTORS file).
|
||||
|
||||
//go:generate -command genxdr go run ../../syncthing/Godeps/_workspace/src/github.com/calmh/xdr/cmd/genxdr/main.go
|
||||
//go:generate genxdr -o packets_xdr.go packets.go
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
syncthingprotocol "github.com/syncthing/protocol"
|
||||
"net"
|
||||
)
|
||||
|
||||
const (
|
||||
messageTypePing int32 = iota
|
||||
messageTypePong
|
||||
messageTypeJoinRelayRequest
|
||||
messageTypeJoinSessionRequest
|
||||
messageTypeResponse
|
||||
messageTypeConnectRequest
|
||||
messageTypeSessionInvitation
|
||||
)
|
||||
|
||||
type header struct {
|
||||
magic uint32
|
||||
messageType int32
|
||||
messageLength int32
|
||||
}
|
||||
|
||||
type Ping struct{}
|
||||
type Pong struct{}
|
||||
type JoinRelayRequest struct{}
|
||||
|
||||
type JoinSessionRequest struct {
|
||||
Key []byte // max:32
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Code int32
|
||||
Message string
|
||||
}
|
||||
|
||||
type ConnectRequest struct {
|
||||
ID []byte // max:32
|
||||
}
|
||||
|
||||
type SessionInvitation struct {
|
||||
From []byte // max:32
|
||||
Key []byte // max:32
|
||||
Address []byte // max:32
|
||||
Port uint16
|
||||
ServerSocket bool
|
||||
}
|
||||
|
||||
func (i SessionInvitation) String() string {
|
||||
return fmt.Sprintf("%s@%s", syncthingprotocol.DeviceIDFromBytes(i.From), i.AddressString())
|
||||
}
|
||||
|
||||
func (i SessionInvitation) GoString() string {
|
||||
return i.String()
|
||||
}
|
||||
|
||||
func (i SessionInvitation) AddressString() string {
|
||||
return fmt.Sprintf("%s:%d", net.IP(i.Address), i.Port)
|
||||
}
|
567
Godeps/_workspace/src/github.com/syncthing/relaysrv/protocol/packets_xdr.go
generated
vendored
Normal file
567
Godeps/_workspace/src/github.com/syncthing/relaysrv/protocol/packets_xdr.go
generated
vendored
Normal file
@ -0,0 +1,567 @@
|
||||
// ************************************************************
|
||||
// This file is automatically generated by genxdr. Do not edit.
|
||||
// ************************************************************
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/calmh/xdr"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
header Structure:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| magic |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| message Type |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| message Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
|
||||
struct header {
|
||||
unsigned int magic;
|
||||
int messageType;
|
||||
int messageLength;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func (o header) EncodeXDR(w io.Writer) (int, error) {
|
||||
var xw = xdr.NewWriter(w)
|
||||
return o.EncodeXDRInto(xw)
|
||||
}
|
||||
|
||||
func (o header) MarshalXDR() ([]byte, error) {
|
||||
return o.AppendXDR(make([]byte, 0, 128))
|
||||
}
|
||||
|
||||
func (o header) MustMarshalXDR() []byte {
|
||||
bs, err := o.MarshalXDR()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
func (o header) AppendXDR(bs []byte) ([]byte, error) {
|
||||
var aw = xdr.AppendWriter(bs)
|
||||
var xw = xdr.NewWriter(&aw)
|
||||
_, err := o.EncodeXDRInto(xw)
|
||||
return []byte(aw), err
|
||||
}
|
||||
|
||||
func (o header) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||
xw.WriteUint32(o.magic)
|
||||
xw.WriteUint32(uint32(o.messageType))
|
||||
xw.WriteUint32(uint32(o.messageLength))
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
func (o *header) DecodeXDR(r io.Reader) error {
|
||||
xr := xdr.NewReader(r)
|
||||
return o.DecodeXDRFrom(xr)
|
||||
}
|
||||
|
||||
func (o *header) UnmarshalXDR(bs []byte) error {
|
||||
var br = bytes.NewReader(bs)
|
||||
var xr = xdr.NewReader(br)
|
||||
return o.DecodeXDRFrom(xr)
|
||||
}
|
||||
|
||||
func (o *header) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||
o.magic = xr.ReadUint32()
|
||||
o.messageType = int32(xr.ReadUint32())
|
||||
o.messageLength = int32(xr.ReadUint32())
|
||||
return xr.Error()
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Ping Structure:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
|
||||
struct Ping {
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func (o Ping) EncodeXDR(w io.Writer) (int, error) {
|
||||
var xw = xdr.NewWriter(w)
|
||||
return o.EncodeXDRInto(xw)
|
||||
}
|
||||
|
||||
func (o Ping) MarshalXDR() ([]byte, error) {
|
||||
return o.AppendXDR(make([]byte, 0, 128))
|
||||
}
|
||||
|
||||
func (o Ping) MustMarshalXDR() []byte {
|
||||
bs, err := o.MarshalXDR()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
func (o Ping) AppendXDR(bs []byte) ([]byte, error) {
|
||||
var aw = xdr.AppendWriter(bs)
|
||||
var xw = xdr.NewWriter(&aw)
|
||||
_, err := o.EncodeXDRInto(xw)
|
||||
return []byte(aw), err
|
||||
}
|
||||
|
||||
func (o Ping) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
func (o *Ping) DecodeXDR(r io.Reader) error {
|
||||
xr := xdr.NewReader(r)
|
||||
return o.DecodeXDRFrom(xr)
|
||||
}
|
||||
|
||||
func (o *Ping) UnmarshalXDR(bs []byte) error {
|
||||
var br = bytes.NewReader(bs)
|
||||
var xr = xdr.NewReader(br)
|
||||
return o.DecodeXDRFrom(xr)
|
||||
}
|
||||
|
||||
func (o *Ping) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||
return xr.Error()
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Pong Structure:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
|
||||
struct Pong {
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func (o Pong) EncodeXDR(w io.Writer) (int, error) {
|
||||
var xw = xdr.NewWriter(w)
|
||||
return o.EncodeXDRInto(xw)
|
||||
}
|
||||
|
||||
func (o Pong) MarshalXDR() ([]byte, error) {
|
||||
return o.AppendXDR(make([]byte, 0, 128))
|
||||
}
|
||||
|
||||
func (o Pong) MustMarshalXDR() []byte {
|
||||
bs, err := o.MarshalXDR()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
func (o Pong) AppendXDR(bs []byte) ([]byte, error) {
|
||||
var aw = xdr.AppendWriter(bs)
|
||||
var xw = xdr.NewWriter(&aw)
|
||||
_, err := o.EncodeXDRInto(xw)
|
||||
return []byte(aw), err
|
||||
}
|
||||
|
||||
func (o Pong) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
func (o *Pong) DecodeXDR(r io.Reader) error {
|
||||
xr := xdr.NewReader(r)
|
||||
return o.DecodeXDRFrom(xr)
|
||||
}
|
||||
|
||||
func (o *Pong) UnmarshalXDR(bs []byte) error {
|
||||
var br = bytes.NewReader(bs)
|
||||
var xr = xdr.NewReader(br)
|
||||
return o.DecodeXDRFrom(xr)
|
||||
}
|
||||
|
||||
func (o *Pong) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||
return xr.Error()
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
JoinRelayRequest Structure:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
|
||||
struct JoinRelayRequest {
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func (o JoinRelayRequest) EncodeXDR(w io.Writer) (int, error) {
|
||||
var xw = xdr.NewWriter(w)
|
||||
return o.EncodeXDRInto(xw)
|
||||
}
|
||||
|
||||
func (o JoinRelayRequest) MarshalXDR() ([]byte, error) {
|
||||
return o.AppendXDR(make([]byte, 0, 128))
|
||||
}
|
||||
|
||||
func (o JoinRelayRequest) MustMarshalXDR() []byte {
|
||||
bs, err := o.MarshalXDR()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
func (o JoinRelayRequest) AppendXDR(bs []byte) ([]byte, error) {
|
||||
var aw = xdr.AppendWriter(bs)
|
||||
var xw = xdr.NewWriter(&aw)
|
||||
_, err := o.EncodeXDRInto(xw)
|
||||
return []byte(aw), err
|
||||
}
|
||||
|
||||
func (o JoinRelayRequest) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
func (o *JoinRelayRequest) DecodeXDR(r io.Reader) error {
|
||||
xr := xdr.NewReader(r)
|
||||
return o.DecodeXDRFrom(xr)
|
||||
}
|
||||
|
||||
func (o *JoinRelayRequest) UnmarshalXDR(bs []byte) error {
|
||||
var br = bytes.NewReader(bs)
|
||||
var xr = xdr.NewReader(br)
|
||||
return o.DecodeXDRFrom(xr)
|
||||
}
|
||||
|
||||
func (o *JoinRelayRequest) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||
return xr.Error()
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
JoinSessionRequest Structure:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of Key |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ Key (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
|
||||
struct JoinSessionRequest {
|
||||
opaque Key<32>;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func (o JoinSessionRequest) EncodeXDR(w io.Writer) (int, error) {
|
||||
var xw = xdr.NewWriter(w)
|
||||
return o.EncodeXDRInto(xw)
|
||||
}
|
||||
|
||||
func (o JoinSessionRequest) MarshalXDR() ([]byte, error) {
|
||||
return o.AppendXDR(make([]byte, 0, 128))
|
||||
}
|
||||
|
||||
func (o JoinSessionRequest) MustMarshalXDR() []byte {
|
||||
bs, err := o.MarshalXDR()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
func (o JoinSessionRequest) AppendXDR(bs []byte) ([]byte, error) {
|
||||
var aw = xdr.AppendWriter(bs)
|
||||
var xw = xdr.NewWriter(&aw)
|
||||
_, err := o.EncodeXDRInto(xw)
|
||||
return []byte(aw), err
|
||||
}
|
||||
|
||||
func (o JoinSessionRequest) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||
if l := len(o.Key); l > 32 {
|
||||
return xw.Tot(), xdr.ElementSizeExceeded("Key", l, 32)
|
||||
}
|
||||
xw.WriteBytes(o.Key)
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
func (o *JoinSessionRequest) DecodeXDR(r io.Reader) error {
|
||||
xr := xdr.NewReader(r)
|
||||
return o.DecodeXDRFrom(xr)
|
||||
}
|
||||
|
||||
func (o *JoinSessionRequest) UnmarshalXDR(bs []byte) error {
|
||||
var br = bytes.NewReader(bs)
|
||||
var xr = xdr.NewReader(br)
|
||||
return o.DecodeXDRFrom(xr)
|
||||
}
|
||||
|
||||
func (o *JoinSessionRequest) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||
o.Key = xr.ReadBytesMax(32)
|
||||
return xr.Error()
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Response Structure:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Code |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of Message |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ Message (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
|
||||
struct Response {
|
||||
int Code;
|
||||
string Message<>;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func (o Response) EncodeXDR(w io.Writer) (int, error) {
|
||||
var xw = xdr.NewWriter(w)
|
||||
return o.EncodeXDRInto(xw)
|
||||
}
|
||||
|
||||
func (o Response) MarshalXDR() ([]byte, error) {
|
||||
return o.AppendXDR(make([]byte, 0, 128))
|
||||
}
|
||||
|
||||
func (o Response) MustMarshalXDR() []byte {
|
||||
bs, err := o.MarshalXDR()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
func (o Response) AppendXDR(bs []byte) ([]byte, error) {
|
||||
var aw = xdr.AppendWriter(bs)
|
||||
var xw = xdr.NewWriter(&aw)
|
||||
_, err := o.EncodeXDRInto(xw)
|
||||
return []byte(aw), err
|
||||
}
|
||||
|
||||
func (o Response) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||
xw.WriteUint32(uint32(o.Code))
|
||||
xw.WriteString(o.Message)
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
func (o *Response) DecodeXDR(r io.Reader) error {
|
||||
xr := xdr.NewReader(r)
|
||||
return o.DecodeXDRFrom(xr)
|
||||
}
|
||||
|
||||
func (o *Response) UnmarshalXDR(bs []byte) error {
|
||||
var br = bytes.NewReader(bs)
|
||||
var xr = xdr.NewReader(br)
|
||||
return o.DecodeXDRFrom(xr)
|
||||
}
|
||||
|
||||
func (o *Response) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||
o.Code = int32(xr.ReadUint32())
|
||||
o.Message = xr.ReadString()
|
||||
return xr.Error()
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
ConnectRequest Structure:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of ID |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ ID (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
|
||||
struct ConnectRequest {
|
||||
opaque ID<32>;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func (o ConnectRequest) EncodeXDR(w io.Writer) (int, error) {
|
||||
var xw = xdr.NewWriter(w)
|
||||
return o.EncodeXDRInto(xw)
|
||||
}
|
||||
|
||||
func (o ConnectRequest) MarshalXDR() ([]byte, error) {
|
||||
return o.AppendXDR(make([]byte, 0, 128))
|
||||
}
|
||||
|
||||
func (o ConnectRequest) MustMarshalXDR() []byte {
|
||||
bs, err := o.MarshalXDR()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
func (o ConnectRequest) AppendXDR(bs []byte) ([]byte, error) {
|
||||
var aw = xdr.AppendWriter(bs)
|
||||
var xw = xdr.NewWriter(&aw)
|
||||
_, err := o.EncodeXDRInto(xw)
|
||||
return []byte(aw), err
|
||||
}
|
||||
|
||||
func (o ConnectRequest) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||
if l := len(o.ID); l > 32 {
|
||||
return xw.Tot(), xdr.ElementSizeExceeded("ID", l, 32)
|
||||
}
|
||||
xw.WriteBytes(o.ID)
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
func (o *ConnectRequest) DecodeXDR(r io.Reader) error {
|
||||
xr := xdr.NewReader(r)
|
||||
return o.DecodeXDRFrom(xr)
|
||||
}
|
||||
|
||||
func (o *ConnectRequest) UnmarshalXDR(bs []byte) error {
|
||||
var br = bytes.NewReader(bs)
|
||||
var xr = xdr.NewReader(br)
|
||||
return o.DecodeXDRFrom(xr)
|
||||
}
|
||||
|
||||
func (o *ConnectRequest) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||
o.ID = xr.ReadBytesMax(32)
|
||||
return xr.Error()
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
SessionInvitation Structure:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of From |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ From (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of Key |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ Key (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of Address |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ Address (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 0x0000 | Port |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Server Socket (V=0 or 1) |V|
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
|
||||
struct SessionInvitation {
|
||||
opaque From<32>;
|
||||
opaque Key<32>;
|
||||
opaque Address<32>;
|
||||
unsigned int Port;
|
||||
bool ServerSocket;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func (o SessionInvitation) EncodeXDR(w io.Writer) (int, error) {
|
||||
var xw = xdr.NewWriter(w)
|
||||
return o.EncodeXDRInto(xw)
|
||||
}
|
||||
|
||||
func (o SessionInvitation) MarshalXDR() ([]byte, error) {
|
||||
return o.AppendXDR(make([]byte, 0, 128))
|
||||
}
|
||||
|
||||
func (o SessionInvitation) MustMarshalXDR() []byte {
|
||||
bs, err := o.MarshalXDR()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
func (o SessionInvitation) AppendXDR(bs []byte) ([]byte, error) {
|
||||
var aw = xdr.AppendWriter(bs)
|
||||
var xw = xdr.NewWriter(&aw)
|
||||
_, err := o.EncodeXDRInto(xw)
|
||||
return []byte(aw), err
|
||||
}
|
||||
|
||||
func (o SessionInvitation) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||
if l := len(o.From); l > 32 {
|
||||
return xw.Tot(), xdr.ElementSizeExceeded("From", l, 32)
|
||||
}
|
||||
xw.WriteBytes(o.From)
|
||||
if l := len(o.Key); l > 32 {
|
||||
return xw.Tot(), xdr.ElementSizeExceeded("Key", l, 32)
|
||||
}
|
||||
xw.WriteBytes(o.Key)
|
||||
if l := len(o.Address); l > 32 {
|
||||
return xw.Tot(), xdr.ElementSizeExceeded("Address", l, 32)
|
||||
}
|
||||
xw.WriteBytes(o.Address)
|
||||
xw.WriteUint16(o.Port)
|
||||
xw.WriteBool(o.ServerSocket)
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
func (o *SessionInvitation) DecodeXDR(r io.Reader) error {
|
||||
xr := xdr.NewReader(r)
|
||||
return o.DecodeXDRFrom(xr)
|
||||
}
|
||||
|
||||
func (o *SessionInvitation) UnmarshalXDR(bs []byte) error {
|
||||
var br = bytes.NewReader(bs)
|
||||
var xr = xdr.NewReader(br)
|
||||
return o.DecodeXDRFrom(xr)
|
||||
}
|
||||
|
||||
func (o *SessionInvitation) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||
o.From = xr.ReadBytesMax(32)
|
||||
o.Key = xr.ReadBytesMax(32)
|
||||
o.Address = xr.ReadBytesMax(32)
|
||||
o.Port = xr.ReadUint16()
|
||||
o.ServerSocket = xr.ReadBool()
|
||||
return xr.Error()
|
||||
}
|
114
Godeps/_workspace/src/github.com/syncthing/relaysrv/protocol/protocol.go
generated
vendored
Normal file
114
Godeps/_workspace/src/github.com/syncthing/relaysrv/protocol/protocol.go
generated
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
// Copyright (C) 2015 Audrius Butkevicius and Contributors (see the CONTRIBUTORS file).
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
magic = 0x9E79BC40
|
||||
ProtocolName = "bep-relay"
|
||||
)
|
||||
|
||||
var (
|
||||
ResponseSuccess = Response{0, "success"}
|
||||
ResponseNotFound = Response{1, "not found"}
|
||||
ResponseAlreadyConnected = Response{2, "already connected"}
|
||||
ResponseInternalError = Response{99, "internal error"}
|
||||
ResponseUnexpectedMessage = Response{100, "unexpected message"}
|
||||
)
|
||||
|
||||
func WriteMessage(w io.Writer, message interface{}) error {
|
||||
header := header{
|
||||
magic: magic,
|
||||
}
|
||||
|
||||
var payload []byte
|
||||
var err error
|
||||
|
||||
switch msg := message.(type) {
|
||||
case Ping:
|
||||
payload, err = msg.MarshalXDR()
|
||||
header.messageType = messageTypePing
|
||||
case Pong:
|
||||
payload, err = msg.MarshalXDR()
|
||||
header.messageType = messageTypePong
|
||||
case JoinRelayRequest:
|
||||
payload, err = msg.MarshalXDR()
|
||||
header.messageType = messageTypeJoinRelayRequest
|
||||
case JoinSessionRequest:
|
||||
payload, err = msg.MarshalXDR()
|
||||
header.messageType = messageTypeJoinSessionRequest
|
||||
case Response:
|
||||
payload, err = msg.MarshalXDR()
|
||||
header.messageType = messageTypeResponse
|
||||
case ConnectRequest:
|
||||
payload, err = msg.MarshalXDR()
|
||||
header.messageType = messageTypeConnectRequest
|
||||
case SessionInvitation:
|
||||
payload, err = msg.MarshalXDR()
|
||||
header.messageType = messageTypeSessionInvitation
|
||||
default:
|
||||
err = fmt.Errorf("Unknown message type")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
header.messageLength = int32(len(payload))
|
||||
|
||||
headerpayload, err := header.MarshalXDR()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(append(headerpayload, payload...))
|
||||
return err
|
||||
}
|
||||
|
||||
func ReadMessage(r io.Reader) (interface{}, error) {
|
||||
var header header
|
||||
if err := header.DecodeXDR(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if header.magic != magic {
|
||||
return nil, fmt.Errorf("magic mismatch")
|
||||
}
|
||||
|
||||
switch header.messageType {
|
||||
case messageTypePing:
|
||||
var msg Ping
|
||||
err := msg.DecodeXDR(r)
|
||||
return msg, err
|
||||
case messageTypePong:
|
||||
var msg Pong
|
||||
err := msg.DecodeXDR(r)
|
||||
return msg, err
|
||||
case messageTypeJoinRelayRequest:
|
||||
var msg JoinRelayRequest
|
||||
err := msg.DecodeXDR(r)
|
||||
return msg, err
|
||||
case messageTypeJoinSessionRequest:
|
||||
var msg JoinSessionRequest
|
||||
err := msg.DecodeXDR(r)
|
||||
return msg, err
|
||||
case messageTypeResponse:
|
||||
var msg Response
|
||||
err := msg.DecodeXDR(r)
|
||||
return msg, err
|
||||
case messageTypeConnectRequest:
|
||||
var msg ConnectRequest
|
||||
err := msg.DecodeXDR(r)
|
||||
return msg, err
|
||||
case messageTypeSessionInvitation:
|
||||
var msg SessionInvitation
|
||||
err := msg.DecodeXDR(r)
|
||||
return msg, err
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Unknown message type")
|
||||
}
|
@ -21,11 +21,11 @@ func main() {
|
||||
|
||||
var server string
|
||||
|
||||
flag.StringVar(&server, "server", "udp4://announce.syncthing.net:22026", "Announce server")
|
||||
flag.StringVar(&server, "server", "udp4://announce.syncthing.net:22027", "Announce server")
|
||||
flag.Parse()
|
||||
|
||||
if len(flag.Args()) != 1 || server == "" {
|
||||
log.Printf("Usage: %s [-server=\"udp4://announce.syncthing.net:22026\"] <device>", os.Args[0])
|
||||
log.Printf("Usage: %s [-server=\"udp4://announce.syncthing.net:22027\"] <device>", os.Args[0])
|
||||
os.Exit(64)
|
||||
}
|
||||
|
||||
@ -35,9 +35,13 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
discoverer := discover.NewDiscoverer(protocol.LocalDeviceID, nil)
|
||||
discoverer := discover.NewDiscoverer(protocol.LocalDeviceID, nil, nil)
|
||||
discoverer.StartGlobal([]string{server}, 1)
|
||||
for _, addr := range discoverer.Lookup(id) {
|
||||
log.Println(addr)
|
||||
addresses, relays := discoverer.Lookup(id)
|
||||
for _, addr := range addresses {
|
||||
log.Println("address:", addr)
|
||||
}
|
||||
for _, addr := range relays {
|
||||
log.Println("relay:", addr)
|
||||
}
|
||||
}
|
||||
|
@ -11,16 +11,28 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/protocol"
|
||||
"github.com/syncthing/relaysrv/client"
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
"github.com/syncthing/syncthing/lib/events"
|
||||
"github.com/syncthing/syncthing/lib/model"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
|
||||
"github.com/thejerf/suture"
|
||||
)
|
||||
|
||||
type DialerFactory func(*url.URL, *tls.Config) (*tls.Conn, error)
|
||||
type ListenerFactory func(*url.URL, *tls.Config, chan<- model.IntermediateConnection)
|
||||
|
||||
var (
|
||||
dialers = make(map[string]DialerFactory, 0)
|
||||
listeners = make(map[string]ListenerFactory, 0)
|
||||
)
|
||||
|
||||
// The connection service listens on TLS and dials configured unconnected
|
||||
// devices. Successful connections are handed to the model.
|
||||
type connectionSvc struct {
|
||||
@ -29,48 +41,36 @@ type connectionSvc struct {
|
||||
myID protocol.DeviceID
|
||||
model *model.Model
|
||||
tlsCfg *tls.Config
|
||||
conns chan *tls.Conn
|
||||
conns chan model.IntermediateConnection
|
||||
|
||||
lastRelayCheck map[protocol.DeviceID]time.Time
|
||||
|
||||
mut sync.RWMutex
|
||||
connType map[protocol.DeviceID]model.ConnectionType
|
||||
relaysEnabled bool
|
||||
}
|
||||
|
||||
func newConnectionSvc(cfg *config.Wrapper, myID protocol.DeviceID, model *model.Model, tlsCfg *tls.Config) *connectionSvc {
|
||||
func newConnectionSvc(cfg *config.Wrapper, myID protocol.DeviceID, mdl *model.Model, tlsCfg *tls.Config) *connectionSvc {
|
||||
svc := &connectionSvc{
|
||||
Supervisor: suture.NewSimple("connectionSvc"),
|
||||
cfg: cfg,
|
||||
myID: myID,
|
||||
model: model,
|
||||
model: mdl,
|
||||
tlsCfg: tlsCfg,
|
||||
conns: make(chan *tls.Conn),
|
||||
conns: make(chan model.IntermediateConnection),
|
||||
|
||||
connType: make(map[protocol.DeviceID]model.ConnectionType),
|
||||
relaysEnabled: cfg.Options().RelaysEnabled,
|
||||
lastRelayCheck: make(map[protocol.DeviceID]time.Time),
|
||||
}
|
||||
cfg.Subscribe(svc)
|
||||
|
||||
// There are several moving parts here; one routine per listening address
|
||||
// to handle incoming connections, one routine to periodically attempt
|
||||
// outgoing connections, and lastly one routine to the the common handling
|
||||
// regardless of whether the connection was incoming or outgoing. It ends
|
||||
// up as in the diagram below. We embed a Supervisor to manage the
|
||||
// routines (i.e. log and restart if they crash or exit, etc).
|
||||
//
|
||||
// +-----------------+
|
||||
// Incoming | +---------------+-+ +-----------------+
|
||||
// Connections | | | | | Outgoing
|
||||
// -------------->| | svc.listen | | | Connections
|
||||
// | | (1 per listen | | svc.connect |-------------->
|
||||
// | | address) | | |
|
||||
// +-+ | | |
|
||||
// +-----------------+ +-----------------+
|
||||
// v v
|
||||
// | |
|
||||
// | |
|
||||
// +------------+-----------+
|
||||
// |
|
||||
// | svc.conns
|
||||
// v
|
||||
// +-----------------+
|
||||
// | |
|
||||
// | |
|
||||
// | svc.handle |------> model.AddConnection()
|
||||
// | |
|
||||
// | |
|
||||
// +-----------------+
|
||||
// outgoing connections, one routine to the the common handling
|
||||
// regardless of whether the connection was incoming or outgoing.
|
||||
// Furthermore, a relay service which handles incoming requests to connect
|
||||
// via the relays.
|
||||
//
|
||||
// TODO: Clean shutdown, and/or handling config changes on the fly. We
|
||||
// partly do this now - new devices and addresses will be picked up, but
|
||||
@ -79,11 +79,25 @@ func newConnectionSvc(cfg *config.Wrapper, myID protocol.DeviceID, model *model.
|
||||
|
||||
svc.Add(serviceFunc(svc.connect))
|
||||
for _, addr := range svc.cfg.Options().ListenAddress {
|
||||
addr := addr
|
||||
listener := serviceFunc(func() {
|
||||
svc.listen(addr)
|
||||
})
|
||||
svc.Add(listener)
|
||||
uri, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
l.Infoln("Failed to parse listen address:", addr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
listener, ok := listeners[uri.Scheme]
|
||||
if !ok {
|
||||
l.Infoln("Unknown listen address scheme:", uri.String())
|
||||
continue
|
||||
}
|
||||
|
||||
if debugNet {
|
||||
l.Debugln("listening on", uri.String())
|
||||
}
|
||||
|
||||
svc.Add(serviceFunc(func() {
|
||||
listener(uri, svc.tlsCfg, svc.conns)
|
||||
}))
|
||||
}
|
||||
svc.Add(serviceFunc(svc.handle))
|
||||
|
||||
@ -92,15 +106,15 @@ func newConnectionSvc(cfg *config.Wrapper, myID protocol.DeviceID, model *model.
|
||||
|
||||
func (s *connectionSvc) handle() {
|
||||
next:
|
||||
for conn := range s.conns {
|
||||
cs := conn.ConnectionState()
|
||||
for c := range s.conns {
|
||||
cs := c.Conn.ConnectionState()
|
||||
|
||||
// We should have negotiated the next level protocol "bep/1.0" as part
|
||||
// of the TLS handshake. Unfortunately this can't be a hard error,
|
||||
// because there are implementations out there that don't support
|
||||
// protocol negotiation (iOS for one...).
|
||||
if !cs.NegotiatedProtocolIsMutual || cs.NegotiatedProtocol != bepProtocolName {
|
||||
l.Infof("Peer %s did not negotiate bep/1.0", conn.RemoteAddr())
|
||||
l.Infof("Peer %s did not negotiate bep/1.0", c.Conn.RemoteAddr())
|
||||
}
|
||||
|
||||
// We should have received exactly one certificate from the other
|
||||
@ -108,8 +122,8 @@ next:
|
||||
// connection.
|
||||
certs := cs.PeerCertificates
|
||||
if cl := len(certs); cl != 1 {
|
||||
l.Infof("Got peer certificate list of length %d != 1 from %s; protocol error", cl, conn.RemoteAddr())
|
||||
conn.Close()
|
||||
l.Infof("Got peer certificate list of length %d != 1 from %s; protocol error", cl, c.Conn.RemoteAddr())
|
||||
c.Conn.Close()
|
||||
continue
|
||||
}
|
||||
remoteCert := certs[0]
|
||||
@ -120,19 +134,29 @@ next:
|
||||
// clients between the same NAT gateway, and global discovery.
|
||||
if remoteID == myID {
|
||||
l.Infof("Connected to myself (%s) - should not happen", remoteID)
|
||||
conn.Close()
|
||||
c.Conn.Close()
|
||||
continue
|
||||
}
|
||||
|
||||
// We should not already be connected to the other party. TODO: This
|
||||
// could use some better handling. If the old connection is dead but
|
||||
// hasn't timed out yet we may want to drop *that* connection and keep
|
||||
// this one. But in case we are two devices connecting to each other
|
||||
// in parallel we don't want to do that or we end up with no
|
||||
// connections still established...
|
||||
if s.model.ConnectedTo(remoteID) {
|
||||
// If we have a relay connection, and the new incoming connection is
|
||||
// not a relay connection, we should drop that, and prefer the this one.
|
||||
s.mut.RLock()
|
||||
ct, ok := s.connType[remoteID]
|
||||
s.mut.RUnlock()
|
||||
if ok && !ct.IsDirect() && c.ConnType.IsDirect() {
|
||||
if debugNet {
|
||||
l.Debugln("Switching connections", remoteID)
|
||||
}
|
||||
s.model.Close(remoteID, fmt.Errorf("switching connections"))
|
||||
} else if s.model.ConnectedTo(remoteID) {
|
||||
// We should not already be connected to the other party. TODO: This
|
||||
// could use some better handling. If the old connection is dead but
|
||||
// hasn't timed out yet we may want to drop *that* connection and keep
|
||||
// this one. But in case we are two devices connecting to each other
|
||||
// in parallel we don't want to do that or we end up with no
|
||||
// connections still established...
|
||||
l.Infof("Connected to already connected device (%s)", remoteID)
|
||||
conn.Close()
|
||||
c.Conn.Close()
|
||||
continue
|
||||
}
|
||||
|
||||
@ -150,35 +174,42 @@ next:
|
||||
// Incorrect certificate name is something the user most
|
||||
// likely wants to know about, since it's an advanced
|
||||
// config. Warn instead of Info.
|
||||
l.Warnf("Bad certificate from %s (%v): %v", remoteID, conn.RemoteAddr(), err)
|
||||
conn.Close()
|
||||
l.Warnf("Bad certificate from %s (%v): %v", remoteID, c.Conn.RemoteAddr(), err)
|
||||
c.Conn.Close()
|
||||
continue next
|
||||
}
|
||||
|
||||
// If rate limiting is set, and based on the address we should
|
||||
// limit the connection, then we wrap it in a limiter.
|
||||
|
||||
limit := s.shouldLimit(conn.RemoteAddr())
|
||||
limit := s.shouldLimit(c.Conn.RemoteAddr())
|
||||
|
||||
wr := io.Writer(conn)
|
||||
wr := io.Writer(c.Conn)
|
||||
if limit && writeRateLimit != nil {
|
||||
wr = &limitedWriter{conn, writeRateLimit}
|
||||
wr = &limitedWriter{c.Conn, writeRateLimit}
|
||||
}
|
||||
|
||||
rd := io.Reader(conn)
|
||||
rd := io.Reader(c.Conn)
|
||||
if limit && readRateLimit != nil {
|
||||
rd = &limitedReader{conn, readRateLimit}
|
||||
rd = &limitedReader{c.Conn, readRateLimit}
|
||||
}
|
||||
|
||||
name := fmt.Sprintf("%s-%s", conn.LocalAddr(), conn.RemoteAddr())
|
||||
name := fmt.Sprintf("%s-%s (%s)", c.Conn.LocalAddr(), c.Conn.RemoteAddr(), c.ConnType)
|
||||
protoConn := protocol.NewConnection(remoteID, rd, wr, s.model, name, deviceCfg.Compression)
|
||||
|
||||
l.Infof("Established secure connection to %s at %s", remoteID, name)
|
||||
if debugNet {
|
||||
l.Debugf("cipher suite: %04X in lan: %t", conn.ConnectionState().CipherSuite, !limit)
|
||||
l.Debugf("cipher suite: %04X in lan: %t", c.Conn.ConnectionState().CipherSuite, !limit)
|
||||
}
|
||||
|
||||
s.model.AddConnection(conn, protoConn)
|
||||
s.model.AddConnection(model.Connection{
|
||||
c.Conn,
|
||||
protoConn,
|
||||
c.ConnType,
|
||||
})
|
||||
s.mut.Lock()
|
||||
s.connType[remoteID] = c.ConnType
|
||||
s.mut.Unlock()
|
||||
continue next
|
||||
}
|
||||
}
|
||||
@ -186,54 +217,14 @@ next:
|
||||
if !s.cfg.IgnoredDevice(remoteID) {
|
||||
events.Default.Log(events.DeviceRejected, map[string]string{
|
||||
"device": remoteID.String(),
|
||||
"address": conn.RemoteAddr().String(),
|
||||
"address": c.Conn.RemoteAddr().String(),
|
||||
})
|
||||
l.Infof("Connection from %s with unknown device ID %s", conn.RemoteAddr(), remoteID)
|
||||
l.Infof("Connection from %s (%s) with unknown device ID %s", c.Conn.RemoteAddr(), c.ConnType, remoteID)
|
||||
} else {
|
||||
l.Infof("Connection from %s with ignored device ID %s", conn.RemoteAddr(), remoteID)
|
||||
l.Infof("Connection from %s (%s) with ignored device ID %s", c.Conn.RemoteAddr(), c.ConnType, remoteID)
|
||||
}
|
||||
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *connectionSvc) listen(addr string) {
|
||||
if debugNet {
|
||||
l.Debugln("listening on", addr)
|
||||
}
|
||||
|
||||
tcaddr, err := net.ResolveTCPAddr("tcp", addr)
|
||||
if err != nil {
|
||||
l.Fatalln("listen (BEP):", err)
|
||||
}
|
||||
listener, err := net.ListenTCP("tcp", tcaddr)
|
||||
if err != nil {
|
||||
l.Fatalln("listen (BEP):", err)
|
||||
}
|
||||
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
l.Warnln("Accepting connection:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if debugNet {
|
||||
l.Debugln("connect from", conn.RemoteAddr())
|
||||
}
|
||||
|
||||
tcpConn := conn.(*net.TCPConn)
|
||||
s.setTCPOptions(tcpConn)
|
||||
|
||||
tc := tls.Server(conn, s.tlsCfg)
|
||||
err = tc.Handshake()
|
||||
if err != nil {
|
||||
l.Infoln("TLS handshake:", err)
|
||||
tc.Close()
|
||||
continue
|
||||
}
|
||||
|
||||
s.conns <- tc
|
||||
c.Conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,19 +237,24 @@ func (s *connectionSvc) connect() {
|
||||
continue
|
||||
}
|
||||
|
||||
if s.model.ConnectedTo(deviceID) {
|
||||
connected := s.model.ConnectedTo(deviceID)
|
||||
|
||||
s.mut.RLock()
|
||||
ct, ok := s.connType[deviceID]
|
||||
relaysEnabled := s.relaysEnabled
|
||||
s.mut.RUnlock()
|
||||
if connected && ok && ct.IsDirect() {
|
||||
continue
|
||||
}
|
||||
|
||||
var addrs []string
|
||||
var relays []string
|
||||
for _, addr := range deviceCfg.Addresses {
|
||||
if addr == "dynamic" {
|
||||
if discoverer != nil {
|
||||
t := discoverer.Lookup(deviceID)
|
||||
if len(t) == 0 {
|
||||
continue
|
||||
}
|
||||
t, r := discoverer.Lookup(deviceID)
|
||||
addrs = append(addrs, t...)
|
||||
relays = append(relays, r...)
|
||||
}
|
||||
} else {
|
||||
addrs = append(addrs, addr)
|
||||
@ -266,45 +262,108 @@ func (s *connectionSvc) connect() {
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
host, port, err := net.SplitHostPort(addr)
|
||||
if err != nil && strings.HasPrefix(err.Error(), "missing port") {
|
||||
// addr is on the form "1.2.3.4"
|
||||
addr = net.JoinHostPort(addr, "22000")
|
||||
} else if err == nil && port == "" {
|
||||
// addr is on the form "1.2.3.4:"
|
||||
addr = net.JoinHostPort(host, "22000")
|
||||
uri, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
l.Infoln("Failed to parse connection url:", addr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
dialer, ok := dialers[uri.Scheme]
|
||||
if !ok {
|
||||
l.Infoln("Unknown address schema", uri.String())
|
||||
continue
|
||||
}
|
||||
|
||||
if debugNet {
|
||||
l.Debugln("dial", deviceCfg.DeviceID, addr)
|
||||
l.Debugln("dial", deviceCfg.DeviceID, uri.String())
|
||||
}
|
||||
|
||||
raddr, err := net.ResolveTCPAddr("tcp", addr)
|
||||
conn, err := dialer(uri, s.tlsCfg)
|
||||
if err != nil {
|
||||
if debugNet {
|
||||
l.Debugln(err)
|
||||
l.Debugln("dial failed", deviceCfg.DeviceID, uri.String(), err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
conn, err := net.DialTCP("tcp", nil, raddr)
|
||||
if connected {
|
||||
s.model.Close(deviceID, fmt.Errorf("switching connections"))
|
||||
}
|
||||
|
||||
s.conns <- model.IntermediateConnection{
|
||||
conn, model.ConnectionTypeBasicDial,
|
||||
}
|
||||
continue nextDevice
|
||||
}
|
||||
|
||||
// Only connect via relays if not already connected
|
||||
// Also, do not set lastRelayCheck time if we have no relays,
|
||||
// as otherwise when we do discover relays, we might have to
|
||||
// wait up to RelayReconnectIntervalM to connect again.
|
||||
// Also, do not try relays if we are explicitly told not to.
|
||||
if connected || len(relays) == 0 || !relaysEnabled {
|
||||
continue nextDevice
|
||||
}
|
||||
|
||||
reconIntv := time.Duration(s.cfg.Options().RelayReconnectIntervalM) * time.Minute
|
||||
if last, ok := s.lastRelayCheck[deviceID]; ok && time.Since(last) < reconIntv {
|
||||
if debugNet {
|
||||
l.Debugln("Skipping connecting via relay to", deviceID, "last checked at", last)
|
||||
}
|
||||
continue nextDevice
|
||||
} else if debugNet {
|
||||
l.Debugln("Trying relay connections to", deviceID, relays)
|
||||
}
|
||||
|
||||
s.lastRelayCheck[deviceID] = time.Now()
|
||||
|
||||
for _, addr := range relays {
|
||||
uri, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
if debugNet {
|
||||
l.Debugln(err)
|
||||
}
|
||||
l.Infoln("Failed to parse relay connection url:", addr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
s.setTCPOptions(conn)
|
||||
inv, err := client.GetInvitationFromRelay(uri, deviceID, s.tlsCfg.Certificates)
|
||||
if err != nil {
|
||||
if debugNet {
|
||||
l.Debugf("Failed to get invitation for %s from %s: %v", deviceID, uri, err)
|
||||
}
|
||||
continue
|
||||
} else if debugNet {
|
||||
l.Debugln("Succesfully retrieved relay invitation", inv, "from", uri)
|
||||
}
|
||||
|
||||
tc := tls.Client(conn, s.tlsCfg)
|
||||
conn, err := client.JoinSession(inv)
|
||||
if err != nil {
|
||||
if debugNet {
|
||||
l.Debugf("Failed to join relay session %s: %v", inv, err)
|
||||
}
|
||||
continue
|
||||
} else if debugNet {
|
||||
l.Debugln("Sucessfully joined relay session", inv)
|
||||
}
|
||||
|
||||
err = osutil.SetTCPOptions(conn.(*net.TCPConn))
|
||||
if err != nil {
|
||||
l.Infoln(err)
|
||||
}
|
||||
|
||||
var tc *tls.Conn
|
||||
|
||||
if inv.ServerSocket {
|
||||
tc = tls.Server(conn, s.tlsCfg)
|
||||
} else {
|
||||
tc = tls.Client(conn, s.tlsCfg)
|
||||
}
|
||||
err = tc.Handshake()
|
||||
if err != nil {
|
||||
l.Infoln("TLS handshake:", err)
|
||||
l.Infof("TLS handshake (BEP/relay %s): %v", inv, err)
|
||||
tc.Close()
|
||||
continue
|
||||
}
|
||||
|
||||
s.conns <- tc
|
||||
s.conns <- model.IntermediateConnection{
|
||||
tc, model.ConnectionTypeRelayDial,
|
||||
}
|
||||
continue nextDevice
|
||||
}
|
||||
}
|
||||
@ -317,22 +376,6 @@ func (s *connectionSvc) connect() {
|
||||
}
|
||||
}
|
||||
|
||||
func (*connectionSvc) setTCPOptions(conn *net.TCPConn) {
|
||||
var err error
|
||||
if err = conn.SetLinger(0); err != nil {
|
||||
l.Infoln(err)
|
||||
}
|
||||
if err = conn.SetNoDelay(false); err != nil {
|
||||
l.Infoln(err)
|
||||
}
|
||||
if err = conn.SetKeepAlivePeriod(60 * time.Second); err != nil {
|
||||
l.Infoln(err)
|
||||
}
|
||||
if err = conn.SetKeepAlive(true); err != nil {
|
||||
l.Infoln(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *connectionSvc) shouldLimit(addr net.Addr) bool {
|
||||
if s.cfg.Options().LimitBandwidthInLan {
|
||||
return true
|
||||
@ -355,6 +398,10 @@ func (s *connectionSvc) VerifyConfiguration(from, to config.Configuration) error
|
||||
}
|
||||
|
||||
func (s *connectionSvc) CommitConfiguration(from, to config.Configuration) bool {
|
||||
s.mut.Lock()
|
||||
s.relaysEnabled = to.Options.RelaysEnabled
|
||||
s.mut.Unlock()
|
||||
|
||||
// We require a restart if a device as been removed.
|
||||
|
||||
newDevices := make(map[protocol.DeviceID]bool, len(to.Devices))
|
||||
|
105
cmd/syncthing/connections_tcp.go
Normal file
105
cmd/syncthing/connections_tcp.go
Normal file
@ -0,0 +1,105 @@
|
||||
// Copyright (C) 2015 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 http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/model"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
)
|
||||
|
||||
func init() {
|
||||
dialers["tcp"] = tcpDialer
|
||||
listeners["tcp"] = tcpListener
|
||||
}
|
||||
|
||||
func tcpDialer(uri *url.URL, tlsCfg *tls.Config) (*tls.Conn, error) {
|
||||
host, port, err := net.SplitHostPort(uri.Host)
|
||||
if err != nil && strings.HasPrefix(err.Error(), "missing port") {
|
||||
// addr is on the form "1.2.3.4"
|
||||
uri.Host = net.JoinHostPort(uri.Host, "22000")
|
||||
} else if err == nil && port == "" {
|
||||
// addr is on the form "1.2.3.4:"
|
||||
uri.Host = net.JoinHostPort(host, "22000")
|
||||
}
|
||||
|
||||
raddr, err := net.ResolveTCPAddr("tcp", uri.Host)
|
||||
if err != nil {
|
||||
if debugNet {
|
||||
l.Debugln(err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn, err := net.DialTCP("tcp", nil, raddr)
|
||||
if err != nil {
|
||||
if debugNet {
|
||||
l.Debugln(err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = osutil.SetTCPOptions(conn)
|
||||
if err != nil {
|
||||
l.Infoln(err)
|
||||
}
|
||||
|
||||
tc := tls.Client(conn, tlsCfg)
|
||||
err = tc.Handshake()
|
||||
if err != nil {
|
||||
tc.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tc, nil
|
||||
}
|
||||
|
||||
func tcpListener(uri *url.URL, tlsCfg *tls.Config, conns chan<- model.IntermediateConnection) {
|
||||
tcaddr, err := net.ResolveTCPAddr("tcp", uri.Host)
|
||||
if err != nil {
|
||||
l.Fatalln("listen (BEP/tcp):", err)
|
||||
return
|
||||
}
|
||||
listener, err := net.ListenTCP("tcp", tcaddr)
|
||||
if err != nil {
|
||||
l.Fatalln("listen (BEP/tcp):", err)
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
l.Warnln("Accepting connection (BEP/tcp):", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if debugNet {
|
||||
l.Debugln("connect from", conn.RemoteAddr())
|
||||
}
|
||||
|
||||
err = osutil.SetTCPOptions(conn.(*net.TCPConn))
|
||||
if err != nil {
|
||||
l.Infoln(err)
|
||||
}
|
||||
|
||||
tc := tls.Server(conn, tlsCfg)
|
||||
err = tc.Handshake()
|
||||
if err != nil {
|
||||
l.Infoln("TLS handshake (BEP/tcp):", err)
|
||||
tc.Close()
|
||||
continue
|
||||
}
|
||||
|
||||
conns <- model.IntermediateConnection{
|
||||
tc, model.ConnectionTypeBasicAccept,
|
||||
}
|
||||
}
|
||||
}
|
@ -628,6 +628,9 @@ func (s *apiSvc) getSystemStatus(w http.ResponseWriter, r *http.Request) {
|
||||
if cfg.Options().GlobalAnnEnabled && discoverer != nil {
|
||||
res["extAnnounceOK"] = discoverer.ExtAnnounceOK()
|
||||
}
|
||||
if relaySvc != nil {
|
||||
res["relayClientStatus"] = relaySvc.ClientStatus()
|
||||
}
|
||||
cpuUsageLock.RLock()
|
||||
var cpusum float64
|
||||
for _, p := range cpuUsagePercent {
|
||||
|
@ -34,8 +34,10 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/events"
|
||||
"github.com/syncthing/syncthing/lib/model"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/relay"
|
||||
"github.com/syncthing/syncthing/lib/symlinks"
|
||||
"github.com/syncthing/syncthing/lib/upgrade"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/errors"
|
||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||
@ -110,6 +112,7 @@ var (
|
||||
readRateLimit *ratelimit.Bucket
|
||||
stop = make(chan int)
|
||||
discoverer *discover.Discoverer
|
||||
relaySvc *relay.Svc
|
||||
cert tls.Certificate
|
||||
lans []*net.IPNet
|
||||
)
|
||||
@ -627,7 +630,7 @@ func syncthingMain() {
|
||||
m.StartDeadlockDetector(time.Duration(it) * time.Second)
|
||||
}
|
||||
} else if !IsRelease || IsBeta {
|
||||
m.StartDeadlockDetector(20 * 60 * time.Second)
|
||||
m.StartDeadlockDetector(20 * time.Minute)
|
||||
}
|
||||
|
||||
// Clear out old indexes for other devices. Otherwise we'll start up and
|
||||
@ -658,15 +661,30 @@ func syncthingMain() {
|
||||
|
||||
// The default port we announce, possibly modified by setupUPnP next.
|
||||
|
||||
addr, err := net.ResolveTCPAddr("tcp", opts.ListenAddress[0])
|
||||
uri, err := url.Parse(opts.ListenAddress[0])
|
||||
if err != nil {
|
||||
l.Fatalf("Failed to parse listen address %s: %v", opts.ListenAddress[0], err)
|
||||
}
|
||||
|
||||
addr, err := net.ResolveTCPAddr("tcp", uri.Host)
|
||||
if err != nil {
|
||||
l.Fatalln("Bad listen address:", err)
|
||||
}
|
||||
|
||||
// Start the relevant services
|
||||
|
||||
connectionSvc := newConnectionSvc(cfg, myID, m, tlsCfg)
|
||||
mainSvc.Add(connectionSvc)
|
||||
|
||||
if opts.RelaysEnabled && (opts.GlobalAnnEnabled || opts.RelayWithoutGlobalAnn) {
|
||||
relaySvc = relay.NewSvc(cfg, tlsCfg, connectionSvc.conns)
|
||||
connectionSvc.Add(relaySvc)
|
||||
}
|
||||
|
||||
// Start discovery
|
||||
|
||||
localPort := addr.Port
|
||||
discoverer = discovery(localPort)
|
||||
discoverer = discovery(localPort, relaySvc)
|
||||
|
||||
// Start UPnP. The UPnP service will restart global discovery if the
|
||||
// external port changes.
|
||||
@ -676,10 +694,6 @@ func syncthingMain() {
|
||||
mainSvc.Add(upnpSvc)
|
||||
}
|
||||
|
||||
connectionSvc := newConnectionSvc(cfg, myID, m, tlsCfg)
|
||||
cfg.Subscribe(connectionSvc)
|
||||
mainSvc.Add(connectionSvc)
|
||||
|
||||
if cpuProfile {
|
||||
f, err := os.Create(fmt.Sprintf("cpu-%d.pprof", os.Getpid()))
|
||||
if err != nil {
|
||||
@ -900,18 +914,23 @@ func shutdown() {
|
||||
stop <- exitSuccess
|
||||
}
|
||||
|
||||
func discovery(extPort int) *discover.Discoverer {
|
||||
func discovery(extPort int, relaySvc *relay.Svc) *discover.Discoverer {
|
||||
opts := cfg.Options()
|
||||
disc := discover.NewDiscoverer(myID, opts.ListenAddress)
|
||||
|
||||
disc := discover.NewDiscoverer(myID, opts.ListenAddress, relaySvc)
|
||||
if opts.LocalAnnEnabled {
|
||||
l.Infoln("Starting local discovery announcements")
|
||||
disc.StartLocal(opts.LocalAnnPort, opts.LocalAnnMCAddr)
|
||||
}
|
||||
|
||||
if opts.GlobalAnnEnabled {
|
||||
l.Infoln("Starting global discovery announcements")
|
||||
disc.StartGlobal(opts.GlobalAnnServers, uint16(extPort))
|
||||
go func() {
|
||||
// Defer starting global announce server, giving time to connect
|
||||
// to relay servers.
|
||||
time.Sleep(5 * time.Second)
|
||||
l.Infoln("Starting global discovery announcements")
|
||||
disc.StartGlobal(opts.GlobalAnnServers, uint16(extPort))
|
||||
}()
|
||||
|
||||
}
|
||||
|
||||
return disc
|
||||
|
@ -74,7 +74,7 @@ func (s *verboseSvc) formatEvent(ev events.Event) string {
|
||||
return fmt.Sprintf("Discovered device %v at %v", data["device"], data["addrs"])
|
||||
case events.DeviceConnected:
|
||||
data := ev.Data.(map[string]string)
|
||||
return fmt.Sprintf("Connected to device %v at %v", data["id"], data["addr"])
|
||||
return fmt.Sprintf("Connected to device %v at %v (type %s)", data["id"], data["addr"], data["type"])
|
||||
case events.DeviceDisconnected:
|
||||
data := ev.Data.(map[string]string)
|
||||
return fmt.Sprintf("Disconnected from device %v", data["id"])
|
||||
|
@ -49,7 +49,7 @@
|
||||
"Edit Folder": "Edit Folder",
|
||||
"Editing": "Editing",
|
||||
"Enable UPnP": "Enable UPnP",
|
||||
"Enter comma separated \"ip:port\" addresses or \"dynamic\" to perform automatic discovery of the address.": "Enter comma separated \"ip:port\" addresses or \"dynamic\" to perform automatic discovery of the address.",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.",
|
||||
"Enter ignore patterns, one per line.": "Enter ignore patterns, one per line.",
|
||||
"Error": "Error",
|
||||
"External File Versioning": "External File Versioning",
|
||||
@ -120,6 +120,8 @@
|
||||
"Quick guide to supported patterns": "Quick guide to supported patterns",
|
||||
"RAM Utilization": "RAM Utilization",
|
||||
"Random": "Random",
|
||||
"Relayed via": "Relayed via",
|
||||
"Relays": "Relays",
|
||||
"Release Notes": "Release Notes",
|
||||
"Remove": "Remove",
|
||||
"Rescan": "Rescan",
|
||||
|
@ -385,6 +385,19 @@
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="system.relayClientStatus != undefined && relayClientsTotal > 0">
|
||||
<th><span class="fa fa-fw fa-sitemap"></span> <span translate>Relays</span></th>
|
||||
<td class="text-right">
|
||||
<span ng-if="relayClientsFailed.length == 0" class="data text-success">
|
||||
<span>OK</span>
|
||||
</span>
|
||||
<span ng-if="relayClientsFailed.length != 0" class="data" ng-class="{'text-danger': relayClientsFailed.length == relayClientsTotal}">
|
||||
<span popover data-trigger="hover" data-placement="bottom" data-content="{{relayClientsFailed.join('\n')}}">
|
||||
{{relayClientsTotal-relayClientsFailed.length}}/{{relayClientsTotal}}
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><span class="fa fa-fw fa-clock-o"></span> <span translate>Uptime</span></th>
|
||||
<td class="text-right">{{system.uptime | duration:"m"}}</td>
|
||||
@ -430,7 +443,11 @@
|
||||
<td class="text-right">{{connections[deviceCfg.deviceID].outbps | binary}}B/s ({{connections[deviceCfg.deviceID].outBytesTotal | binary}}B)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><span class="fa fa-fw fa-link"></span> <span translate>Address</span></th>
|
||||
<th>
|
||||
<span class="fa fa-fw fa-link"></span>
|
||||
<span translate ng-if="connections[deviceCfg.deviceID].type.indexOf('basic') == 0" >Address</span>
|
||||
<span translate ng-if="connections[deviceCfg.deviceID].type.indexOf('relay') == 0" >Relayed via</span>
|
||||
</th>
|
||||
<td class="text-right">{{deviceAddr(deviceCfg)}}</td>
|
||||
</tr>
|
||||
<tr ng-if="deviceCfg.compression != 'metadata'">
|
||||
|
@ -176,6 +176,7 @@ angular.module('syncthing.core')
|
||||
outbps: 0,
|
||||
inBytesTotal: 0,
|
||||
outBytesTotal: 0,
|
||||
type: arg.data.type,
|
||||
address: arg.data.addr
|
||||
};
|
||||
$scope.completion[arg.data.id] = {
|
||||
@ -346,14 +347,24 @@ angular.module('syncthing.core')
|
||||
$http.get(urlbase + '/system/status').success(function (data) {
|
||||
$scope.myID = data.myID;
|
||||
$scope.system = data;
|
||||
|
||||
$scope.announceServersTotal = data.extAnnounceOK ? Object.keys(data.extAnnounceOK).length : 0;
|
||||
var failed = [];
|
||||
var failedAnnounce = [];
|
||||
for (var server in data.extAnnounceOK) {
|
||||
if (!data.extAnnounceOK[server]) {
|
||||
failed.push(server);
|
||||
failedAnnounce.push(server);
|
||||
}
|
||||
}
|
||||
$scope.announceServersFailed = failed;
|
||||
$scope.announceServersFailed = failedAnnounce;
|
||||
|
||||
$scope.relayClientsTotal = data.relayClientStatus ? Object.keys(data.relayClientStatus).length : 0;
|
||||
var failedRelays = [];
|
||||
for (var relay in data.relayClientStatus) {
|
||||
if (!data.relayClientStatus[relay]) {
|
||||
failedRelays.push(relay);
|
||||
}
|
||||
}
|
||||
$scope.relayClientsFailed = failedRelays;
|
||||
|
||||
|
||||
console.log("refreshSystem", data);
|
||||
|
@ -32,7 +32,7 @@
|
||||
<div class="form-group">
|
||||
<label translate for="addresses">Addresses</label>
|
||||
<input ng-disabled="currentDevice.deviceID == myID" id="addresses" class="form-control" type="text" ng-model="currentDevice._addressesStr"></input>
|
||||
<p translate class="help-block">Enter comma separated "ip:port" addresses or "dynamic" to perform automatic discovery of the address.</p>
|
||||
<p translate class="help-block">Enter comma separated ("tcp://ip:port", "tcp://host:port") addresses or "dynamic" to perform automatic discovery of the address.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label translate>Compression</label>
|
||||
|
File diff suppressed because one or more lines are too long
@ -9,6 +9,7 @@ package config
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
@ -26,7 +27,7 @@ import (
|
||||
|
||||
const (
|
||||
OldestHandledVersion = 5
|
||||
CurrentVersion = 11
|
||||
CurrentVersion = 12
|
||||
MaxRescanIntervalS = 365 * 24 * 60 * 60
|
||||
)
|
||||
|
||||
@ -212,15 +213,19 @@ type FolderDeviceConfiguration struct {
|
||||
}
|
||||
|
||||
type OptionsConfiguration struct {
|
||||
ListenAddress []string `xml:"listenAddress" json:"listenAddress" default:"0.0.0.0:22000"`
|
||||
GlobalAnnServers []string `xml:"globalAnnounceServer" json:"globalAnnounceServers" json:"globalAnnounceServer" default:"udp4://announce.syncthing.net:22026, udp6://announce-v6.syncthing.net:22026"`
|
||||
ListenAddress []string `xml:"listenAddress" json:"listenAddress" default:"tcp://0.0.0.0:22000"`
|
||||
GlobalAnnServers []string `xml:"globalAnnounceServer" json:"globalAnnounceServers" json:"globalAnnounceServer" default:"udp4://announce.syncthing.net:22027, udp6://announce-v6.syncthing.net:22027"`
|
||||
GlobalAnnEnabled bool `xml:"globalAnnounceEnabled" json:"globalAnnounceEnabled" default:"true"`
|
||||
LocalAnnEnabled bool `xml:"localAnnounceEnabled" json:"localAnnounceEnabled" default:"true"`
|
||||
LocalAnnPort int `xml:"localAnnouncePort" json:"localAnnouncePort" default:"21025"`
|
||||
LocalAnnMCAddr string `xml:"localAnnounceMCAddr" json:"localAnnounceMCAddr" default:"[ff32::5222]:21026"`
|
||||
RelayServers []string `xml:"relayServer" json:"relayServers" default:"dynamic+https://relays.syncthing.net"`
|
||||
MaxSendKbps int `xml:"maxSendKbps" json:"maxSendKbps"`
|
||||
MaxRecvKbps int `xml:"maxRecvKbps" json:"maxRecvKbps"`
|
||||
ReconnectIntervalS int `xml:"reconnectionIntervalS" json:"reconnectionIntervalS" default:"60"`
|
||||
RelaysEnabled bool `xml:"relaysEnabled" json:"relaysEnabled" default:"true"`
|
||||
RelayReconnectIntervalM int `xml:"relayReconnectIntervalM" json:"relayReconnectIntervalM" default:"10"`
|
||||
RelayWithoutGlobalAnn bool `xml:"relayWithoutGlobalAnn" json:"relayWithoutGlobalAnn" default:"false"`
|
||||
StartBrowser bool `xml:"startBrowser" json:"startBrowser" default:"true"`
|
||||
UPnPEnabled bool `xml:"upnpEnabled" json:"upnpEnabled" default:"true"`
|
||||
UPnPLeaseM int `xml:"upnpLeaseMinutes" json:"upnpLeaseMinutes" default:"60"`
|
||||
@ -346,6 +351,9 @@ func (cfg *Configuration) prepare(myID protocol.DeviceID) {
|
||||
}
|
||||
}
|
||||
|
||||
cfg.Options.ListenAddress = uniqueStrings(cfg.Options.ListenAddress)
|
||||
cfg.Options.GlobalAnnServers = uniqueStrings(cfg.Options.GlobalAnnServers)
|
||||
|
||||
if cfg.Version < OldestHandledVersion {
|
||||
l.Warnf("Configuration version %d is deprecated. Attempting best effort conversion, but please verify manually.", cfg.Version)
|
||||
}
|
||||
@ -369,6 +377,9 @@ func (cfg *Configuration) prepare(myID protocol.DeviceID) {
|
||||
if cfg.Version == 10 {
|
||||
convertV10V11(cfg)
|
||||
}
|
||||
if cfg.Version == 11 {
|
||||
convertV11V12(cfg)
|
||||
}
|
||||
|
||||
// Hash old cleartext passwords
|
||||
if len(cfg.GUI.Password) > 0 && cfg.GUI.Password[0] != '$' {
|
||||
@ -420,9 +431,6 @@ func (cfg *Configuration) prepare(myID protocol.DeviceID) {
|
||||
cfg.Options.ReconnectIntervalS = 5
|
||||
}
|
||||
|
||||
cfg.Options.ListenAddress = uniqueStrings(cfg.Options.ListenAddress)
|
||||
cfg.Options.GlobalAnnServers = uniqueStrings(cfg.Options.GlobalAnnServers)
|
||||
|
||||
if cfg.GUI.APIKey == "" {
|
||||
cfg.GUI.APIKey = randomString(32)
|
||||
}
|
||||
@ -467,6 +475,38 @@ func convertV10V11(cfg *Configuration) {
|
||||
cfg.Version = 11
|
||||
}
|
||||
|
||||
func convertV11V12(cfg *Configuration) {
|
||||
// Change listen address schema
|
||||
for i, addr := range cfg.Options.ListenAddress {
|
||||
if len(addr) > 0 && !strings.HasPrefix(addr, "tcp://") {
|
||||
cfg.Options.ListenAddress[i] = fmt.Sprintf("tcp://%s", addr)
|
||||
}
|
||||
}
|
||||
|
||||
for i, device := range cfg.Devices {
|
||||
for j, addr := range device.Addresses {
|
||||
if addr != "dynamic" && addr != "" {
|
||||
cfg.Devices[i].Addresses[j] = fmt.Sprintf("tcp://%s", addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use new discovery server
|
||||
for i, addr := range cfg.Options.GlobalAnnServers {
|
||||
if addr == "udp4://announce.syncthing.net:22026" {
|
||||
cfg.Options.GlobalAnnServers[i] = "udp4://announce.syncthing.net:22027"
|
||||
} else if addr == "udp6://announce-v6.syncthing.net:22026" {
|
||||
cfg.Options.GlobalAnnServers[i] = "udp6://announce-v6.syncthing.net:22027"
|
||||
} else if addr == "udp4://194.126.249.5:22026" {
|
||||
cfg.Options.GlobalAnnServers[i] = "udp4://194.126.249.5:22027"
|
||||
} else if addr == "udp6://[2001:470:28:4d6::5]:22026" {
|
||||
cfg.Options.GlobalAnnServers[i] = "udp6://[2001:470:28:4d6::5]:22027"
|
||||
}
|
||||
}
|
||||
|
||||
cfg.Version = 12
|
||||
}
|
||||
|
||||
func convertV9V10(cfg *Configuration) {
|
||||
// Enable auto normalization on existing folders.
|
||||
for i := range cfg.Folders {
|
||||
|
@ -31,15 +31,19 @@ func init() {
|
||||
|
||||
func TestDefaultValues(t *testing.T) {
|
||||
expected := OptionsConfiguration{
|
||||
ListenAddress: []string{"0.0.0.0:22000"},
|
||||
GlobalAnnServers: []string{"udp4://announce.syncthing.net:22026", "udp6://announce-v6.syncthing.net:22026"},
|
||||
ListenAddress: []string{"tcp://0.0.0.0:22000"},
|
||||
GlobalAnnServers: []string{"udp4://announce.syncthing.net:22027", "udp6://announce-v6.syncthing.net:22027"},
|
||||
GlobalAnnEnabled: true,
|
||||
LocalAnnEnabled: true,
|
||||
LocalAnnPort: 21025,
|
||||
LocalAnnMCAddr: "[ff32::5222]:21026",
|
||||
RelayServers: []string{"dynamic+https://relays.syncthing.net"},
|
||||
MaxSendKbps: 0,
|
||||
MaxRecvKbps: 0,
|
||||
ReconnectIntervalS: 60,
|
||||
RelaysEnabled: true,
|
||||
RelayReconnectIntervalM: 10,
|
||||
RelayWithoutGlobalAnn: false,
|
||||
StartBrowser: true,
|
||||
UPnPEnabled: true,
|
||||
UPnPLeaseM: 60,
|
||||
@ -100,13 +104,13 @@ func TestDeviceConfig(t *testing.T) {
|
||||
{
|
||||
DeviceID: device1,
|
||||
Name: "node one",
|
||||
Addresses: []string{"a"},
|
||||
Addresses: []string{"tcp://a"},
|
||||
Compression: protocol.CompressMetadata,
|
||||
},
|
||||
{
|
||||
DeviceID: device4,
|
||||
Name: "node two",
|
||||
Addresses: []string{"b"},
|
||||
Addresses: []string{"tcp://b"},
|
||||
Compression: protocol.CompressMetadata,
|
||||
},
|
||||
}
|
||||
@ -142,15 +146,19 @@ func TestNoListenAddress(t *testing.T) {
|
||||
|
||||
func TestOverriddenValues(t *testing.T) {
|
||||
expected := OptionsConfiguration{
|
||||
ListenAddress: []string{":23000"},
|
||||
ListenAddress: []string{"tcp://:23000"},
|
||||
GlobalAnnServers: []string{"udp4://syncthing.nym.se:22026"},
|
||||
GlobalAnnEnabled: false,
|
||||
LocalAnnEnabled: false,
|
||||
LocalAnnPort: 42123,
|
||||
LocalAnnMCAddr: "quux:3232",
|
||||
RelayServers: []string{"relay://123.123.123.123:1234", "relay://125.125.125.125:1255"},
|
||||
MaxSendKbps: 1234,
|
||||
MaxRecvKbps: 2341,
|
||||
ReconnectIntervalS: 6000,
|
||||
RelaysEnabled: false,
|
||||
RelayReconnectIntervalM: 20,
|
||||
RelayWithoutGlobalAnn: true,
|
||||
StartBrowser: false,
|
||||
UPnPEnabled: false,
|
||||
UPnPLeaseM: 90,
|
||||
@ -255,15 +263,15 @@ func TestDeviceAddressesStatic(t *testing.T) {
|
||||
expected := map[protocol.DeviceID]DeviceConfiguration{
|
||||
device1: {
|
||||
DeviceID: device1,
|
||||
Addresses: []string{"192.0.2.1", "192.0.2.2"},
|
||||
Addresses: []string{"tcp://192.0.2.1", "tcp://192.0.2.2"},
|
||||
},
|
||||
device2: {
|
||||
DeviceID: device2,
|
||||
Addresses: []string{"192.0.2.3:6070", "[2001:db8::42]:4242"},
|
||||
Addresses: []string{"tcp://192.0.2.3:6070", "tcp://[2001:db8::42]:4242"},
|
||||
},
|
||||
device3: {
|
||||
DeviceID: device3,
|
||||
Addresses: []string{"[2001:db8::44]:4444", "192.0.2.4:6090"},
|
||||
Addresses: []string{"tcp://[2001:db8::44]:4444", "tcp://192.0.2.4:6090"},
|
||||
},
|
||||
device4: {
|
||||
DeviceID: device4,
|
||||
@ -330,12 +338,12 @@ func TestIssue1750(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if cfg.Options().ListenAddress[0] != ":23000" {
|
||||
t.Errorf("%q != %q", cfg.Options().ListenAddress[0], ":23000")
|
||||
if cfg.Options().ListenAddress[0] != "tcp://:23000" {
|
||||
t.Errorf("%q != %q", cfg.Options().ListenAddress[0], "tcp://:23000")
|
||||
}
|
||||
|
||||
if cfg.Options().ListenAddress[1] != ":23001" {
|
||||
t.Errorf("%q != %q", cfg.Options().ListenAddress[1], ":23001")
|
||||
if cfg.Options().ListenAddress[1] != "tcp://:23001" {
|
||||
t.Errorf("%q != %q", cfg.Options().ListenAddress[1], "tcp://:23001")
|
||||
}
|
||||
|
||||
if cfg.Options().GlobalAnnServers[0] != "udp4://syncthing.nym.se:22026" {
|
||||
|
5
lib/config/testdata/overridenvalues.xml
vendored
5
lib/config/testdata/overridenvalues.xml
vendored
@ -7,10 +7,15 @@
|
||||
<localAnnounceEnabled>false</localAnnounceEnabled>
|
||||
<localAnnouncePort>42123</localAnnouncePort>
|
||||
<localAnnounceMCAddr>quux:3232</localAnnounceMCAddr>
|
||||
<relayServer>relay://123.123.123.123:1234</relayServer>
|
||||
<relayServer>relay://125.125.125.125:1255</relayServer>
|
||||
<parallelRequests>32</parallelRequests>
|
||||
<maxSendKbps>1234</maxSendKbps>
|
||||
<maxRecvKbps>2341</maxRecvKbps>
|
||||
<reconnectionIntervalS>6000</reconnectionIntervalS>
|
||||
<relaysEnabled>false</relaysEnabled>
|
||||
<relayReconnectIntervalM>20</relayReconnectIntervalM>
|
||||
<relayWithoutGlobalAnn>true</relayWithoutGlobalAnn>
|
||||
<startBrowser>false</startBrowser>
|
||||
<upnpEnabled>false</upnpEnabled>
|
||||
<upnpLeaseMinutes>90</upnpLeaseMinutes>
|
||||
|
13
lib/config/testdata/v12.xml
vendored
Normal file
13
lib/config/testdata/v12.xml
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<configuration version="12">
|
||||
<folder id="test" path="testdata" ro="true" ignorePerms="false" rescanIntervalS="600" autoNormalize="true">
|
||||
<device id="AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR"></device>
|
||||
<device id="P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2"></device>
|
||||
<minDiskFreePct>1</minDiskFreePct>
|
||||
</folder>
|
||||
<device id="AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR" name="node one" compression="metadata">
|
||||
<address>tcp://a</address>
|
||||
</device>
|
||||
<device id="P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2" name="node two" compression="metadata">
|
||||
<address>tcp://b</address>
|
||||
</device>
|
||||
</configuration>
|
@ -114,6 +114,21 @@ func (w *Wrapper) Subscribe(c Committer) {
|
||||
w.sMut.Unlock()
|
||||
}
|
||||
|
||||
// Unsubscribe de-registers the given handler from any future calls to
|
||||
// configuration changes
|
||||
func (w *Wrapper) Unsubscribe(c Committer) {
|
||||
w.sMut.Lock()
|
||||
for i := range w.subs {
|
||||
if w.subs[i] == c {
|
||||
copy(w.subs[i:], w.subs[i+1:])
|
||||
w.subs[len(w.subs)-1] = nil
|
||||
w.subs = w.subs[:len(w.subs)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
w.sMut.Unlock()
|
||||
}
|
||||
|
||||
// Raw returns the currently wrapped Configuration object.
|
||||
func (w *Wrapper) Raw() Configuration {
|
||||
return w.cfg
|
||||
|
@ -14,7 +14,11 @@ import (
|
||||
"github.com/syncthing/protocol"
|
||||
)
|
||||
|
||||
type Factory func(*url.URL, *Announce) (Client, error)
|
||||
type Announcer interface {
|
||||
Announcement() Announce
|
||||
}
|
||||
|
||||
type Factory func(*url.URL, Announcer) (Client, error)
|
||||
|
||||
var (
|
||||
factories = make(map[string]Factory)
|
||||
@ -26,7 +30,7 @@ func Register(proto string, factory Factory) {
|
||||
factories[proto] = factory
|
||||
}
|
||||
|
||||
func New(addr string, pkt *Announce) (Client, error) {
|
||||
func New(addr string, announcer Announcer) (Client, error) {
|
||||
uri, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -35,7 +39,7 @@ func New(addr string, pkt *Announce) (Client, error) {
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Unsupported scheme: %s", uri.Scheme)
|
||||
}
|
||||
client, err := factory(uri, pkt)
|
||||
client, err := factory(uri, announcer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -43,7 +47,7 @@ func New(addr string, pkt *Announce) (Client, error) {
|
||||
}
|
||||
|
||||
type Client interface {
|
||||
Lookup(device protocol.DeviceID) []string
|
||||
Lookup(device protocol.DeviceID) (Announce, error)
|
||||
StatusOK() bool
|
||||
Address() string
|
||||
Stop()
|
||||
|
@ -24,6 +24,14 @@ func init() {
|
||||
device, _ = protocol.DeviceIDFromString("P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2")
|
||||
}
|
||||
|
||||
type FakeAnnouncer struct {
|
||||
pkt Announce
|
||||
}
|
||||
|
||||
func (f *FakeAnnouncer) Announcement() Announce {
|
||||
return f.pkt
|
||||
}
|
||||
|
||||
func TestUDP4Success(t *testing.T) {
|
||||
conn, err := net.ListenUDP("udp4", nil)
|
||||
if err != nil {
|
||||
@ -33,18 +41,19 @@ func TestUDP4Success(t *testing.T) {
|
||||
port := conn.LocalAddr().(*net.UDPAddr).Port
|
||||
|
||||
address := fmt.Sprintf("udp4://127.0.0.1:%d", port)
|
||||
pkt := &Announce{
|
||||
pkt := Announce{
|
||||
Magic: AnnouncementMagic,
|
||||
This: Device{
|
||||
device[:],
|
||||
[]Address{{
|
||||
IP: net.IPv4(123, 123, 123, 123),
|
||||
Port: 1234,
|
||||
}},
|
||||
[]string{"tcp://123.123.123.123:1234"},
|
||||
nil,
|
||||
},
|
||||
}
|
||||
ann := &FakeAnnouncer{
|
||||
pkt: pkt,
|
||||
}
|
||||
|
||||
client, err := New(address, pkt)
|
||||
client, err := New(address, ann)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -101,7 +110,12 @@ func TestUDP4Success(t *testing.T) {
|
||||
wg := sync.NewWaitGroup()
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
addrs = client.Lookup(device)
|
||||
pkt, err := client.Lookup(device)
|
||||
if err == nil {
|
||||
for _, addr := range pkt.This.Addresses {
|
||||
addrs = append(addrs, addr)
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
@ -117,7 +131,7 @@ func TestUDP4Success(t *testing.T) {
|
||||
// Wait for the lookup to arrive, verify that the number of answers is correct
|
||||
wg.Wait()
|
||||
|
||||
if len(addrs) != 1 || addrs[0] != "123.123.123.123:1234" {
|
||||
if len(addrs) != 1 || addrs[0] != "tcp://123.123.123.123:1234" {
|
||||
t.Fatal("Wrong number of answers")
|
||||
}
|
||||
|
||||
@ -134,18 +148,19 @@ func TestUDP4Failure(t *testing.T) {
|
||||
|
||||
address := fmt.Sprintf("udp4://127.0.0.1:%d/?listenaddress=127.0.0.1&retry=5", port)
|
||||
|
||||
pkt := &Announce{
|
||||
pkt := Announce{
|
||||
Magic: AnnouncementMagic,
|
||||
This: Device{
|
||||
device[:],
|
||||
[]Address{{
|
||||
IP: net.IPv4(123, 123, 123, 123),
|
||||
Port: 1234,
|
||||
}},
|
||||
[]string{"tcp://123.123.123.123:1234"},
|
||||
nil,
|
||||
},
|
||||
}
|
||||
ann := &FakeAnnouncer{
|
||||
pkt: pkt,
|
||||
}
|
||||
|
||||
client, err := New(address, pkt)
|
||||
client, err := New(address, ann)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -197,7 +212,12 @@ func TestUDP4Failure(t *testing.T) {
|
||||
wg := sync.NewWaitGroup()
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
addrs = client.Lookup(device)
|
||||
pkt, err := client.Lookup(device)
|
||||
if err == nil {
|
||||
for _, addr := range pkt.This.Addresses {
|
||||
addrs = append(addrs, addr)
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
|
@ -20,12 +20,13 @@ import (
|
||||
|
||||
func init() {
|
||||
for _, proto := range []string{"udp", "udp4", "udp6"} {
|
||||
Register(proto, func(uri *url.URL, pkt *Announce) (Client, error) {
|
||||
Register(proto, func(uri *url.URL, announcer Announcer) (Client, error) {
|
||||
c := &UDPClient{
|
||||
wg: sync.NewWaitGroup(),
|
||||
mut: sync.NewRWMutex(),
|
||||
announcer: announcer,
|
||||
wg: sync.NewWaitGroup(),
|
||||
mut: sync.NewRWMutex(),
|
||||
}
|
||||
err := c.Start(uri, pkt)
|
||||
err := c.Start(uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -37,22 +38,20 @@ func init() {
|
||||
type UDPClient struct {
|
||||
url *url.URL
|
||||
|
||||
id protocol.DeviceID
|
||||
|
||||
stop chan struct{}
|
||||
wg sync.WaitGroup
|
||||
listenAddress *net.UDPAddr
|
||||
|
||||
globalBroadcastInterval time.Duration
|
||||
errorRetryInterval time.Duration
|
||||
announcer Announcer
|
||||
|
||||
status bool
|
||||
mut sync.RWMutex
|
||||
}
|
||||
|
||||
func (d *UDPClient) Start(uri *url.URL, pkt *Announce) error {
|
||||
func (d *UDPClient) Start(uri *url.URL) error {
|
||||
d.url = uri
|
||||
d.id = protocol.DeviceIDFromBytes(pkt.This.ID)
|
||||
d.stop = make(chan struct{})
|
||||
|
||||
params := uri.Query()
|
||||
@ -79,11 +78,11 @@ func (d *UDPClient) Start(uri *url.URL, pkt *Announce) error {
|
||||
}
|
||||
|
||||
d.wg.Add(1)
|
||||
go d.broadcast(pkt.MustMarshalXDR())
|
||||
go d.broadcast()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *UDPClient) broadcast(pkt []byte) {
|
||||
func (d *UDPClient) broadcast() {
|
||||
defer d.wg.Done()
|
||||
|
||||
conn, err := net.ListenUDP(d.url.Scheme, d.listenAddress)
|
||||
@ -126,7 +125,14 @@ func (d *UDPClient) broadcast(pkt []byte) {
|
||||
l.Debugf("discover %s: broadcast: Sending self announcement to %v", d.url, remote)
|
||||
}
|
||||
|
||||
_, err := conn.WriteTo(pkt, remote)
|
||||
ann := d.announcer.Announcement()
|
||||
pkt, err := ann.MarshalXDR()
|
||||
if err != nil {
|
||||
timer.Reset(d.errorRetryInterval)
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = conn.WriteTo(pkt, remote)
|
||||
if err != nil {
|
||||
if debug {
|
||||
l.Debugf("discover %s: broadcast: Failed to send self announcement: %s", d.url, err)
|
||||
@ -137,11 +143,13 @@ func (d *UDPClient) broadcast(pkt []byte) {
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
res := d.Lookup(d.id)
|
||||
if debug {
|
||||
l.Debugf("discover %s: broadcast: Self-lookup returned: %v", d.url, res)
|
||||
pkt, err := d.Lookup(protocol.DeviceIDFromBytes(ann.This.ID))
|
||||
if err != nil && debug {
|
||||
l.Debugf("discover %s: broadcast: Self-lookup failed: %v", d.url, err)
|
||||
} else if debug {
|
||||
l.Debugf("discover %s: broadcast: Self-lookup returned: %v", d.url, pkt.This.Addresses)
|
||||
}
|
||||
ok = len(res) > 0
|
||||
ok = len(pkt.This.Addresses) > 0
|
||||
}
|
||||
|
||||
d.mut.Lock()
|
||||
@ -157,13 +165,13 @@ func (d *UDPClient) broadcast(pkt []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *UDPClient) Lookup(device protocol.DeviceID) []string {
|
||||
func (d *UDPClient) Lookup(device protocol.DeviceID) (Announce, error) {
|
||||
extIP, err := net.ResolveUDPAddr(d.url.Scheme, d.url.Host)
|
||||
if err != nil {
|
||||
if debug {
|
||||
l.Debugf("discover %s: Lookup(%s): %s", d.url, device, err)
|
||||
}
|
||||
return nil
|
||||
return Announce{}, err
|
||||
}
|
||||
|
||||
conn, err := net.DialUDP(d.url.Scheme, d.listenAddress, extIP)
|
||||
@ -171,7 +179,7 @@ func (d *UDPClient) Lookup(device protocol.DeviceID) []string {
|
||||
if debug {
|
||||
l.Debugf("discover %s: Lookup(%s): %s", d.url, device, err)
|
||||
}
|
||||
return nil
|
||||
return Announce{}, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
@ -180,7 +188,7 @@ func (d *UDPClient) Lookup(device protocol.DeviceID) []string {
|
||||
if debug {
|
||||
l.Debugf("discover %s: Lookup(%s): %s", d.url, device, err)
|
||||
}
|
||||
return nil
|
||||
return Announce{}, err
|
||||
}
|
||||
|
||||
buf := Query{QueryMagic, device[:]}.MustMarshalXDR()
|
||||
@ -189,7 +197,7 @@ func (d *UDPClient) Lookup(device protocol.DeviceID) []string {
|
||||
if debug {
|
||||
l.Debugf("discover %s: Lookup(%s): %s", d.url, device, err)
|
||||
}
|
||||
return nil
|
||||
return Announce{}, err
|
||||
}
|
||||
|
||||
buf = make([]byte, 2048)
|
||||
@ -197,12 +205,12 @@ func (d *UDPClient) Lookup(device protocol.DeviceID) []string {
|
||||
if err != nil {
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
// Expected if the server doesn't know about requested device ID
|
||||
return nil
|
||||
return Announce{}, err
|
||||
}
|
||||
if debug {
|
||||
l.Debugf("discover %s: Lookup(%s): %s", d.url, device, err)
|
||||
}
|
||||
return nil
|
||||
return Announce{}, err
|
||||
}
|
||||
|
||||
var pkt Announce
|
||||
@ -211,18 +219,13 @@ func (d *UDPClient) Lookup(device protocol.DeviceID) []string {
|
||||
if debug {
|
||||
l.Debugf("discover %s: Lookup(%s): %s\n%s", d.url, device, err, hex.Dump(buf[:n]))
|
||||
}
|
||||
return nil
|
||||
return Announce{}, err
|
||||
}
|
||||
|
||||
var addrs []string
|
||||
for _, a := range pkt.This.Addresses {
|
||||
deviceAddr := net.JoinHostPort(net.IP(a.IP).String(), strconv.Itoa(int(a.Port)))
|
||||
addrs = append(addrs, deviceAddr)
|
||||
}
|
||||
if debug {
|
||||
l.Debugf("discover %s: Lookup(%s) result: %v", d.url, device, addrs)
|
||||
l.Debugf("discover %s: Lookup(%s) result: %v relays: %v", d.url, device, pkt.This.Addresses, pkt.This.Relays)
|
||||
}
|
||||
return addrs
|
||||
return pkt, nil
|
||||
}
|
||||
|
||||
func (d *UDPClient) Stop() {
|
||||
|
@ -10,21 +10,26 @@ import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/protocol"
|
||||
"github.com/syncthing/syncthing/lib/beacon"
|
||||
"github.com/syncthing/syncthing/lib/events"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/relay"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
)
|
||||
|
||||
type Discoverer struct {
|
||||
myID protocol.DeviceID
|
||||
listenAddrs []string
|
||||
relaySvc *relay.Svc
|
||||
localBcastIntv time.Duration
|
||||
localBcastStart time.Time
|
||||
cacheLifetime time.Duration
|
||||
@ -34,9 +39,10 @@ type Discoverer struct {
|
||||
localBcastTick <-chan time.Time
|
||||
forcedBcastTick chan time.Time
|
||||
|
||||
registryLock sync.RWMutex
|
||||
registry map[protocol.DeviceID][]CacheEntry
|
||||
lastLookup map[protocol.DeviceID]time.Time
|
||||
registryLock sync.RWMutex
|
||||
addressRegistry map[protocol.DeviceID][]CacheEntry
|
||||
relayRegistry map[protocol.DeviceID][]CacheEntry
|
||||
lastLookup map[protocol.DeviceID]time.Time
|
||||
|
||||
clients []Client
|
||||
mut sync.RWMutex
|
||||
@ -51,17 +57,19 @@ var (
|
||||
ErrIncorrectMagic = errors.New("incorrect magic number")
|
||||
)
|
||||
|
||||
func NewDiscoverer(id protocol.DeviceID, addresses []string) *Discoverer {
|
||||
func NewDiscoverer(id protocol.DeviceID, addresses []string, relaySvc *relay.Svc) *Discoverer {
|
||||
return &Discoverer{
|
||||
myID: id,
|
||||
listenAddrs: addresses,
|
||||
localBcastIntv: 30 * time.Second,
|
||||
cacheLifetime: 5 * time.Minute,
|
||||
negCacheCutoff: 3 * time.Minute,
|
||||
registry: make(map[protocol.DeviceID][]CacheEntry),
|
||||
lastLookup: make(map[protocol.DeviceID]time.Time),
|
||||
registryLock: sync.NewRWMutex(),
|
||||
mut: sync.NewRWMutex(),
|
||||
myID: id,
|
||||
listenAddrs: addresses,
|
||||
relaySvc: relaySvc,
|
||||
localBcastIntv: 30 * time.Second,
|
||||
cacheLifetime: 5 * time.Minute,
|
||||
negCacheCutoff: 3 * time.Minute,
|
||||
addressRegistry: make(map[protocol.DeviceID][]CacheEntry),
|
||||
relayRegistry: make(map[protocol.DeviceID][]CacheEntry),
|
||||
lastLookup: make(map[protocol.DeviceID]time.Time),
|
||||
registryLock: sync.NewRWMutex(),
|
||||
mut: sync.NewRWMutex(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,14 +144,13 @@ func (d *Discoverer) StartGlobal(servers []string, extPort uint16) {
|
||||
}
|
||||
|
||||
d.extPort = extPort
|
||||
pkt := d.announcementPkt()
|
||||
wg := sync.NewWaitGroup()
|
||||
clients := make(chan Client, len(servers))
|
||||
for _, address := range servers {
|
||||
wg.Add(1)
|
||||
go func(addr string) {
|
||||
defer wg.Done()
|
||||
client, err := New(addr, pkt)
|
||||
client, err := New(addr, d)
|
||||
if err != nil {
|
||||
l.Infoln("Error creating discovery client", addr, err)
|
||||
return
|
||||
@ -184,75 +191,108 @@ func (d *Discoverer) ExtAnnounceOK() map[string]bool {
|
||||
return ret
|
||||
}
|
||||
|
||||
func (d *Discoverer) Lookup(device protocol.DeviceID) []string {
|
||||
// Lookup returns a list of addresses the device is available at, as well as
|
||||
// a list of relays the device is supposed to be available on sorted by the
|
||||
// sum of latencies between this device, and the device in question.
|
||||
func (d *Discoverer) Lookup(device protocol.DeviceID) ([]string, []string) {
|
||||
d.registryLock.RLock()
|
||||
cached := d.filterCached(d.registry[device])
|
||||
cachedAddresses := d.filterCached(d.addressRegistry[device])
|
||||
cachedRelays := d.filterCached(d.relayRegistry[device])
|
||||
lastLookup := d.lastLookup[device]
|
||||
d.registryLock.RUnlock()
|
||||
|
||||
d.mut.RLock()
|
||||
defer d.mut.RUnlock()
|
||||
|
||||
if len(cached) > 0 {
|
||||
relays := make([]string, len(cachedRelays))
|
||||
for i := range cachedRelays {
|
||||
relays[i] = cachedRelays[i].Address
|
||||
}
|
||||
|
||||
if len(cachedAddresses) > 0 {
|
||||
// There are cached address entries.
|
||||
addrs := make([]string, len(cached))
|
||||
for i := range cached {
|
||||
addrs[i] = cached[i].Address
|
||||
addrs := make([]string, len(cachedAddresses))
|
||||
for i := range cachedAddresses {
|
||||
addrs[i] = cachedAddresses[i].Address
|
||||
}
|
||||
return addrs
|
||||
return addrs, relays
|
||||
}
|
||||
|
||||
if time.Since(lastLookup) < d.negCacheCutoff {
|
||||
// We have recently tried to lookup this address and failed. Lets
|
||||
// chill for a while.
|
||||
return nil
|
||||
return nil, relays
|
||||
}
|
||||
|
||||
if len(d.clients) != 0 && time.Since(d.localBcastStart) > d.localBcastIntv {
|
||||
// Only perform external lookups if we have at least one external
|
||||
// server client and one local announcement interval has passed. This is
|
||||
// to avoid finding local peers on their remote address at startup.
|
||||
results := make(chan []string, len(d.clients))
|
||||
results := make(chan Announce, len(d.clients))
|
||||
wg := sync.NewWaitGroup()
|
||||
for _, client := range d.clients {
|
||||
wg.Add(1)
|
||||
go func(c Client) {
|
||||
defer wg.Done()
|
||||
results <- c.Lookup(device)
|
||||
ann, err := c.Lookup(device)
|
||||
if err == nil {
|
||||
results <- ann
|
||||
}
|
||||
|
||||
}(client)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
close(results)
|
||||
|
||||
cached := []CacheEntry{}
|
||||
seen := make(map[string]struct{})
|
||||
cachedAddresses := []CacheEntry{}
|
||||
availableRelays := []Relay{}
|
||||
seenAddresses := make(map[string]struct{})
|
||||
seenRelays := make(map[string]struct{})
|
||||
now := time.Now()
|
||||
|
||||
var addrs []string
|
||||
for result := range results {
|
||||
for _, addr := range result {
|
||||
_, ok := seen[addr]
|
||||
for _, addr := range result.This.Addresses {
|
||||
_, ok := seenAddresses[addr]
|
||||
if !ok {
|
||||
cached = append(cached, CacheEntry{
|
||||
cachedAddresses = append(cachedAddresses, CacheEntry{
|
||||
Address: addr,
|
||||
Seen: now,
|
||||
})
|
||||
seen[addr] = struct{}{}
|
||||
seenAddresses[addr] = struct{}{}
|
||||
addrs = append(addrs, addr)
|
||||
}
|
||||
}
|
||||
|
||||
for _, relay := range result.This.Relays {
|
||||
_, ok := seenRelays[relay.Address]
|
||||
if !ok {
|
||||
availableRelays = append(availableRelays, relay)
|
||||
seenRelays[relay.Address] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
relays = addressesSortedByLatency(availableRelays)
|
||||
cachedRelays := make([]CacheEntry, len(relays))
|
||||
for i := range relays {
|
||||
cachedRelays[i] = CacheEntry{
|
||||
Address: relays[i],
|
||||
Seen: now,
|
||||
}
|
||||
}
|
||||
|
||||
d.registryLock.Lock()
|
||||
d.registry[device] = cached
|
||||
d.addressRegistry[device] = cachedAddresses
|
||||
d.relayRegistry[device] = cachedRelays
|
||||
d.lastLookup[device] = time.Now()
|
||||
d.registryLock.Unlock()
|
||||
|
||||
return addrs
|
||||
return addrs, relays
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil, relays
|
||||
}
|
||||
|
||||
func (d *Discoverer) Hint(device string, addrs []string) {
|
||||
@ -267,8 +307,8 @@ func (d *Discoverer) Hint(device string, addrs []string) {
|
||||
|
||||
func (d *Discoverer) All() map[protocol.DeviceID][]CacheEntry {
|
||||
d.registryLock.RLock()
|
||||
devices := make(map[protocol.DeviceID][]CacheEntry, len(d.registry))
|
||||
for device, addrs := range d.registry {
|
||||
devices := make(map[protocol.DeviceID][]CacheEntry, len(d.addressRegistry))
|
||||
for device, addrs := range d.addressRegistry {
|
||||
addrsCopy := make([]CacheEntry, len(addrs))
|
||||
copy(addrsCopy, addrs)
|
||||
devices[device] = addrsCopy
|
||||
@ -277,41 +317,36 @@ func (d *Discoverer) All() map[protocol.DeviceID][]CacheEntry {
|
||||
return devices
|
||||
}
|
||||
|
||||
func (d *Discoverer) announcementPkt() *Announce {
|
||||
var addrs []Address
|
||||
if d.extPort != 0 {
|
||||
addrs = []Address{{Port: d.extPort}}
|
||||
func (d *Discoverer) Announcement() Announce {
|
||||
return d.announcementPkt(true)
|
||||
}
|
||||
|
||||
func (d *Discoverer) announcementPkt(allowExternal bool) Announce {
|
||||
var addrs []string
|
||||
if d.extPort != 0 && allowExternal {
|
||||
addrs = []string{fmt.Sprintf("tcp://:%d", d.extPort)}
|
||||
} else {
|
||||
for _, astr := range d.listenAddrs {
|
||||
addr, err := net.ResolveTCPAddr("tcp", astr)
|
||||
if err != nil {
|
||||
l.Warnln("discover: %v: not announcing %s", err, astr)
|
||||
continue
|
||||
} else if debug {
|
||||
l.Debugf("discover: resolved %s as %#v", astr, addr)
|
||||
}
|
||||
if len(addr.IP) == 0 || addr.IP.IsUnspecified() {
|
||||
addrs = append(addrs, Address{Port: uint16(addr.Port)})
|
||||
} else if bs := addr.IP.To4(); bs != nil {
|
||||
addrs = append(addrs, Address{IP: bs, Port: uint16(addr.Port)})
|
||||
} else if bs := addr.IP.To16(); bs != nil {
|
||||
addrs = append(addrs, Address{IP: bs, Port: uint16(addr.Port)})
|
||||
addrs = resolveAddrs(d.listenAddrs)
|
||||
}
|
||||
|
||||
var relayAddrs []string
|
||||
if d.relaySvc != nil {
|
||||
status := d.relaySvc.ClientStatus()
|
||||
for uri, ok := range status {
|
||||
if ok {
|
||||
relayAddrs = append(relayAddrs, uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
return &Announce{
|
||||
|
||||
return Announce{
|
||||
Magic: AnnouncementMagic,
|
||||
This: Device{d.myID[:], addrs},
|
||||
This: Device{d.myID[:], addrs, measureLatency(relayAddrs)},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Discoverer) sendLocalAnnouncements() {
|
||||
var addrs = resolveAddrs(d.listenAddrs)
|
||||
|
||||
var pkt = Announce{
|
||||
Magic: AnnouncementMagic,
|
||||
This: Device{d.myID[:], addrs},
|
||||
}
|
||||
var pkt = d.announcementPkt(false)
|
||||
msg := pkt.MustMarshalXDR()
|
||||
|
||||
for {
|
||||
@ -363,19 +398,32 @@ func (d *Discoverer) registerDevice(addr net.Addr, device Device) bool {
|
||||
d.registryLock.Lock()
|
||||
defer d.registryLock.Unlock()
|
||||
|
||||
current := d.filterCached(d.registry[id])
|
||||
current := d.filterCached(d.addressRegistry[id])
|
||||
|
||||
orig := current
|
||||
|
||||
for _, a := range device.Addresses {
|
||||
var deviceAddr string
|
||||
if len(a.IP) > 0 {
|
||||
deviceAddr = net.JoinHostPort(net.IP(a.IP).String(), strconv.Itoa(int(a.Port)))
|
||||
} else if addr != nil {
|
||||
ua := addr.(*net.UDPAddr)
|
||||
ua.Port = int(a.Port)
|
||||
deviceAddr = ua.String()
|
||||
for _, deviceAddr := range device.Addresses {
|
||||
uri, err := url.Parse(deviceAddr)
|
||||
if err != nil {
|
||||
if debug {
|
||||
l.Debugf("discover: Failed to parse address %s: %s", deviceAddr, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
host, port, err := net.SplitHostPort(uri.Host)
|
||||
if err != nil {
|
||||
if debug {
|
||||
l.Debugf("discover: Failed to split address host %s: %s", deviceAddr, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if host == "" {
|
||||
uri.Host = net.JoinHostPort(addr.(*net.UDPAddr).IP.String(), port)
|
||||
deviceAddr = uri.String()
|
||||
}
|
||||
|
||||
for i := range current {
|
||||
if current[i].Address == deviceAddr {
|
||||
current[i].Seen = time.Now()
|
||||
@ -393,7 +441,7 @@ func (d *Discoverer) registerDevice(addr net.Addr, device Device) bool {
|
||||
l.Debugf("discover: Caching %s addresses: %v", id, current)
|
||||
}
|
||||
|
||||
d.registry[id] = current
|
||||
d.addressRegistry[id] = current
|
||||
|
||||
if len(current) > len(orig) {
|
||||
addrs := make([]string, len(current))
|
||||
@ -413,7 +461,7 @@ func (d *Discoverer) filterCached(c []CacheEntry) []CacheEntry {
|
||||
for i := 0; i < len(c); {
|
||||
if ago := time.Since(c[i].Seen); ago > d.cacheLifetime {
|
||||
if debug {
|
||||
l.Debugf("discover: Removing cached address %s - seen %v ago", c[i].Address, ago)
|
||||
l.Debugf("discover: Removing cached entry %s - seen %v ago", c[i].Address, ago)
|
||||
}
|
||||
c[i] = c[len(c)-1]
|
||||
c = c[:len(c)-1]
|
||||
@ -424,30 +472,99 @@ func (d *Discoverer) filterCached(c []CacheEntry) []CacheEntry {
|
||||
return c
|
||||
}
|
||||
|
||||
func addrToAddr(addr *net.TCPAddr) Address {
|
||||
func addrToAddr(addr *net.TCPAddr) string {
|
||||
if len(addr.IP) == 0 || addr.IP.IsUnspecified() {
|
||||
return Address{Port: uint16(addr.Port)}
|
||||
return fmt.Sprintf(":%d", addr.Port)
|
||||
} else if bs := addr.IP.To4(); bs != nil {
|
||||
return Address{IP: bs, Port: uint16(addr.Port)}
|
||||
return fmt.Sprintf("%s:%d", bs.String(), addr.Port)
|
||||
} else if bs := addr.IP.To16(); bs != nil {
|
||||
return Address{IP: bs, Port: uint16(addr.Port)}
|
||||
return fmt.Sprintf("[%s]:%d", bs.String(), addr.Port)
|
||||
}
|
||||
return Address{}
|
||||
return ""
|
||||
}
|
||||
|
||||
func resolveAddrs(addrs []string) []Address {
|
||||
var raddrs []Address
|
||||
func resolveAddrs(addrs []string) []string {
|
||||
var raddrs []string
|
||||
for _, addrStr := range addrs {
|
||||
addrRes, err := net.ResolveTCPAddr("tcp", addrStr)
|
||||
uri, err := url.Parse(addrStr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
addrRes, err := net.ResolveTCPAddr("tcp", uri.Host)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
addr := addrToAddr(addrRes)
|
||||
if len(addr.IP) > 0 {
|
||||
raddrs = append(raddrs, addr)
|
||||
} else {
|
||||
raddrs = append(raddrs, Address{Port: addr.Port})
|
||||
if len(addr) > 0 {
|
||||
uri.Host = addr
|
||||
raddrs = append(raddrs, uri.String())
|
||||
}
|
||||
}
|
||||
return raddrs
|
||||
}
|
||||
|
||||
func measureLatency(relayAdresses []string) []Relay {
|
||||
relays := make([]Relay, 0, len(relayAdresses))
|
||||
for i, addr := range relayAdresses {
|
||||
relay := Relay{
|
||||
Address: addr,
|
||||
Latency: int32(time.Hour / time.Millisecond),
|
||||
}
|
||||
relays = append(relays, relay)
|
||||
|
||||
if latency, err := getLatencyForURL(addr); err == nil {
|
||||
if debug {
|
||||
l.Debugf("Relay %s latency %s", addr, latency)
|
||||
}
|
||||
relays[i].Latency = int32(latency / time.Millisecond)
|
||||
} else {
|
||||
l.Debugf("Failed to get relay %s latency %s", addr, err)
|
||||
}
|
||||
}
|
||||
return relays
|
||||
}
|
||||
|
||||
// addressesSortedByLatency adds local latency to the relay, and sorts them
|
||||
// by sum latency, and returns the addresses.
|
||||
func addressesSortedByLatency(input []Relay) []string {
|
||||
relays := make([]Relay, len(input))
|
||||
copy(relays, input)
|
||||
for i, relay := range relays {
|
||||
if latency, err := getLatencyForURL(relay.Address); err == nil {
|
||||
relays[i].Latency += int32(latency / time.Millisecond)
|
||||
} else {
|
||||
relays[i].Latency += int32(time.Hour / time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(relayList(relays))
|
||||
|
||||
addresses := make([]string, 0, len(relays))
|
||||
for _, relay := range relays {
|
||||
addresses = append(addresses, relay.Address)
|
||||
}
|
||||
return addresses
|
||||
}
|
||||
|
||||
func getLatencyForURL(addr string) (time.Duration, error) {
|
||||
uri, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return osutil.TCPPing(uri.Host)
|
||||
}
|
||||
|
||||
type relayList []Relay
|
||||
|
||||
func (l relayList) Len() int {
|
||||
return len(l)
|
||||
}
|
||||
|
||||
func (l relayList) Less(a, b int) bool {
|
||||
return l[a].Latency < l[b].Latency
|
||||
}
|
||||
|
||||
func (l relayList) Swap(a, b int) {
|
||||
l[a], l[b] = l[b], l[a]
|
||||
}
|
||||
|
@ -18,15 +18,15 @@ import (
|
||||
type DummyClient struct {
|
||||
url *url.URL
|
||||
lookups []protocol.DeviceID
|
||||
lookupRet []string
|
||||
lookupRet Announce
|
||||
stops int
|
||||
statusRet bool
|
||||
statusChecks int
|
||||
}
|
||||
|
||||
func (c *DummyClient) Lookup(device protocol.DeviceID) []string {
|
||||
func (c *DummyClient) Lookup(device protocol.DeviceID) (Announce, error) {
|
||||
c.lookups = append(c.lookups, device)
|
||||
return c.lookupRet
|
||||
return c.lookupRet, nil
|
||||
}
|
||||
|
||||
func (c *DummyClient) StatusOK() bool {
|
||||
@ -45,34 +45,58 @@ func (c *DummyClient) Address() string {
|
||||
func TestGlobalDiscovery(t *testing.T) {
|
||||
c1 := &DummyClient{
|
||||
statusRet: false,
|
||||
lookupRet: []string{"test.com:1234"},
|
||||
lookupRet: Announce{
|
||||
Magic: AnnouncementMagic,
|
||||
This: Device{
|
||||
ID: protocol.LocalDeviceID[:],
|
||||
Addresses: []string{"test.com:1234"},
|
||||
Relays: nil,
|
||||
},
|
||||
Extra: nil,
|
||||
},
|
||||
}
|
||||
|
||||
c2 := &DummyClient{
|
||||
statusRet: true,
|
||||
lookupRet: []string{},
|
||||
lookupRet: Announce{
|
||||
Magic: AnnouncementMagic,
|
||||
This: Device{
|
||||
ID: protocol.LocalDeviceID[:],
|
||||
Addresses: nil,
|
||||
Relays: nil,
|
||||
},
|
||||
Extra: nil,
|
||||
},
|
||||
}
|
||||
|
||||
c3 := &DummyClient{
|
||||
statusRet: true,
|
||||
lookupRet: []string{"best.com:2345"},
|
||||
lookupRet: Announce{
|
||||
Magic: AnnouncementMagic,
|
||||
This: Device{
|
||||
ID: protocol.LocalDeviceID[:],
|
||||
Addresses: []string{"best.com:2345"},
|
||||
Relays: nil,
|
||||
},
|
||||
Extra: nil,
|
||||
},
|
||||
}
|
||||
|
||||
clients := []*DummyClient{c1, c2}
|
||||
|
||||
Register("test1", func(uri *url.URL, pkt *Announce) (Client, error) {
|
||||
Register("test1", func(uri *url.URL, ann Announcer) (Client, error) {
|
||||
c := clients[0]
|
||||
clients = clients[1:]
|
||||
c.url = uri
|
||||
return c, nil
|
||||
})
|
||||
|
||||
Register("test2", func(uri *url.URL, pkt *Announce) (Client, error) {
|
||||
Register("test2", func(uri *url.URL, ann Announcer) (Client, error) {
|
||||
c3.url = uri
|
||||
return c3, nil
|
||||
})
|
||||
|
||||
d := NewDiscoverer(device, []string{})
|
||||
d := NewDiscoverer(device, []string{}, nil)
|
||||
d.localBcastStart = time.Time{}
|
||||
servers := []string{
|
||||
"test1://123.123.123.123:1234",
|
||||
@ -93,7 +117,7 @@ func TestGlobalDiscovery(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
addrs := d.Lookup(device)
|
||||
addrs, _ := d.Lookup(device)
|
||||
if len(addrs) != 2 {
|
||||
t.Fatal("Wrong number of addresses", addrs)
|
||||
}
|
||||
@ -117,7 +141,7 @@ func TestGlobalDiscovery(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
addrs = d.Lookup(device)
|
||||
addrs, _ = d.Lookup(device)
|
||||
if len(addrs) != 2 {
|
||||
t.Fatal("Wrong number of addresses", addrs)
|
||||
}
|
||||
|
@ -10,8 +10,8 @@
|
||||
package discover
|
||||
|
||||
const (
|
||||
AnnouncementMagic = 0x9D79BC39
|
||||
QueryMagic = 0x2CA856F5
|
||||
AnnouncementMagic = 0x9D79BC40
|
||||
QueryMagic = 0x2CA856F6
|
||||
)
|
||||
|
||||
type Query struct {
|
||||
@ -25,12 +25,13 @@ type Announce struct {
|
||||
Extra []Device // max:16
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
ID []byte // max:32
|
||||
Addresses []Address // max:16
|
||||
type Relay struct {
|
||||
Address string // max:256
|
||||
Latency int32
|
||||
}
|
||||
|
||||
type Address struct {
|
||||
IP []byte // max:16
|
||||
Port uint16
|
||||
type Device struct {
|
||||
ID []byte // max:32
|
||||
Addresses []string // max:16
|
||||
Relays []Relay // max:16
|
||||
}
|
||||
|
@ -187,6 +187,80 @@ func (o *Announce) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||
|
||||
/*
|
||||
|
||||
Relay Structure:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of Address |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ Address (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Latency |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
|
||||
struct Relay {
|
||||
string Address<256>;
|
||||
int Latency;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func (o Relay) EncodeXDR(w io.Writer) (int, error) {
|
||||
var xw = xdr.NewWriter(w)
|
||||
return o.EncodeXDRInto(xw)
|
||||
}
|
||||
|
||||
func (o Relay) MarshalXDR() ([]byte, error) {
|
||||
return o.AppendXDR(make([]byte, 0, 128))
|
||||
}
|
||||
|
||||
func (o Relay) MustMarshalXDR() []byte {
|
||||
bs, err := o.MarshalXDR()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
func (o Relay) AppendXDR(bs []byte) ([]byte, error) {
|
||||
var aw = xdr.AppendWriter(bs)
|
||||
var xw = xdr.NewWriter(&aw)
|
||||
_, err := o.EncodeXDRInto(xw)
|
||||
return []byte(aw), err
|
||||
}
|
||||
|
||||
func (o Relay) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||
if l := len(o.Address); l > 256 {
|
||||
return xw.Tot(), xdr.ElementSizeExceeded("Address", l, 256)
|
||||
}
|
||||
xw.WriteString(o.Address)
|
||||
xw.WriteUint32(uint32(o.Latency))
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
func (o *Relay) DecodeXDR(r io.Reader) error {
|
||||
xr := xdr.NewReader(r)
|
||||
return o.DecodeXDRFrom(xr)
|
||||
}
|
||||
|
||||
func (o *Relay) UnmarshalXDR(bs []byte) error {
|
||||
var br = bytes.NewReader(bs)
|
||||
var xr = xdr.NewReader(br)
|
||||
return o.DecodeXDRFrom(xr)
|
||||
}
|
||||
|
||||
func (o *Relay) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||
o.Address = xr.ReadStringMax(256)
|
||||
o.Latency = int32(xr.ReadUint32())
|
||||
return xr.Error()
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Device Structure:
|
||||
|
||||
0 1 2 3
|
||||
@ -200,15 +274,24 @@ Device Structure:
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Number of Addresses |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of Addresses |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ Zero or more Address Structures \
|
||||
\ Addresses (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Number of Relays |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ Zero or more Relay Structures \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
|
||||
struct Device {
|
||||
opaque ID<32>;
|
||||
Address Addresses<16>;
|
||||
string Addresses<16>;
|
||||
Relay Relays<16>;
|
||||
}
|
||||
|
||||
*/
|
||||
@ -247,7 +330,14 @@ func (o Device) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||
}
|
||||
xw.WriteUint32(uint32(len(o.Addresses)))
|
||||
for i := range o.Addresses {
|
||||
_, err := o.Addresses[i].EncodeXDRInto(xw)
|
||||
xw.WriteString(o.Addresses[i])
|
||||
}
|
||||
if l := len(o.Relays); l > 16 {
|
||||
return xw.Tot(), xdr.ElementSizeExceeded("Relays", l, 16)
|
||||
}
|
||||
xw.WriteUint32(uint32(len(o.Relays)))
|
||||
for i := range o.Relays {
|
||||
_, err := o.Relays[i].EncodeXDRInto(xw)
|
||||
if err != nil {
|
||||
return xw.Tot(), err
|
||||
}
|
||||
@ -275,83 +365,20 @@ func (o *Device) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||
if _AddressesSize > 16 {
|
||||
return xdr.ElementSizeExceeded("Addresses", _AddressesSize, 16)
|
||||
}
|
||||
o.Addresses = make([]Address, _AddressesSize)
|
||||
o.Addresses = make([]string, _AddressesSize)
|
||||
for i := range o.Addresses {
|
||||
(&o.Addresses[i]).DecodeXDRFrom(xr)
|
||||
o.Addresses[i] = xr.ReadString()
|
||||
}
|
||||
_RelaysSize := int(xr.ReadUint32())
|
||||
if _RelaysSize < 0 {
|
||||
return xdr.ElementSizeExceeded("Relays", _RelaysSize, 16)
|
||||
}
|
||||
if _RelaysSize > 16 {
|
||||
return xdr.ElementSizeExceeded("Relays", _RelaysSize, 16)
|
||||
}
|
||||
o.Relays = make([]Relay, _RelaysSize)
|
||||
for i := range o.Relays {
|
||||
(&o.Relays[i]).DecodeXDRFrom(xr)
|
||||
}
|
||||
return xr.Error()
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Address Structure:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length of IP |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ IP (variable length) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 0x0000 | Port |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
|
||||
struct Address {
|
||||
opaque IP<16>;
|
||||
unsigned int Port;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func (o Address) EncodeXDR(w io.Writer) (int, error) {
|
||||
var xw = xdr.NewWriter(w)
|
||||
return o.EncodeXDRInto(xw)
|
||||
}
|
||||
|
||||
func (o Address) MarshalXDR() ([]byte, error) {
|
||||
return o.AppendXDR(make([]byte, 0, 128))
|
||||
}
|
||||
|
||||
func (o Address) MustMarshalXDR() []byte {
|
||||
bs, err := o.MarshalXDR()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
func (o Address) AppendXDR(bs []byte) ([]byte, error) {
|
||||
var aw = xdr.AppendWriter(bs)
|
||||
var xw = xdr.NewWriter(&aw)
|
||||
_, err := o.EncodeXDRInto(xw)
|
||||
return []byte(aw), err
|
||||
}
|
||||
|
||||
func (o Address) EncodeXDRInto(xw *xdr.Writer) (int, error) {
|
||||
if l := len(o.IP); l > 16 {
|
||||
return xw.Tot(), xdr.ElementSizeExceeded("IP", l, 16)
|
||||
}
|
||||
xw.WriteBytes(o.IP)
|
||||
xw.WriteUint16(o.Port)
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
func (o *Address) DecodeXDR(r io.Reader) error {
|
||||
xr := xdr.NewReader(r)
|
||||
return o.DecodeXDRFrom(xr)
|
||||
}
|
||||
|
||||
func (o *Address) UnmarshalXDR(bs []byte) error {
|
||||
var br = bytes.NewReader(bs)
|
||||
var xr = xdr.NewReader(br)
|
||||
return o.DecodeXDRFrom(xr)
|
||||
}
|
||||
|
||||
func (o *Address) DecodeXDRFrom(xr *xdr.Reader) error {
|
||||
o.IP = xr.ReadBytesMax(16)
|
||||
o.Port = xr.ReadUint16()
|
||||
return xr.Error()
|
||||
}
|
||||
|
52
lib/model/connection.go
Normal file
52
lib/model/connection.go
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright (C) 2015 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 http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
|
||||
"github.com/syncthing/protocol"
|
||||
)
|
||||
|
||||
type IntermediateConnection struct {
|
||||
Conn *tls.Conn
|
||||
ConnType ConnectionType
|
||||
}
|
||||
|
||||
type Connection struct {
|
||||
net.Conn
|
||||
protocol.Connection
|
||||
Type ConnectionType
|
||||
}
|
||||
|
||||
const (
|
||||
ConnectionTypeBasicAccept ConnectionType = iota
|
||||
ConnectionTypeBasicDial
|
||||
ConnectionTypeRelayAccept
|
||||
ConnectionTypeRelayDial
|
||||
)
|
||||
|
||||
type ConnectionType int
|
||||
|
||||
func (t ConnectionType) String() string {
|
||||
switch t {
|
||||
case ConnectionTypeBasicAccept:
|
||||
return "basic-accept"
|
||||
case ConnectionTypeBasicDial:
|
||||
return "basic-dial"
|
||||
case ConnectionTypeRelayAccept:
|
||||
return "relay-accept"
|
||||
case ConnectionTypeRelayDial:
|
||||
return "relay-dial"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
func (t ConnectionType) IsDirect() bool {
|
||||
return t == ConnectionTypeBasicAccept || t == ConnectionTypeBasicDial
|
||||
}
|
@ -87,10 +87,9 @@ type Model struct {
|
||||
folderStatRefs map[string]*stats.FolderStatisticsReference // folder -> statsRef
|
||||
fmut sync.RWMutex // protects the above
|
||||
|
||||
protoConn map[protocol.DeviceID]protocol.Connection
|
||||
rawConn map[protocol.DeviceID]io.Closer
|
||||
conn map[protocol.DeviceID]Connection
|
||||
deviceVer map[protocol.DeviceID]string
|
||||
pmut sync.RWMutex // protects protoConn and rawConn
|
||||
pmut sync.RWMutex // protects conn and deviceVer
|
||||
|
||||
reqValidationCache map[string]time.Time // folder / file name => time when confirmed to exist
|
||||
rvmut sync.RWMutex // protects reqValidationCache
|
||||
@ -130,8 +129,7 @@ func NewModel(cfg *config.Wrapper, id protocol.DeviceID, deviceName, clientName,
|
||||
folderIgnores: make(map[string]*ignore.Matcher),
|
||||
folderRunners: make(map[string]service),
|
||||
folderStatRefs: make(map[string]*stats.FolderStatisticsReference),
|
||||
protoConn: make(map[protocol.DeviceID]protocol.Connection),
|
||||
rawConn: make(map[protocol.DeviceID]io.Closer),
|
||||
conn: make(map[protocol.DeviceID]Connection),
|
||||
deviceVer: make(map[protocol.DeviceID]string),
|
||||
reqValidationCache: make(map[string]time.Time),
|
||||
|
||||
@ -221,6 +219,7 @@ type ConnectionInfo struct {
|
||||
protocol.Statistics
|
||||
Address string
|
||||
ClientVersion string
|
||||
Type ConnectionType
|
||||
}
|
||||
|
||||
func (info ConnectionInfo) MarshalJSON() ([]byte, error) {
|
||||
@ -229,6 +228,7 @@ func (info ConnectionInfo) MarshalJSON() ([]byte, error) {
|
||||
"inBytesTotal": info.InBytesTotal,
|
||||
"outBytesTotal": info.OutBytesTotal,
|
||||
"address": info.Address,
|
||||
"type": info.Type.String(),
|
||||
"clientVersion": info.ClientVersion,
|
||||
})
|
||||
}
|
||||
@ -243,14 +243,15 @@ func (m *Model) ConnectionStats() map[string]interface{} {
|
||||
m.fmut.RLock()
|
||||
|
||||
var res = make(map[string]interface{})
|
||||
conns := make(map[string]ConnectionInfo, len(m.protoConn))
|
||||
for device, conn := range m.protoConn {
|
||||
conns := make(map[string]ConnectionInfo, len(m.conn))
|
||||
for device, conn := range m.conn {
|
||||
ci := ConnectionInfo{
|
||||
Statistics: conn.Statistics(),
|
||||
ClientVersion: m.deviceVer[device],
|
||||
}
|
||||
if nc, ok := m.rawConn[device].(remoteAddrer); ok {
|
||||
ci.Address = nc.RemoteAddr().String()
|
||||
if addr := m.conn[device].RemoteAddr(); addr != nil {
|
||||
ci.Address = addr.String()
|
||||
ci.Type = conn.Type
|
||||
}
|
||||
|
||||
conns[device.String()] = ci
|
||||
@ -586,8 +587,12 @@ func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
|
||||
"clientVersion": cm.ClientVersion,
|
||||
}
|
||||
|
||||
if conn, ok := m.rawConn[deviceID].(*tls.Conn); ok {
|
||||
event["addr"] = conn.RemoteAddr().String()
|
||||
if conn, ok := m.conn[deviceID]; ok {
|
||||
event["type"] = conn.Type.String()
|
||||
addr := conn.RemoteAddr()
|
||||
if addr != nil {
|
||||
event["addr"] = addr.String()
|
||||
}
|
||||
}
|
||||
|
||||
m.pmut.Unlock()
|
||||
@ -693,12 +698,11 @@ func (m *Model) Close(device protocol.DeviceID, err error) {
|
||||
}
|
||||
m.fmut.RUnlock()
|
||||
|
||||
conn, ok := m.rawConn[device]
|
||||
conn, ok := m.conn[device]
|
||||
if ok {
|
||||
closeRawConn(conn)
|
||||
}
|
||||
delete(m.protoConn, device)
|
||||
delete(m.rawConn, device)
|
||||
delete(m.conn, device)
|
||||
delete(m.deviceVer, device)
|
||||
m.pmut.Unlock()
|
||||
}
|
||||
@ -860,7 +864,7 @@ func (cf cFiler) CurrentFile(file string) (protocol.FileInfo, bool) {
|
||||
// ConnectedTo returns true if we are connected to the named device.
|
||||
func (m *Model) ConnectedTo(deviceID protocol.DeviceID) bool {
|
||||
m.pmut.RLock()
|
||||
_, ok := m.protoConn[deviceID]
|
||||
_, ok := m.conn[deviceID]
|
||||
m.pmut.RUnlock()
|
||||
if ok {
|
||||
m.deviceWasSeen(deviceID)
|
||||
@ -927,28 +931,24 @@ func (m *Model) SetIgnores(folder string, content []string) error {
|
||||
// AddConnection adds a new peer connection to the model. An initial index will
|
||||
// be sent to the connected peer, thereafter index updates whenever the local
|
||||
// folder changes.
|
||||
func (m *Model) AddConnection(rawConn io.Closer, protoConn protocol.Connection) {
|
||||
deviceID := protoConn.ID()
|
||||
func (m *Model) AddConnection(conn Connection) {
|
||||
deviceID := conn.ID()
|
||||
|
||||
m.pmut.Lock()
|
||||
if _, ok := m.protoConn[deviceID]; ok {
|
||||
if _, ok := m.conn[deviceID]; ok {
|
||||
panic("add existing device")
|
||||
}
|
||||
m.protoConn[deviceID] = protoConn
|
||||
if _, ok := m.rawConn[deviceID]; ok {
|
||||
panic("add existing device")
|
||||
}
|
||||
m.rawConn[deviceID] = rawConn
|
||||
m.conn[deviceID] = conn
|
||||
|
||||
protoConn.Start()
|
||||
conn.Start()
|
||||
|
||||
cm := m.clusterConfig(deviceID)
|
||||
protoConn.ClusterConfig(cm)
|
||||
conn.ClusterConfig(cm)
|
||||
|
||||
m.fmut.RLock()
|
||||
for _, folder := range m.deviceFolders[deviceID] {
|
||||
fs := m.folderFiles[folder]
|
||||
go sendIndexes(protoConn, folder, fs, m.folderIgnores[folder])
|
||||
go sendIndexes(conn, folder, fs, m.folderIgnores[folder])
|
||||
}
|
||||
m.fmut.RUnlock()
|
||||
m.pmut.Unlock()
|
||||
@ -1114,7 +1114,7 @@ func (m *Model) updateLocals(folder string, fs []protocol.FileInfo) {
|
||||
|
||||
func (m *Model) requestGlobal(deviceID protocol.DeviceID, folder, name string, offset int64, size int, hash []byte, flags uint32, options []protocol.Option) ([]byte, error) {
|
||||
m.pmut.RLock()
|
||||
nc, ok := m.protoConn[deviceID]
|
||||
nc, ok := m.conn[deviceID]
|
||||
m.pmut.RUnlock()
|
||||
|
||||
if !ok {
|
||||
@ -1640,7 +1640,7 @@ func (m *Model) Availability(folder, file string) []protocol.DeviceID {
|
||||
|
||||
availableDevices := []protocol.DeviceID{}
|
||||
for _, device := range fs.Availability(file) {
|
||||
_, ok := m.protoConn[device]
|
||||
_, ok := m.conn[device]
|
||||
if ok {
|
||||
availableDevices = append(availableDevices, device)
|
||||
}
|
||||
@ -1764,7 +1764,7 @@ func (m *Model) CommitConfiguration(from, to config.Configuration) bool {
|
||||
// folder.
|
||||
m.pmut.Lock()
|
||||
for _, dev := range cfg.DeviceIDs() {
|
||||
if conn, ok := m.rawConn[dev]; ok {
|
||||
if conn, ok := m.conn[dev]; ok {
|
||||
closeRawConn(conn)
|
||||
}
|
||||
}
|
||||
@ -1812,7 +1812,7 @@ func (m *Model) CommitConfiguration(from, to config.Configuration) bool {
|
||||
// disconnect it so that we start sharing the folder with it.
|
||||
// We close the underlying connection and let the normal error
|
||||
// handling kick in to clean up and reconnect.
|
||||
if conn, ok := m.rawConn[dev]; ok {
|
||||
if conn, ok := m.conn[dev]; ok {
|
||||
closeRawConn(conn)
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@ -281,7 +282,11 @@ func BenchmarkRequest(b *testing.B) {
|
||||
id: device1,
|
||||
requestData: []byte("some data to return"),
|
||||
}
|
||||
m.AddConnection(fc, fc)
|
||||
m.AddConnection(Connection{
|
||||
&net.TCPConn{},
|
||||
fc,
|
||||
ConnectionTypeBasicAccept,
|
||||
})
|
||||
m.Index(device1, "default", files, 0, nil)
|
||||
|
||||
b.ResetTimer()
|
||||
@ -314,6 +319,18 @@ func TestDeviceRename(t *testing.T) {
|
||||
|
||||
db, _ := leveldb.Open(storage.NewMemStorage(), nil)
|
||||
m := NewModel(cfg, protocol.LocalDeviceID, "device", "syncthing", "dev", db)
|
||||
|
||||
fc := FakeConnection{
|
||||
id: device1,
|
||||
requestData: []byte("some data to return"),
|
||||
}
|
||||
|
||||
m.AddConnection(Connection{
|
||||
&net.TCPConn{},
|
||||
fc,
|
||||
ConnectionTypeBasicAccept,
|
||||
})
|
||||
|
||||
m.ServeBackground()
|
||||
if cfg.Devices()[device1].Name != "" {
|
||||
t.Errorf("Device already has a name")
|
||||
|
@ -11,10 +11,12 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/calmh/du"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
@ -221,3 +223,21 @@ func DiskFreePercentage(path string) (freePct float64, err error) {
|
||||
u, err := du.Get(path)
|
||||
return (float64(u.FreeBytes) / float64(u.TotalBytes)) * 100, err
|
||||
}
|
||||
|
||||
// SetTCPOptions sets syncthings default TCP options on a TCP connection
|
||||
func SetTCPOptions(conn *net.TCPConn) error {
|
||||
var err error
|
||||
if err = conn.SetLinger(0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = conn.SetNoDelay(false); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = conn.SetKeepAlivePeriod(60 * time.Second); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = conn.SetKeepAlive(true); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
27
lib/osutil/ping.go
Normal file
27
lib/osutil/ping.go
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright (C) 2015 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 http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package osutil
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TCPPing returns the duration required to establish a TCP connection
|
||||
// to the given host. ICMP packets require root priviledges, hence why we use
|
||||
// tcp.
|
||||
func TCPPing(address string) (time.Duration, error) {
|
||||
dialer := net.Dialer{
|
||||
Deadline: time.Now().Add(time.Second),
|
||||
}
|
||||
start := time.Now()
|
||||
conn, err := dialer.Dial("tcp", address)
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
return time.Since(start), err
|
||||
}
|
19
lib/relay/debug.go
Normal file
19
lib/relay/debug.go
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright (C) 2015 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 http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package relay
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/calmh/logger"
|
||||
)
|
||||
|
||||
var (
|
||||
debug = strings.Contains(os.Getenv("STTRACE"), "relay") || os.Getenv("STTRACE") == "all"
|
||||
l = logger.DefaultLogger
|
||||
)
|
250
lib/relay/relay.go
Normal file
250
lib/relay/relay.go
Normal file
@ -0,0 +1,250 @@
|
||||
// Copyright (C) 2015 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 http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package relay
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/relaysrv/client"
|
||||
"github.com/syncthing/relaysrv/protocol"
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
"github.com/syncthing/syncthing/lib/model"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
|
||||
"github.com/thejerf/suture"
|
||||
)
|
||||
|
||||
func NewSvc(cfg *config.Wrapper, tlsCfg *tls.Config, conns chan<- model.IntermediateConnection) *Svc {
|
||||
svc := &Svc{
|
||||
Supervisor: suture.New("Svc", suture.Spec{
|
||||
Log: func(log string) {
|
||||
if debug {
|
||||
l.Infoln(log)
|
||||
}
|
||||
},
|
||||
FailureBackoff: 5 * time.Minute,
|
||||
FailureDecay: float64((10 * time.Minute) / time.Second),
|
||||
FailureThreshold: 5,
|
||||
}),
|
||||
cfg: cfg,
|
||||
tlsCfg: tlsCfg,
|
||||
|
||||
tokens: make(map[string]suture.ServiceToken),
|
||||
clients: make(map[string]*client.ProtocolClient),
|
||||
mut: sync.NewRWMutex(),
|
||||
|
||||
invitations: make(chan protocol.SessionInvitation),
|
||||
}
|
||||
|
||||
rcfg := cfg.Raw()
|
||||
svc.CommitConfiguration(rcfg, rcfg)
|
||||
cfg.Subscribe(svc)
|
||||
|
||||
receiver := &invitationReceiver{
|
||||
tlsCfg: tlsCfg,
|
||||
conns: conns,
|
||||
invitations: svc.invitations,
|
||||
}
|
||||
|
||||
svc.receiverToken = svc.Add(receiver)
|
||||
|
||||
return svc
|
||||
}
|
||||
|
||||
type Svc struct {
|
||||
*suture.Supervisor
|
||||
cfg *config.Wrapper
|
||||
tlsCfg *tls.Config
|
||||
|
||||
receiverToken suture.ServiceToken
|
||||
tokens map[string]suture.ServiceToken
|
||||
clients map[string]*client.ProtocolClient
|
||||
mut sync.RWMutex
|
||||
invitations chan protocol.SessionInvitation
|
||||
}
|
||||
|
||||
func (s *Svc) VerifyConfiguration(from, to config.Configuration) error {
|
||||
for _, addr := range to.Options.RelayServers {
|
||||
_, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Svc) CommitConfiguration(from, to config.Configuration) bool {
|
||||
existing := make(map[string]*url.URL, len(to.Options.RelayServers))
|
||||
|
||||
for _, addr := range to.Options.RelayServers {
|
||||
uri, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
if debug {
|
||||
l.Debugln("Failed to parse relay address", addr, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
existing[uri.String()] = uri
|
||||
}
|
||||
|
||||
// Expand dynamic addresses into a set of relays
|
||||
for key, uri := range existing {
|
||||
if uri.Scheme != "dynamic+http" && uri.Scheme != "dynamic+https" {
|
||||
continue
|
||||
}
|
||||
delete(existing, key)
|
||||
|
||||
uri.Scheme = uri.Scheme[8:]
|
||||
|
||||
data, err := http.Get(uri.String())
|
||||
if err != nil {
|
||||
if debug {
|
||||
l.Debugln("Failed to lookup dynamic relays", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
var ann dynamicAnnouncement
|
||||
err = json.NewDecoder(data.Body).Decode(&ann)
|
||||
data.Body.Close()
|
||||
if err != nil {
|
||||
if debug {
|
||||
l.Debugln("Failed to lookup dynamic relays", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
for _, relayAnn := range ann.Relays {
|
||||
ruri, err := url.Parse(relayAnn.URL)
|
||||
if err != nil {
|
||||
if debug {
|
||||
l.Debugln("Failed to parse dynamic relay address", relayAnn.URL, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if debug {
|
||||
l.Debugln("Found", ruri, "via", uri)
|
||||
}
|
||||
existing[ruri.String()] = ruri
|
||||
}
|
||||
}
|
||||
|
||||
for key, uri := range existing {
|
||||
_, ok := s.tokens[key]
|
||||
if !ok {
|
||||
if debug {
|
||||
l.Debugln("Connecting to relay", uri)
|
||||
}
|
||||
c := client.NewProtocolClient(uri, s.tlsCfg.Certificates, s.invitations)
|
||||
s.tokens[key] = s.Add(c)
|
||||
s.mut.Lock()
|
||||
s.clients[key] = c
|
||||
s.mut.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
for key, token := range s.tokens {
|
||||
_, ok := existing[key]
|
||||
if !ok {
|
||||
err := s.Remove(token)
|
||||
delete(s.tokens, key)
|
||||
s.mut.Lock()
|
||||
delete(s.clients, key)
|
||||
s.mut.Unlock()
|
||||
if debug {
|
||||
l.Debugln("Disconnecting from relay", key, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Svc) ClientStatus() map[string]bool {
|
||||
s.mut.RLock()
|
||||
status := make(map[string]bool, len(s.clients))
|
||||
for uri, client := range s.clients {
|
||||
status[uri] = client.StatusOK()
|
||||
}
|
||||
s.mut.RUnlock()
|
||||
return status
|
||||
}
|
||||
|
||||
type invitationReceiver struct {
|
||||
invitations chan protocol.SessionInvitation
|
||||
tlsCfg *tls.Config
|
||||
conns chan<- model.IntermediateConnection
|
||||
stop chan struct{}
|
||||
}
|
||||
|
||||
func (r *invitationReceiver) Serve() {
|
||||
if r.stop != nil {
|
||||
return
|
||||
}
|
||||
r.stop = make(chan struct{})
|
||||
|
||||
for {
|
||||
select {
|
||||
case inv := <-r.invitations:
|
||||
if debug {
|
||||
l.Debugln("Received relay invitation", inv)
|
||||
}
|
||||
conn, err := client.JoinSession(inv)
|
||||
if err != nil {
|
||||
if debug {
|
||||
l.Debugf("Failed to join relay session %s: %v", inv, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
err = osutil.SetTCPOptions(conn.(*net.TCPConn))
|
||||
if err != nil {
|
||||
l.Infoln(err)
|
||||
}
|
||||
|
||||
var tc *tls.Conn
|
||||
|
||||
if inv.ServerSocket {
|
||||
tc = tls.Server(conn, r.tlsCfg)
|
||||
} else {
|
||||
tc = tls.Client(conn, r.tlsCfg)
|
||||
}
|
||||
err = tc.Handshake()
|
||||
if err != nil {
|
||||
l.Infof("TLS handshake (BEP/relay %s): %v", inv, err)
|
||||
tc.Close()
|
||||
continue
|
||||
}
|
||||
r.conns <- model.IntermediateConnection{
|
||||
tc, model.ConnectionTypeRelayAccept,
|
||||
}
|
||||
case <-r.stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *invitationReceiver) Stop() {
|
||||
if r.stop == nil {
|
||||
return
|
||||
}
|
||||
r.stop <- struct{}{}
|
||||
r.stop = nil
|
||||
}
|
||||
|
||||
type dynamicAnnouncement struct {
|
||||
Relays []relayAnnouncement `json:"relays"`
|
||||
}
|
||||
|
||||
type relayAnnouncement struct {
|
||||
URL string `json:"url"`
|
||||
}
|
Loading…
Reference in New Issue
Block a user