server: STUN, TCP echo, observations API, delayed-echo + connect-back actions
- 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:
co-authored by
Claude Opus 5
parent
507a8bfc1f
commit
7b676e666e
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user