checker-stun-turn/checker/discovery.go
Pierre-Olivier Mercier fa516fdaad Initial commit
Adds a happyDomain checker that probes STUN/TURN servers end-to-end:
DNS/SRV discovery, UDP/TCP/TLS/DTLS dial, STUN binding + reflexive-addr
sanity, open-relay detection, authenticated TURN Allocate (long-term
creds or REST-API HMAC), public-relay check, CreatePermission + Send
round-trip through the relay, and optional ChannelBind.

Failing sub-tests carry a remediation string (`Fix`) that the HTML
report surfaces as a yellow headline callout and inline next to each
row. Mapping covers the most common coturn misconfigurations
(external-ip, relay-ip, lt-cred-mech, min-port/max-port, cert issues,
401 nonce drift, 441/442/486/508 allocation errors).

Implements sdk.EndpointDiscoverer (checker/discovery.go): every
stuns:/turns:/DTLS endpoint observed during Collect is published as a
DiscoveredEndpoint{Type: "tls"|"dtls"} so a downstream TLS checker can
verify certificates without re-parsing the observation.

Backed by pion/stun/v3 + pion/turn/v4 + pion/dtls/v3; SDK pinned to a
local replace until the EndpointDiscoverer interface ships in a tagged
release.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 13:41:52 +07:00

47 lines
1.3 KiB
Go

package checker
import (
"fmt"
sdk "git.happydns.org/checker-sdk-go/checker"
)
// DiscoverEndpoints implements sdk.EndpointDiscoverer. It publishes every
// TLS/DTLS endpoint seen during collection so a downstream TLS checker can
// verify certificates without re-parsing the observation.
//
// stuns:/turns: speak TLS immediately after the TCP handshake (no STARTTLS),
// so we emit Type="tls". DTLS endpoints are published as Type="dtls" — an
// open-string convention, consumed by DTLS-aware probes.
func (p *stunTurnProvider) DiscoverEndpoints(data any) ([]sdk.DiscoveredEndpoint, error) {
d, ok := data.(*StunTurnData)
if !ok {
return nil, fmt.Errorf("unexpected data type %T", data)
}
seen := make(map[string]struct{})
var out []sdk.DiscoveredEndpoint
for _, ep := range d.Endpoints {
if !ep.Endpoint.Secure {
continue
}
epType := "tls"
if ep.Endpoint.Transport == TransportDTLS {
epType = "dtls"
}
key := fmt.Sprintf("%s|%s|%d", epType, ep.Endpoint.Host, ep.Endpoint.Port)
if _, dup := seen[key]; dup {
continue
}
seen[key] = struct{}{}
out = append(out, sdk.DiscoveredEndpoint{
Type: epType,
Host: ep.Endpoint.Host,
Port: ep.Endpoint.Port,
Meta: map[string]any{
"source": "stun-turn",
"uri": ep.Endpoint.URI,
},
})
}
return out, nil
}