cmd/derper: add a STUN server
"io"
"io/ioutil"
"log"
+ "net"
"net/http"
"os"
"path/filepath"
+ "time"
"github.com/tailscale/wireguard-go/wgcfg"
"golang.org/x/crypto/acme/autocert"
"tailscale.com/derp"
"tailscale.com/derp/derphttp"
"tailscale.com/logpolicy"
+ "tailscale.com/stun"
"tailscale.com/tsweb"
"tailscale.com/types/key"
)
hostname = flag.String("hostname", "derp.tailscale.com", "LetsEncrypt host name, if addr's port is :443")
mbps = flag.Int("mbps", 5, "Mbps (mebibit/s) per-client rate limit; 0 means unlimited")
logCollection = flag.String("logcollection", "", "If non-empty, logtail collection to log to")
+ runSTUN = flag.Bool("stun", false, "also run a STUN server")
)
type config struct {
@@ -135,6 +139,10 @@ func main() {
}
}))
+ if *runSTUN {
+ go serveSTUN()
+ }
+
httpsrv := &http.Server{
Addr: *addr,
Handler: mux,
@@ -190,3 +198,58 @@ func debugHandler(s *derp.Server) http.Handler {
`)
})
}
+
+func serveSTUN() {
+ pc, err := net.ListenPacket("udp", ":3478")
+ if err != nil {
+ log.Fatalf("failed to open STUN listener: %v", err)
+ }
+ log.Printf("running STUN server on %v", pc.LocalAddr())
+ var (
+ stunReadErrors = expvar.NewInt("stun-read-error")
+ stunWriteErrors = expvar.NewInt("stun-write-error")
+ stunReadNotSTUN = expvar.NewInt("stun-read-not-stun")
+ stunReadNotSTUNValid = expvar.NewInt("stun-read-not-stun-valid")
+ stunReadIPv4 = expvar.NewInt("stun-read-ipv4")
+ stunReadIPv6 = expvar.NewInt("stun-read-ipv6")
+ stunWrite = expvar.NewInt("stun-write")
+ )
+ var buf [64 << 10]byte
+ for {
+ n, addr, err := pc.ReadFrom(buf[:])
+ if err != nil {
+ log.Printf("STUN ReadFrom: %v", err)
+ time.Sleep(time.Second)
+ stunReadErrors.Add(1)
+ continue
+ }
+ ua, ok := addr.(*net.UDPAddr)
+ if !ok {
+ log.Printf("STUN unexpected address %T %v", addr, addr)
+ stunReadErrors.Add(1)
+ continue
+ }
+ pkt := buf[:n]
+ if !stun.Is(pkt) {
+ stunReadNotSTUN.Add(1)
+ continue
+ }
+ txid, err := stun.ParseBindingRequest(pkt)
+ if err != nil {
+ stunReadNotSTUNValid.Add(1)
+ continue
+ }
+ if ua.IP.To4() != nil {
+ stunReadIPv4.Add(1)
+ } else {
+ stunReadIPv6.Add(1)
+ }
+ res := stun.Response(txid, ua.IP, uint16(ua.Port))
+ _, err = pc.WriteTo(res, addr)
+ if err != nil {
+ stunWriteErrors.Add(1)
+ } else {
+ stunWrite.Add(1)
+ }
+ }
+}
