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.
77 lines
2.1 KiB
Go
77 lines
2.1 KiB
Go
//go:build standalone
|
|
|
|
package checker
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
sdk "git.happydns.org/checker-sdk-go/checker"
|
|
)
|
|
|
|
// RenderForm exposes the inputs needed to drive a STUN/TURN check from
|
|
// the standalone HTML form. The fields mirror the canonical option
|
|
// documentation in Definition() (RunOpts then UserOpts) so the form
|
|
// stays in lock-step with the JSON schema served at /definition.
|
|
func (p *stunTurnProvider) RenderForm() []sdk.CheckerOptionField {
|
|
def := p.Definition()
|
|
fields := make([]sdk.CheckerOptionField, 0, len(def.Options.RunOpts)+len(def.Options.UserOpts))
|
|
fields = append(fields, def.Options.RunOpts...)
|
|
fields = append(fields, def.Options.UserOpts...)
|
|
return fields
|
|
}
|
|
|
|
// ParseForm turns the submitted form into a CheckerOptions. At least one
|
|
// of zone or serverURI must be provided so discoverEndpoints has
|
|
// something to work with.
|
|
func (p *stunTurnProvider) ParseForm(r *http.Request) (sdk.CheckerOptions, error) {
|
|
zone := strings.TrimSpace(r.FormValue("zone"))
|
|
zone = strings.TrimSuffix(zone, ".")
|
|
uri := strings.TrimSpace(r.FormValue("serverURI"))
|
|
if zone == "" && uri == "" {
|
|
return nil, errors.New("either zone or serverURI is required")
|
|
}
|
|
|
|
opts := sdk.CheckerOptions{}
|
|
if zone != "" {
|
|
opts["zone"] = zone
|
|
}
|
|
if uri != "" {
|
|
opts["serverURI"] = uri
|
|
}
|
|
|
|
if v := strings.TrimSpace(r.FormValue("mode")); v != "" {
|
|
switch v {
|
|
case "auto", "stun", "turn":
|
|
opts["mode"] = v
|
|
default:
|
|
return nil, errors.New("mode must be one of: auto, stun, turn")
|
|
}
|
|
}
|
|
|
|
for _, k := range []string{"username", "credential", "sharedSecret", "realm", "transports", "probePeer"} {
|
|
if v := strings.TrimSpace(r.FormValue(k)); v != "" {
|
|
opts[k] = v
|
|
}
|
|
}
|
|
|
|
if v := r.FormValue("testChannelBind"); v != "" {
|
|
opts["testChannelBind"] = v == "true" || v == "on" || v == "1"
|
|
}
|
|
|
|
for _, k := range []string{"warningRTT", "criticalRTT", "timeout"} {
|
|
v := strings.TrimSpace(r.FormValue(k))
|
|
if v == "" {
|
|
continue
|
|
}
|
|
n, err := strconv.ParseUint(v, 10, 32)
|
|
if err != nil {
|
|
return nil, errors.New(k + " must be a non-negative integer")
|
|
}
|
|
opts[k] = int(n)
|
|
}
|
|
|
|
return opts, nil
|
|
}
|