// Copyright (c) Tailscale Inc & AUTHORS // SPDX-License-Identifier: BSD-3-Clause // Command stunc makes a STUN request to a STUN server and prints the result. package main import ( "flag" "log" "net" "os" "strconv" "time" "tailscale.com/net/stun" ) func main() { log.SetFlags(0) log.SetOutput(os.Stdout) // CLI tool, write to stdout. var host string port := "3478" var readTimeout time.Duration flag.DurationVar(&readTimeout, "timeout", 3*time.Second, "response wait timeout") quiet := flag.Bool("quiet", false, "only print errors") flag.Parse() values := flag.Args() if len(values) < 1 || len(values) > 2 { log.Printf("usage: %s [port]", os.Args[0]) flag.PrintDefaults() os.Exit(1) } else { for i, value := range values { switch i { case 0: host = value case 1: port = value } } } _, err := strconv.ParseUint(port, 10, 16) if err != nil { log.Fatalf("invalid port: %v", err) } hostPort := net.JoinHostPort(host, port) log.SetPrefix(hostPort + ": ") uaddr, err := net.ResolveUDPAddr("udp", hostPort) if err != nil { log.Fatal(err) } c, err := net.ListenUDP("udp", nil) if err != nil { log.Fatal(err) } txID := stun.NewTxID() req := stun.Request(txID) _, err = c.WriteToUDP(req, uaddr) if err != nil { log.Fatal(err) } err = c.SetReadDeadline(time.Now().Add(readTimeout)) if err != nil { log.Fatal(err) } var buf [1024]byte n, raddr, err := c.ReadFromUDPAddrPort(buf[:]) if err != nil { log.Fatal(err) } tid, saddr, err := stun.ParseResponse(buf[:n]) if err != nil { log.Fatal(err) } if tid != txID { log.Fatalf("txid mismatch: got %v, want %v", tid, txID) } if !*quiet { log.Printf("sent addr: %v", uaddr) log.Printf("from addr: %v", raddr) log.Printf("stun addr: %v", saddr) } }