9 Commits
Author SHA1 Message Date
wweir 30e80ab510 fix(router): bound degrade retry to snapshot, gate internal PTR on selected upstream
- degradeUpstream now takes the caller's snapshot length: a concurrent
  refresh that grows the live upstream list no longer lets the retry
  index past the stale snapshot and panic the DNS handler.
- Internal reverse lookups take a dedicated forward path that never runs
  the degrade state machine: the query goes only to the selected internal
  upstream and surfaces SERVFAIL on failure instead of retrying onto a
  public fallback, closing the TOCTOU leak in the PTR gate.
- Local DNS replies set the RecursionAvailable bit per RFC 1035.
- A failed TCP retry for a truncated UDP response keeps the truncated
  response (with TC set) instead of dropping it for SERVFAIL, so
  networks that block TCP/53 still get usable answers.
2026-08-12 17:08:32 +08:00
wweir 60ac21f230 feat(router): guard DNS upstream refresh with generation
- Generation counter invalidates in-flight refreshes after SetDNS
- Probe/promote/degrade paths carry the generation to avoid stale mutations
2026-08-07 12:02:21 +08:00
wweir b4cb204e3f feat(admin): OTEL metrics, real-time rates and in-process history
Monitoring overhaul for the admin console:

- Add internal/metrics: OpenTelemetry meter with Prometheus exporter
  (counters for bytes/dns/connections/rule hits, updown counter for
  active connections, histogram for connection duration, process
  gauges). Served unauthenticated at GET /metrics; an OTLP exporter
  can be added later without touching call sites.
- Fix uptime: TrafficSnapshot.Uptime was a time.Duration serialized
  as nanoseconds while the frontend renders seconds; it is now int64
  seconds. Stats is created before rule loading so uptime tracks the
  process start.
- Extend admin.Stats: per-protocol active connections (idempotent
  decrement on Close), 60s sliding-window rates, rule-hit counters,
  process resources, and a bounded in-process history ring (1h at 5s
  sampling, gap-clamped) served at GET /api/history.
- Router route observer: block/direct decisions in DialSmart, proxy
  decisions in DialProxyOnly (covers HTTP/HTTPS paths), exactly once
  per connection.
- Fix socks5 handler closing the raw conn instead of the wrapped one,
  which would have leaked active-connection counts.
- Frontend: Overview gains rate cards, active connections, rule hits,
  system stats, and four hand-rolled SVG area charts (traffic, DNS/
  conns, active per protocol, rule hits) with hover readouts.
2026-08-06 17:17:24 +08:00
wweir ce5cf284b7 feat(router): add thread-safe RuleSet for runtime rule management
RuleSet retains the raw rule list alongside the suffix tree so rules can be
listed, added and removed at runtime. Removal rebuilds the tree from the
retained list; startup batch loading keeps the old single-compaction flow.
Router.BlockRule/DirectRule/ProxyRule migrate to *RuleSet.
2026-08-06 08:56:22 +08:00
wweir 3dec49dec4 test: add regression tests for routing layer separation and fail-fast
- DNS unknown domain forwards upstream; TCP smart dial falls back proxy.
- Transparent HTTP/HTTPS uses proxy-only even when block rule matches.
- Invalid country CIDR and invalid MMDB path fail at construction.
- Access probe cache is instance-scoped; test injects fake prober.
- net.Pipe tests add timeout protection via waitForHandler helper.
- newTestRouter helper wraps NewRouter error handling.
2026-06-24 13:30:48 +08:00
wweir 875f80e999 refactor: replace mem cache with otter 2026-05-14 21:53:16 +08:00
wweir ad338598ae fix: harden dns proxy routing 2026-04-25 04:12:42 +08:00
wweir 5fb9dfcfee fix(router): skip empty country mmdb 2026-04-25 03:17:45 +08:00
wweir b2228f8643 refactor: harden routing and transport handling 2026-03-30 23:02:13 +08:00