Repository: syncthing/syncthing (language: go)

Issue:
Resolve primary STUN server(s) via DNS SRV
### Feature description

I'd suggest that we resolve our own STUN servers via DNS SRV. This will allow us to change the domain name and IP address of our own STUN server without having to patch Syncthing.

This should be enough to fend off most non-Syncthing users.

If there's a consensus that this is a viable approach, I'd start working on a PR.

### Problem or use case

As previously discussed on the forum, we may still want to host our own STUN server. The problem with such a service is that it tends to be abused by others, which ultimately led to the current server being shut down. Any hardcoded address in the codebase is likely to suffer the same fate sooner or later.

see https://forum.syncthing.net/t/stun-syncthing-net-doesnt-resolve-anymore/24075/8

### Alternatives or workarounds

Static secrets for the STUN server, but this could suffer the same fate. Anything that can simply be hardcoded into a WebRTC page will eventually be abused. DNS SRV requires backend logic, which should deter more offenders.


Discussion / hints:
I'm on board with this. Let's try it. If it fails we're in no worse a spot than we are today.

Fix patch:
diff --git a/lib/config/config.go b/lib/config/config.go
index 6805a15ebf2..e3d9f11c990 100644
--- a/lib/config/config.go
+++ b/lib/config/config.go
@@ -68,13 +68,9 @@ var (
 	DefaultTheme = "default"
 	// Default stun servers should be substituted when the configuration
 	// contains <stunServer>default</stunServer>.
-
-	// DefaultPrimaryStunServers are servers provided by us (to avoid causing the public servers burden)
-	DefaultPrimaryStunServers = []string{
-		// Discontinued because of misuse. See https://forum.syncthing.net/t/stun-server-misuse/23319
-		//"stun.syncthing.net:3478",
-	}
-	DefaultSecondaryStunServers = []string{
+	// The primary stun servers are provided by us and are resolved via an SRV record
+	// The fallback stun servers are used if the primary ones can't be resolved or are down.
+	DefaultFallbackStunServers = []string{
 		"stun.counterpath.com:3478",
 		"stun.counterpath.net:3478",
 		"stun.ekiga.net:3478",
diff --git a/lib/config/optionsconfiguration.go b/lib/config/optionsconfiguration.go
index eecf34dda56..32e1b4891d8 100644
--- a/lib/config/optionsconfiguration.go
+++ b/lib/config/optionsconfiguration.go
@@ -8,8 +8,10 @@ package config
 
 import (
 	"fmt"
+	"net"
 	"runtime"
 	"slices"
+	"strings"
 
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/rand"
@@ -184,15 +186,22 @@ func (opts OptionsConfiguration) StunServers() []string {
 	for _, addr := range opts.RawStunServers {
 		switch addr {
 		case "default":
-			defaultPrimaryAddresses := make([]string, len(DefaultPrimaryStunServers))
-			copy(defaultPrimaryAddresses, DefaultPrimaryStunServers)
-			rand.Shuffle(defaultPrimaryAddresses)
-			addresses = append(addresses, defaultPrimaryAddresses...)
+			_, records, err := net.LookupSRV("stun", "udp", "syncthing.net")
+			if err != nil {
+				l.Warnln("Unable to resolve primary STUN servers via DNS:", err)
+			}
+
+			for _, record := range records {
+				priority := record.Priority
+				target := strings.TrimSuffix(record.Target, ".")
+				address := fmt.Sprintf("%s:%d", target, record.Port)
+				l.Debugf("Resolved primary STUN server %s with priority %d", address, priority)
+				addresses = append(addresses, address)
+			}
 
-			defaultSecondaryAddresses := make([]string, len(DefaultSecondaryStunServers))
-			copy(defaultSecondaryAddresses, DefaultSecondaryStunServers)
-			rand.Shuffle(defaultSecondaryAddresses)
-			addresses = append(addresses, defaultSecondaryAddresses...)
+			fallbackAddresses := slices.Clone(DefaultFallbackStunServers)
+			rand.Shuffle(fallbackAddresses)
+			addresses = append(addresses, fallbackAddresses...)
 		default:
 			addresses = append(addresses, addr)
 		}


Test patch: