Browse Source

Add UPnP renewal

Audrius Butkevicius 11 years ago
parent
commit
8976e53998
2 changed files with 42 additions and 16 deletions
  1. 40 16
      cmd/syncthing/main.go
  2. 2 0
      config/config.go

+ 40 - 16
cmd/syncthing/main.go

@@ -73,15 +73,16 @@ func init() {
 }
 
 var (
-	cfg        config.Configuration
-	myID       protocol.NodeID
-	confDir    string
-	logFlags   int = log.Ltime
-	rateBucket *ratelimit.Bucket
-	stop       = make(chan bool)
-	discoverer *discover.Discoverer
-	lockConn   *net.TCPListener
-	lockPort   int
+	cfg          config.Configuration
+	myID         protocol.NodeID
+	confDir      string
+	logFlags     int = log.Ltime
+	rateBucket   *ratelimit.Bucket
+	stop         = make(chan bool)
+	discoverer   *discover.Discoverer
+	lockConn     *net.TCPListener
+	lockPort     int
+	externalPort int
 )
 
 const (
@@ -470,11 +471,10 @@ nextRepo:
 
 	// UPnP
 
-	var externalPort = 0
 	if cfg.Options.UPnPEnabled {
 		// We seed the random number generator with the node ID to get a
 		// repeatable sequence of random external ports.
-		externalPort = setupUPnP(rand.NewSource(certSeed(cert.Certificate[0])))
+		setupUPnP(rand.NewSource(certSeed(cert.Certificate[0])))
 	}
 
 	// Routine to connect out to configured nodes
@@ -561,8 +561,7 @@ func waitForParentExit() {
 	l.Infoln("Continuing")
 }
 
-func setupUPnP(r rand.Source) int {
-	var externalPort = 0
+func setupUPnP(rnd rand.Source) {
 	if len(cfg.Options.ListenAddress) == 1 {
 		_, portStr, err := net.SplitHostPort(cfg.Options.ListenAddress[0])
 		if err != nil {
@@ -573,11 +572,12 @@ func setupUPnP(r rand.Source) int {
 			igd, err := upnp.Discover()
 			if err == nil {
 				for i := 0; i < 10; i++ {
-					r := 1024 + int(r.Int63()%(65535-1024))
-					err := igd.AddPortMapping(upnp.TCP, r, port, "syncthing", 0)
+					r := 1024 + int(rnd.Int63()%(65535-1024))
+					err := igd.AddPortMapping(upnp.TCP, r, port, "syncthing", cfg.Options.UPnPLease*60)
 					if err == nil {
 						externalPort = r
 						l.Infoln("Created UPnP port mapping - external port", externalPort)
+						go renewUPnP(igd, rnd, port)
 						break
 					}
 				}
@@ -594,7 +594,31 @@ func setupUPnP(r rand.Source) int {
 	} else {
 		l.Warnln("Multiple listening addresses; not attempting UPnP port mapping")
 	}
-	return externalPort
+}
+
+func renewUPnP(igd *upnp.IGD, rnd rand.Source, port int) {
+	if cfg.Options.UPnPRenewal < 1 {
+		return
+	}
+
+	for {
+		time.Sleep(time.Duration(cfg.Options.UPnPRenewal) * time.Minute)
+
+		err := igd.AddPortMapping(upnp.TCP, externalPort, port, "syncthing", cfg.Options.UPnPLease*60)
+		if err == nil {
+			l.Infoln("Renewed UPnP port mapping - external port", externalPort)
+			continue
+		}
+
+		r := 1024 + int(rnd.Int63()%(65535-1024))
+		err = igd.AddPortMapping(upnp.TCP, r, port, "syncthing", cfg.Options.UPnPLease*60)
+		if err == nil {
+			l.Infoln("Updated UPnP port mapping - external port", externalPort)
+			externalPort = r
+			continue
+		}
+		l.Warnln("Failed to update UPnP port mapping - externalPort", externalPort)
+	}
 }
 
 func resetRepositories() {

+ 2 - 0
config/config.go

@@ -113,6 +113,8 @@ type OptionsConfiguration struct {
 	MaxChangeKbps      int      `xml:"maxChangeKbps" default:"10000"`
 	StartBrowser       bool     `xml:"startBrowser" default:"true"`
 	UPnPEnabled        bool     `xml:"upnpEnabled" default:"true"`
+	UPnPLease          int      `xml:"upnpLeaseMinutes" default:"0"`
+	UPnPRenewal        int      `xml:"upnpRenewalMinutes" default:"0"`
 	URAccepted         int      `xml:"urAccepted"` // Accepted usage reporting version; 0 for off (undecided), -1 for off (permanently)
 
 	Deprecated_UREnabled  bool   `xml:"urEnabled,omitempty" json:"-"`