Bladeren bron

Expose connection type and relay status in the UI

Audrius Butkevicius 10 jaren geleden
bovenliggende
commit
8f2db99c86

+ 33 - 51
cmd/syncthing/connections.go

@@ -16,17 +16,17 @@ import (
 	"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<- intermediateConnection)
+type ListenerFactory func(*url.URL, *tls.Config, chan<- model.IntermediateConnection)
 
 var (
 	dialers   = make(map[string]DialerFactory, 0)
@@ -41,7 +41,7 @@ type connectionSvc struct {
 	myID   protocol.DeviceID
 	model  *model.Model
 	tlsCfg *tls.Config
-	conns  chan intermediateConnection
+	conns  chan model.IntermediateConnection
 
 	lastRelayCheck map[protocol.DeviceID]time.Time
 
@@ -49,11 +49,6 @@ type connectionSvc struct {
 	connType map[protocol.DeviceID]model.ConnectionType
 }
 
-type intermediateConnection struct {
-	conn     *tls.Conn
-	connType model.ConnectionType
-}
-
 func newConnectionSvc(cfg *config.Wrapper, myID protocol.DeviceID, mdl *model.Model, tlsCfg *tls.Config) *connectionSvc {
 	svc := &connectionSvc{
 		Supervisor: suture.NewSimple("connectionSvc"),
@@ -61,7 +56,7 @@ func newConnectionSvc(cfg *config.Wrapper, myID protocol.DeviceID, mdl *model.Mo
 		myID:       myID,
 		model:      mdl,
 		tlsCfg:     tlsCfg,
-		conns:      make(chan intermediateConnection),
+		conns:      make(chan model.IntermediateConnection),
 
 		connType:       make(map[protocol.DeviceID]model.ConnectionType),
 		lastRelayCheck: make(map[protocol.DeviceID]time.Time),
@@ -110,14 +105,14 @@ func newConnectionSvc(cfg *config.Wrapper, myID protocol.DeviceID, mdl *model.Mo
 func (s *connectionSvc) handle() {
 next:
 	for c := range s.conns {
-		cs := c.conn.ConnectionState()
+		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", c.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
@@ -125,8 +120,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, c.conn.RemoteAddr())
-			c.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]
@@ -137,7 +132,7 @@ next:
 		// clients between the same NAT gateway, and global discovery.
 		if remoteID == myID {
 			l.Infof("Connected to myself (%s) - should not happen", remoteID)
-			c.conn.Close()
+			c.Conn.Close()
 			continue
 		}
 
@@ -146,7 +141,7 @@ next:
 		s.mut.RLock()
 		ct, ok := s.connType[remoteID]
 		s.mut.RUnlock()
-		if ok && !ct.IsDirect() && c.connType.IsDirect() {
+		if ok && !ct.IsDirect() && c.ConnType.IsDirect() {
 			if debugNet {
 				l.Debugln("Switching connections", remoteID)
 			}
@@ -159,7 +154,7 @@ next:
 			// 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)
-			c.conn.Close()
+			c.Conn.Close()
 			continue
 		}
 
@@ -177,41 +172,41 @@ 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, c.conn.RemoteAddr(), err)
-					c.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(c.conn.RemoteAddr())
+				limit := s.shouldLimit(c.Conn.RemoteAddr())
 
-				wr := io.Writer(c.conn)
+				wr := io.Writer(c.Conn)
 				if limit && writeRateLimit != nil {
-					wr = &limitedWriter{c.conn, writeRateLimit}
+					wr = &limitedWriter{c.Conn, writeRateLimit}
 				}
 
-				rd := io.Reader(c.conn)
+				rd := io.Reader(c.Conn)
 				if limit && readRateLimit != nil {
-					rd = &limitedReader{c.conn, readRateLimit}
+					rd = &limitedReader{c.Conn, readRateLimit}
 				}
 
-				name := fmt.Sprintf("%s-%s (%s)", c.conn.LocalAddr(), c.conn.RemoteAddr(), c.connType)
+				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", c.conn.ConnectionState().CipherSuite, !limit)
+					l.Debugf("cipher suite: %04X in lan: %t", c.Conn.ConnectionState().CipherSuite, !limit)
 				}
 
 				s.model.AddConnection(model.Connection{
-					c.conn,
+					c.Conn,
 					protoConn,
-					c.connType,
+					c.ConnType,
 				})
 				s.mut.Lock()
-				s.connType[remoteID] = c.connType
+				s.connType[remoteID] = c.ConnType
 				s.mut.Unlock()
 				continue next
 			}
@@ -220,14 +215,14 @@ next:
 		if !s.cfg.IgnoredDevice(remoteID) {
 			events.Default.Log(events.DeviceRejected, map[string]string{
 				"device":  remoteID.String(),
-				"address": c.conn.RemoteAddr().String(),
+				"address": c.Conn.RemoteAddr().String(),
 			})
-			l.Infof("Connection from %s (%s) with unknown device ID %s", c.conn.RemoteAddr(), c.connType, remoteID)
+			l.Infof("Connection from %s (%s) with unknown device ID %s", c.Conn.RemoteAddr(), c.ConnType, remoteID)
 		} else {
-			l.Infof("Connection from %s (%s) with ignored device ID %s", c.conn.RemoteAddr(), c.connType, remoteID)
+			l.Infof("Connection from %s (%s) with ignored device ID %s", c.Conn.RemoteAddr(), c.ConnType, remoteID)
 		}
 
-		c.conn.Close()
+		c.Conn.Close()
 	}
 }
 
@@ -294,7 +289,7 @@ func (s *connectionSvc) connect() {
 					s.model.Close(deviceID, fmt.Errorf("switching connections"))
 				}
 
-				s.conns <- intermediateConnection{
+				s.conns <- model.IntermediateConnection{
 					conn, model.ConnectionTypeBasicDial,
 				}
 				continue nextDevice
@@ -347,7 +342,10 @@ func (s *connectionSvc) connect() {
 					l.Debugln("Sucessfully joined relay session", inv)
 				}
 
-				setTCPOptions(conn.(*net.TCPConn))
+				err = osutil.SetTCPOptions(conn.(*net.TCPConn))
+				if err != nil {
+					l.Infoln(err)
+				}
 
 				var tc *tls.Conn
 
@@ -362,7 +360,7 @@ func (s *connectionSvc) connect() {
 					tc.Close()
 					continue
 				}
-				s.conns <- intermediateConnection{
+				s.conns <- model.IntermediateConnection{
 					tc, model.ConnectionTypeRelayDial,
 				}
 				continue nextDevice
@@ -414,19 +412,3 @@ 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)
-	}
-}

+ 11 - 5
cmd/syncthing/connections_tcp.go

@@ -13,6 +13,7 @@ import (
 	"strings"
 
 	"github.com/syncthing/syncthing/lib/model"
+	"github.com/syncthing/syncthing/lib/osutil"
 )
 
 func init() {
@@ -46,7 +47,10 @@ func tcpDialer(uri *url.URL, tlsCfg *tls.Config) (*tls.Conn, error) {
 		return nil, err
 	}
 
-	setTCPOptions(conn)
+	err = osutil.SetTCPOptions(conn)
+	if err != nil {
+		l.Infoln(err)
+	}
 
 	tc := tls.Client(conn, tlsCfg)
 	err = tc.Handshake()
@@ -58,7 +62,7 @@ func tcpDialer(uri *url.URL, tlsCfg *tls.Config) (*tls.Conn, error) {
 	return tc, nil
 }
 
-func tcpListener(uri *url.URL, tlsCfg *tls.Config, conns chan<- intermediateConnection) {
+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)
@@ -81,8 +85,10 @@ func tcpListener(uri *url.URL, tlsCfg *tls.Config, conns chan<- intermediateConn
 			l.Debugln("connect from", conn.RemoteAddr())
 		}
 
-		tcpConn := conn.(*net.TCPConn)
-		setTCPOptions(tcpConn)
+		err = osutil.SetTCPOptions(conn.(*net.TCPConn))
+		if err != nil {
+			l.Infoln(err)
+		}
 
 		tc := tls.Server(conn, tlsCfg)
 		err = tc.Handshake()
@@ -92,7 +98,7 @@ func tcpListener(uri *url.URL, tlsCfg *tls.Config, conns chan<- intermediateConn
 			continue
 		}
 
-		conns <- intermediateConnection{
+		conns <- model.IntermediateConnection{
 			tc, model.ConnectionTypeBasicAccept,
 		}
 	}

+ 3 - 0
cmd/syncthing/gui.go

@@ -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 {

+ 7 - 5
cmd/syncthing/main.go

@@ -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
 )
@@ -671,14 +674,14 @@ func syncthingMain() {
 	// Start the relevant services
 
 	connectionSvc := newConnectionSvc(cfg, myID, m, tlsCfg)
-	relaySvc := newRelaySvc(cfg, tlsCfg, connectionSvc.conns)
+	relaySvc = relay.NewSvc(cfg, tlsCfg, connectionSvc.conns)
 	connectionSvc.Add(relaySvc)
 	mainSvc.Add(connectionSvc)
 
 	// 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.
@@ -908,10 +911,9 @@ 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, opts.RelayServers)
-
+	disc := discover.NewDiscoverer(myID, opts.ListenAddress, relaySvc)
 	if opts.LocalAnnEnabled {
 		l.Infoln("Starting local discovery announcements")
 		disc.StartLocal(opts.LocalAnnPort, opts.LocalAnnMCAddr)

+ 1 - 1
cmd/syncthing/verbose.go

@@ -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"])

+ 3 - 1
gui/assets/lang/lang-en.json

@@ -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",

+ 18 - 1
gui/index.html

@@ -385,6 +385,19 @@
                       </span>
                     </td>
                   </tr>
+                  <tr ng-if="system.relayClientStatus != undefined && relayClientsTotal > 0">
+                    <th><span class="fa fa-fw fa-sitemap"></span>&nbsp;<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>&nbsp;<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>&nbsp;<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'">

+ 14 - 3
gui/syncthing/core/syncthingController.js

@@ -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 = 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.announceServersFailed = failed;
+                $scope.relayClientsFailed = failedRelays;
 
 
                 console.log("refreshSystem", data);

File diff suppressed because it is too large
+ 1 - 1
lib/auto/gui.files.go


+ 19 - 35
lib/discover/discover.go

@@ -22,13 +22,14 @@ import (
 	"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
-	relays          []Relay
+	relaySvc        *relay.Svc
 	localBcastIntv  time.Duration
 	localBcastStart time.Time
 	cacheLifetime   time.Duration
@@ -56,11 +57,11 @@ var (
 	ErrIncorrectMagic = errors.New("incorrect magic number")
 )
 
-func NewDiscoverer(id protocol.DeviceID, addresses []string, relayAdresses []string) *Discoverer {
+func NewDiscoverer(id protocol.DeviceID, addresses []string, relaySvc *relay.Svc) *Discoverer {
 	return &Discoverer{
 		myID:            id,
 		listenAddrs:     addresses,
-		relays:          measureLatency(relayAdresses),
+		relaySvc:        relaySvc,
 		localBcastIntv:  30 * time.Second,
 		cacheLifetime:   5 * time.Minute,
 		negCacheCutoff:  3 * time.Minute,
@@ -143,7 +144,7 @@ func (d *Discoverer) StartGlobal(servers []string, extPort uint16) {
 	}
 
 	d.extPort = extPort
-	pkt := d.announcementPkt()
+	pkt := d.announcementPkt(true)
 	wg := sync.NewWaitGroup()
 	clients := make(chan Client, len(servers))
 	for _, address := range servers {
@@ -317,49 +318,32 @@ func (d *Discoverer) All() map[protocol.DeviceID][]CacheEntry {
 	return devices
 }
 
-func (d *Discoverer) announcementPkt() *Announce {
+func (d *Discoverer) announcementPkt(allowExternal bool) *Announce {
 	var addrs []string
-	if d.extPort != 0 {
+	if d.extPort != 0 && allowExternal {
 		addrs = []string{fmt.Sprintf("tcp://:%d", d.extPort)}
 	} else {
-		for _, aurl := range d.listenAddrs {
-			uri, err := url.Parse(aurl)
-			if err != nil {
-				if debug {
-					l.Debugf("discovery: failed to parse listen address %s: %s", aurl, err)
-				}
-				continue
-			}
-			addr, err := net.ResolveTCPAddr("tcp", uri.Host)
-			if err != nil {
-				l.Warnln("discover: %v: not announcing %s", err, aurl)
-				continue
-			} else if debug {
-				l.Debugf("discover: resolved %s as %#v", aurl, uri.Host)
-			}
-			if len(addr.IP) == 0 || addr.IP.IsUnspecified() {
-				uri.Host = fmt.Sprintf(":%d", addr.Port)
-			} else if bs := addr.IP.To4(); bs != nil {
-				uri.Host = fmt.Sprintf("%s:%d", bs.String(), addr.Port)
-			} else if bs := addr.IP.To16(); bs != nil {
-				uri.Host = fmt.Sprintf("[%s]:%d", bs.String(), addr.Port)
+		addrs = resolveAddrs(d.listenAddrs)
+	}
+
+	relayAddrs := make([]string, 0)
+	if d.relaySvc != nil {
+		status := d.relaySvc.ClientStatus()
+		for uri, ok := range status {
+			if ok {
+				relayAddrs = append(relayAddrs, uri)
 			}
-			addrs = append(addrs, uri.String())
 		}
 	}
+
 	return &Announce{
 		Magic: AnnouncementMagic,
-		This:  Device{d.myID[:], addrs, d.relays},
+		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, d.relays},
-	}
+	var pkt = d.announcementPkt(false)
 	msg := pkt.MustMarshalXDR()
 
 	for {

+ 6 - 0
lib/model/connection.go

@@ -7,11 +7,17 @@
 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

+ 4 - 0
lib/model/model.go

@@ -219,6 +219,7 @@ type ConnectionInfo struct {
 	protocol.Statistics
 	Address       string
 	ClientVersion string
+	Type          ConnectionType
 }
 
 func (info ConnectionInfo) MarshalJSON() ([]byte, error) {
@@ -227,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,
 	})
 }
@@ -249,6 +251,7 @@ func (m *Model) ConnectionStats() map[string]interface{} {
 		}
 		if addr := m.conn[device].RemoteAddr(); addr != nil {
 			ci.Address = addr.String()
+			ci.Type = conn.Type
 		}
 
 		conns[device.String()] = ci
@@ -585,6 +588,7 @@ func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
 	}
 
 	if conn, ok := m.conn[deviceID]; ok {
+		event["type"] = conn.Type.String()
 		addr := conn.RemoteAddr()
 		if addr != nil {
 			event["addr"] = addr.String()

+ 20 - 0
lib/osutil/osutil.go

@@ -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
+}

+ 19 - 0
lib/relay/debug.go

@@ -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
+)

+ 21 - 17
cmd/syncthing/relays.go → lib/relay/relay.go

@@ -4,7 +4,7 @@
 // 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
+package relay
 
 import (
 	"crypto/tls"
@@ -16,16 +16,17 @@ import (
 	"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 newRelaySvc(cfg *config.Wrapper, tlsCfg *tls.Config, conns chan<- intermediateConnection) *relaySvc {
-	svc := &relaySvc{
-		Supervisor: suture.New("relaySvc", suture.Spec{
+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 debugNet {
+				if debug {
 					l.Infoln(log)
 				}
 			},
@@ -58,7 +59,7 @@ func newRelaySvc(cfg *config.Wrapper, tlsCfg *tls.Config, conns chan<- intermedi
 	return svc
 }
 
-type relaySvc struct {
+type Svc struct {
 	*suture.Supervisor
 	cfg    *config.Wrapper
 	tlsCfg *tls.Config
@@ -70,7 +71,7 @@ type relaySvc struct {
 	invitations   chan protocol.SessionInvitation
 }
 
-func (s *relaySvc) VerifyConfiguration(from, to config.Configuration) error {
+func (s *Svc) VerifyConfiguration(from, to config.Configuration) error {
 	for _, addr := range to.Options.RelayServers {
 		_, err := url.Parse(addr)
 		if err != nil {
@@ -80,12 +81,12 @@ func (s *relaySvc) VerifyConfiguration(from, to config.Configuration) error {
 	return nil
 }
 
-func (s *relaySvc) CommitConfiguration(from, to config.Configuration) bool {
+func (s *Svc) CommitConfiguration(from, to config.Configuration) bool {
 	existing := make(map[string]struct{}, len(to.Options.RelayServers))
 	for _, addr := range to.Options.RelayServers {
 		uri, err := url.Parse(addr)
 		if err != nil {
-			if debugNet {
+			if debug {
 				l.Debugln("Failed to parse relay address", addr, err)
 			}
 			continue
@@ -95,7 +96,7 @@ func (s *relaySvc) CommitConfiguration(from, to config.Configuration) bool {
 
 		_, ok := s.tokens[uri.String()]
 		if !ok {
-			if debugNet {
+			if debug {
 				l.Debugln("Connecting to relay", uri)
 			}
 			c := client.NewProtocolClient(uri, s.tlsCfg.Certificates, s.invitations)
@@ -114,7 +115,7 @@ func (s *relaySvc) CommitConfiguration(from, to config.Configuration) bool {
 			s.mut.Lock()
 			delete(s.clients, uri)
 			s.mut.Unlock()
-			if debugNet {
+			if debug {
 				l.Debugln("Disconnecting from relay", uri, err)
 			}
 		}
@@ -123,7 +124,7 @@ func (s *relaySvc) CommitConfiguration(from, to config.Configuration) bool {
 	return true
 }
 
-func (s *relaySvc) ClientStatus() map[string]bool {
+func (s *Svc) ClientStatus() map[string]bool {
 	s.mut.RLock()
 	status := make(map[string]bool, len(s.clients))
 	for uri, client := range s.clients {
@@ -136,7 +137,7 @@ func (s *relaySvc) ClientStatus() map[string]bool {
 type invitationReceiver struct {
 	invitations chan protocol.SessionInvitation
 	tlsCfg      *tls.Config
-	conns       chan<- intermediateConnection
+	conns       chan<- model.IntermediateConnection
 	stop        chan struct{}
 }
 
@@ -149,18 +150,21 @@ func (r *invitationReceiver) Serve() {
 	for {
 		select {
 		case inv := <-r.invitations:
-			if debugNet {
+			if debug {
 				l.Debugln("Received relay invitation", inv)
 			}
 			conn, err := client.JoinSession(inv)
 			if err != nil {
-				if debugNet {
+				if debug {
 					l.Debugf("Failed to join relay session %s: %v", inv, err)
 				}
 				continue
 			}
 
-			setTCPOptions(conn.(*net.TCPConn))
+			err = osutil.SetTCPOptions(conn.(*net.TCPConn))
+			if err != nil {
+				l.Infoln(err)
+			}
 
 			var tc *tls.Conn
 
@@ -175,7 +179,7 @@ func (r *invitationReceiver) Serve() {
 				tc.Close()
 				continue
 			}
-			r.conns <- intermediateConnection{
+			r.conns <- model.IntermediateConnection{
 				tc, model.ConnectionTypeRelayAccept,
 			}
 		case <-r.stop:

Some files were not shown because too many files changed in this diff