From 34c04babbe2b361af9dfd843d0664fd4d062c40f Mon Sep 17 00:00:00 2001 From: Audrius Butkevicius Date: Tue, 23 Jun 2015 13:55:30 +0100 Subject: [PATCH 01/11] Large refactoring/feature commit 1. Change listen addresses to URIs 2. Break out connectionSvc to support listeners and dialers based on schema 3. Add relay announcement and lookups part of discovery service --- cmd/stfinddevice/main.go | 14 +- cmd/syncthing/connections.go | 155 ++++------ cmd/syncthing/connections_tcp.go | 95 +++++++ cmd/syncthing/main.go | 9 +- gui/syncthing/device/editDeviceModalView.html | 2 +- lib/auto/gui.files.go | 6 +- lib/config/config.go | 49 +++- lib/config/config_test.go | 25 +- lib/config/testdata/overridenvalues.xml | 2 + lib/config/testdata/v12.xml | 13 + lib/discover/client.go | 2 +- lib/discover/client_test.go | 28 +- lib/discover/client_udp.go | 35 ++- lib/discover/discover.go | 268 +++++++++++++----- lib/discover/discover_test.go | 42 ++- lib/discover/packets.go | 17 +- lib/discover/packets_xdr.go | 157 +++++----- lib/osutil/ping.go | 27 ++ 18 files changed, 641 insertions(+), 305 deletions(-) create mode 100644 cmd/syncthing/connections_tcp.go create mode 100644 lib/config/testdata/v12.xml create mode 100644 lib/osutil/ping.go diff --git a/cmd/stfinddevice/main.go b/cmd/stfinddevice/main.go index 165c85e7153..c0cee884be5 100644 --- a/cmd/stfinddevice/main.go +++ b/cmd/stfinddevice/main.go @@ -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\"] ", os.Args[0]) + log.Printf("Usage: %s [-server=\"udp4://announce.syncthing.net:22027\"] ", 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) } } diff --git a/cmd/syncthing/connections.go b/cmd/syncthing/connections.go index b0774681e38..35a383e00ea 100644 --- a/cmd/syncthing/connections.go +++ b/cmd/syncthing/connections.go @@ -11,7 +11,7 @@ import ( "fmt" "io" "net" - "strings" + "net/url" "time" "github.com/syncthing/protocol" @@ -21,6 +21,14 @@ import ( "github.com/thejerf/suture" ) +type DialerFactory func(*url.URL, *tls.Config) (*tls.Conn, error) +type ListenerFactory func(*url.URL, *tls.Config, chan<- *tls.Conn) + +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 { @@ -51,9 +59,9 @@ func newConnectionSvc(cfg *config.Wrapper, myID protocol.DeviceID, model *model. // // +-----------------+ // Incoming | +---------------+-+ +-----------------+ - // Connections | | | | | Outgoing - // -------------->| | svc.listen | | | Connections - // | | (1 per listen | | svc.connect |--------------> + // Connections | | | | | + // -------------->| | listener | | | Outgoing connections via dialers + // | | (1 per listen | | svc.connect |-----------------------------------> // | | address) | | | // +-+ | | | // +-----------------+ +-----------------+ @@ -79,11 +87,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)) @@ -197,46 +219,6 @@ next: } } -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 - } -} - func (s *connectionSvc) connect() { delay := time.Second for { @@ -254,7 +236,7 @@ func (s *connectionSvc) connect() { for _, addr := range deviceCfg.Addresses { if addr == "dynamic" { if discoverer != nil { - t := discoverer.Lookup(deviceID) + t, _ := discoverer.Lookup(deviceID) if len(t) == 0 { continue } @@ -266,45 +248,30 @@ 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") - } - if debugNet { - l.Debugln("dial", deviceCfg.DeviceID, addr) - } - - raddr, err := net.ResolveTCPAddr("tcp", addr) + uri, err := url.Parse(addr) if err != nil { - if debugNet { - l.Debugln(err) - } + l.Infoln("Failed to parse connection url:", addr, err) continue } - conn, err := net.DialTCP("tcp", nil, raddr) - if err != nil { - if debugNet { - l.Debugln(err) - } + dialer, ok := dialers[uri.Scheme] + if !ok { + l.Infoln("Unknown address schema", uri.String()) continue } - s.setTCPOptions(conn) - - tc := tls.Client(conn, s.tlsCfg) - err = tc.Handshake() + if debugNet { + l.Debugln("dial", deviceCfg.DeviceID, uri.String()) + } + conn, err := dialer(uri, s.tlsCfg) if err != nil { - l.Infoln("TLS handshake:", err) - tc.Close() + if debugNet { + l.Debugln("dial failed", deviceCfg.DeviceID, uri.String(), err) + } continue } - s.conns <- tc + s.conns <- conn continue nextDevice } } @@ -317,22 +284,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 @@ -370,3 +321,19 @@ func (s *connectionSvc) CommitConfiguration(from, to config.Configuration) bool return true } + +func 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) + } +} diff --git a/cmd/syncthing/connections_tcp.go b/cmd/syncthing/connections_tcp.go new file mode 100644 index 00000000000..10f2ebed8ce --- /dev/null +++ b/cmd/syncthing/connections_tcp.go @@ -0,0 +1,95 @@ +// 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" +) + +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 + } + + setTCPOptions(conn) + + 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<- *tls.Conn) { + 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()) + } + + tcpConn := conn.(*net.TCPConn) + setTCPOptions(tcpConn) + + tc := tls.Server(conn, tlsCfg) + err = tc.Handshake() + if err != nil { + l.Infoln("TLS handshake (BEP/tcp):", err) + tc.Close() + continue + } + + conns <- tc + } +} diff --git a/cmd/syncthing/main.go b/cmd/syncthing/main.go index 9c2c1ce6006..ffa0dc8da22 100644 --- a/cmd/syncthing/main.go +++ b/cmd/syncthing/main.go @@ -658,7 +658,12 @@ 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) } @@ -902,7 +907,7 @@ func shutdown() { func discovery(extPort int) *discover.Discoverer { opts := cfg.Options() - disc := discover.NewDiscoverer(myID, opts.ListenAddress) + disc := discover.NewDiscoverer(myID, opts.ListenAddress, opts.RelayServers) if opts.LocalAnnEnabled { l.Infoln("Starting local discovery announcements") diff --git a/gui/syncthing/device/editDeviceModalView.html b/gui/syncthing/device/editDeviceModalView.html index 85cab08c134..0606d133755 100644 --- a/gui/syncthing/device/editDeviceModalView.html +++ b/gui/syncthing/device/editDeviceModalView.html @@ -32,7 +32,7 @@