server: STUN, TCP echo, observations API, delayed-echo + connect-back actions
server-test / test (push) Successful in 27s
server-release / image (push) Successful in 14s
server-release / release (push) Successful in 27s

- stun: RFC 5389 binding responder + RFC 5780 attributes (OTHER-ADDRESS,
  RESPONSE-ORIGIN, CHANGE-REQUEST) on a primary/alt-port socket grid per
  address; advertises stun-5780 with >=2 same-family addrs, else
  stun-basic. Unmodified framing for tooling interop. Tested.
- tcpecho: JSON greeting with observed src + TCP_INFO MSS/options
  (Linux getsockopt; zeroed elsewhere via build tags), then byte echo.
- session: per-packet UDP observations + connect-back results, ByID lookup.
- control: GET /v1/sessions/{id}/observations, POST .../actions
  (delayed_echo → DELAYED_ECHO at the observed data-plane source;
  connect_back → dial the control-plane source, record connected/refused/
  timeout+rtt). Capabilities computed from what is actually wired.
- config/main: comma-separated STUN listeners; all planes bind explicit
  addresses; graceful shutdown of the new listeners.

Full flow smoke-tested; go test green (stun binding/change-port,
dataplane wire format).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
mrambossek
2026-07-31 19:53:36 +02:00
co-authored by Claude Opus 5
parent 507a8bfc1f
commit 7b676e666e
10 changed files with 901 additions and 18 deletions
+53 -3
View File
@@ -38,7 +38,9 @@ import (
"echo-lot.app/server/internal/selfupdate"
"echo-lot.app/server/internal/session"
"echo-lot.app/server/internal/store"
"echo-lot.app/server/internal/stun"
"echo-lot.app/server/internal/system"
"echo-lot.app/server/internal/tcpecho"
)
// Version is stamped via -ldflags "-X main.Version=v1.2.3" in CI.
@@ -95,9 +97,20 @@ func serve(cfg *config.Config) error {
slog.Info("control-plane certificate", "pin-sha256", pin)
sessions := session.NewManager(15 * time.Minute)
dp := &dataplane.Server{Sessions: sessions}
tcpSrv := &tcpecho.Server{}
caps := []string{"udp-probe", "delayed-echo", "connect-back"}
if len(config.Addrs(cfg.TCPListen)) > 0 {
caps = append(caps, "tcp-echo")
}
ctl := &control.Server{
Store: st, Sessions: sessions, Name: cfg.Name,
UDPPort: mustPort(firstAddr(cfg.UDPListen)), TCPPort: mustPort(firstAddr(cfg.TCPListen)), PinB64: pin,
UDPPort: mustPort(firstAddr(cfg.UDPListen)), TCPPort: mustPort(firstAddr(cfg.TCPListen)),
StunPort: mustPort(firstAddr(cfg.StunListen)), PinB64: pin,
DelayedEcho: dp.SendDelayedEcho,
TCPRecent: func(ip string) any { return tcpSrv.RecentFor(ip) },
}
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
@@ -156,14 +169,45 @@ func serve(cfg *config.Config) error {
return fmt.Errorf("udp listen %s: %w", addr, err)
}
udpConns = append(udpConns, conn)
dp := &dataplane.Server{Sessions: sessions}
go func(a string, c *net.UDPConn) {
errCh <- fmt.Errorf("udp %s: %w", a, dp.Serve(c))
}(addr, conn)
}
// TCP echo (spec §4)
var tcpLns []net.Listener
for _, addr := range config.Addrs(cfg.TCPListen) {
ln, err := net.Listen("tcp", addr)
if err != nil {
return fmt.Errorf("tcp listen %s: %w", addr, err)
}
tcpLns = append(tcpLns, ln)
go func(a string, l net.Listener) {
errCh <- fmt.Errorf("tcp %s: %w", a, tcpSrv.Serve(l))
}(addr, ln)
}
// STUN (spec §4) — advertises stun-5780 only with ≥2 same-family addrs.
var stunSrv *stun.Server
if stunAddrs := config.Addrs(cfg.StunListen); len(stunAddrs) > 0 {
stunSrv, err = stun.Listen(stunAddrs)
if err != nil {
return fmt.Errorf("stun listen: %w", err)
}
go func() { errCh <- fmt.Errorf("stun: %w", stunSrv.Serve()) }()
if stunSrv.Has5780() {
ctl.Capabilities = append(caps, "stun-5780")
} else {
ctl.Capabilities = append(caps, "stun-basic")
}
} else {
ctl.Capabilities = caps
}
slog.Info("listening",
"control", ctlAddrs, "admin", cfg.AdminListen, "udp", udpAddrs)
"control", ctlAddrs, "admin", cfg.AdminListen, "udp", udpAddrs,
"tcp", config.Addrs(cfg.TCPListen), "stun", config.Addrs(cfg.StunListen),
"capabilities", ctl.Capabilities)
select {
case <-ctx.Done():
@@ -175,6 +219,12 @@ func serve(cfg *config.Config) error {
for _, c := range udpConns {
_ = c.Close()
}
for _, l := range tcpLns {
_ = l.Close()
}
if stunSrv != nil {
stunSrv.Close()
}
return nil
case err := <-errCh:
return err