| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 |
- // Copyright (c) Tailscale Inc & AUTHORS
- // SPDX-License-Identifier: BSD-3-Clause
- // Package stunserver implements a STUN server. The package publishes a number of stats
- // to expvar under the top level label "stun". Logs are sent to the standard log package.
- package stunserver
- import (
- "context"
- "errors"
- "io"
- "log"
- "net"
- "net/netip"
- "time"
- "tailscale.com/metrics"
- "tailscale.com/net/stun"
- )
- var (
- stats = metrics.NewSet("stun")
- stunDisposition = stats.NewLabelMap("counter_requests", "disposition")
- stunAddrFamily = stats.NewLabelMap("counter_addrfamily", "family")
- stunReadError = stunDisposition.Get("read_error")
- stunNotSTUN = stunDisposition.Get("not_stun")
- stunWriteError = stunDisposition.Get("write_error")
- stunSuccess = stunDisposition.Get("success")
- stunIPv4 = stunAddrFamily.Get("ipv4")
- stunIPv6 = stunAddrFamily.Get("ipv6")
- )
- type STUNServer struct {
- ctx context.Context // ctx signals service shutdown
- pc *net.UDPConn // pc is the UDP listener
- }
- // New creates a new STUN server. The server is shutdown when ctx is done.
- func New(ctx context.Context) *STUNServer {
- return &STUNServer{ctx: ctx}
- }
- // Listen binds the listen socket for the server at listenAddr.
- func (s *STUNServer) Listen(listenAddr string) error {
- uaddr, err := net.ResolveUDPAddr("udp", listenAddr)
- if err != nil {
- return err
- }
- s.pc, err = net.ListenUDP("udp", uaddr)
- if err != nil {
- return err
- }
- log.Printf("STUN server listening on %v", s.LocalAddr())
- // close the listener on shutdown in order to break out of the read loop
- go func() {
- <-s.ctx.Done()
- s.pc.Close()
- }()
- return nil
- }
- // Serve starts serving responses to STUN requests. Listen must be called before Serve.
- func (s *STUNServer) Serve() error {
- var buf [64 << 10]byte
- var (
- n int
- ua *net.UDPAddr
- err error
- )
- for {
- n, ua, err = s.pc.ReadFromUDP(buf[:])
- if err != nil {
- if errors.Is(err, io.EOF) || errors.Is(err, net.ErrClosed) {
- return nil
- }
- log.Printf("STUN ReadFrom: %v", err)
- time.Sleep(time.Second)
- stunReadError.Add(1)
- continue
- }
- pkt := buf[:n]
- if !stun.Is(pkt) {
- stunNotSTUN.Add(1)
- continue
- }
- txid, err := stun.ParseBindingRequest(pkt)
- if err != nil {
- stunNotSTUN.Add(1)
- continue
- }
- if ua.IP.To4() != nil {
- stunIPv4.Add(1)
- } else {
- stunIPv6.Add(1)
- }
- addr, _ := netip.AddrFromSlice(ua.IP)
- res := stun.Response(txid, netip.AddrPortFrom(addr, uint16(ua.Port)))
- _, err = s.pc.WriteTo(res, ua)
- if err != nil {
- stunWriteError.Add(1)
- } else {
- stunSuccess.Add(1)
- }
- }
- }
- // ListenAndServe starts the STUN server on listenAddr.
- func (s *STUNServer) ListenAndServe(listenAddr string) error {
- if err := s.Listen(listenAddr); err != nil {
- return err
- }
- return s.Serve()
- }
- // LocalAddr returns the local address of the STUN server. It must not be called before ListenAndServe.
- func (s *STUNServer) LocalAddr() net.Addr {
- return s.pc.LocalAddr()
- }
|