checker-xmpp/checker/types.go

135 lines
4.7 KiB
Go

// Package checker implements the XMPP server checker for happyDomain.
//
// It probes a domain's XMPP deployment (SRV discovery, STARTTLS,
// stream features, SASL mechanisms, dialback / SASL EXTERNAL,
// XEP-0368 direct TLS) and reports actionable findings.
//
// TLS certificate chain / SAN / expiry / cipher posture is intentionally
// out of scope; a dedicated TLS checker covers that.
package checker
import (
sdk "git.happydns.org/checker-sdk-go/checker"
)
const ObservationKeyXMPP sdk.ObservationKey = "xmpp"
type XMPPMode string
const (
ModeClient XMPPMode = "c2s"
ModeServer XMPPMode = "s2s"
ModeBoth XMPPMode = "both"
)
var validModes = []string{string(ModeClient), string(ModeServer), string(ModeBoth)}
// XMPPData is the full observation stored per run.
type XMPPData struct {
Domain string `json:"domain"`
RunAt string `json:"run_at"`
SRV SRVLookup `json:"srv"`
Endpoints []EndpointProbe `json:"endpoints"`
Coverage ReachabilitySpan `json:"coverage"`
Issues []Issue `json:"issues"`
}
type SRVLookup struct {
Client []SRVRecord `json:"client,omitempty"`
Server []SRVRecord `json:"server,omitempty"`
ClientSecure []SRVRecord `json:"client_secure,omitempty"`
ServerSecure []SRVRecord `json:"server_secure,omitempty"`
Jabber []SRVRecord `json:"jabber,omitempty"`
// Errors per-set (keyed by record type like "_xmpp-client._tcp").
Errors map[string]string `json:"errors,omitempty"`
// FallbackProbed is true when no SRV was published and we probed the bare domain.
FallbackProbed bool `json:"fallback_probed,omitempty"`
}
type SRVRecord struct {
Target string `json:"target"`
Port uint16 `json:"port"`
Priority uint16 `json:"priority"`
Weight uint16 `json:"weight"`
// IPv4 and IPv6 addresses resolved for the target (at probe time).
IPv4 []string `json:"ipv4,omitempty"`
IPv6 []string `json:"ipv6,omitempty"`
}
// EndpointProbe is the result of probing one (mode, host, port, address) tuple.
type EndpointProbe struct {
Mode XMPPMode `json:"mode"`
SRVPrefix string `json:"srv_prefix"`
Target string `json:"target"`
Port uint16 `json:"port"`
Address string `json:"address"`
IsIPv6 bool `json:"is_ipv6,omitempty"`
DirectTLS bool `json:"direct_tls,omitempty"`
// What happened.
TCPConnected bool `json:"tcp_connected"`
StreamOpened bool `json:"stream_opened"`
STARTTLSOffered bool `json:"starttls_offered"`
STARTTLSRequired bool `json:"starttls_required"`
STARTTLSUpgraded bool `json:"starttls_upgraded"`
TLSVersion string `json:"tls_version,omitempty"`
TLSCipher string `json:"tls_cipher,omitempty"`
// Post-TLS features.
FeaturesRead bool `json:"features_read,omitempty"`
SASLMechanisms []string `json:"sasl_mechanisms,omitempty"`
DialbackOffered bool `json:"dialback_offered,omitempty"`
SASLExternal bool `json:"sasl_external,omitempty"`
StreamFrom string `json:"stream_from,omitempty"`
ElapsedMS int64 `json:"elapsed_ms"`
Error string `json:"error,omitempty"`
}
type ReachabilitySpan struct {
HasIPv4 bool `json:"has_ipv4"`
HasIPv6 bool `json:"has_ipv6"`
// WorkingC2S is true when at least one c2s endpoint completed TLS + advertised SASL.
WorkingC2S bool `json:"working_c2s"`
// WorkingS2S is true when at least one s2s endpoint completed TLS + advertised dialback or SASL EXTERNAL.
WorkingS2S bool `json:"working_s2s"`
}
// Issue is a structured finding attached to the observation so the rule and
// the HTML report can both consume them without re-deriving logic.
type Issue struct {
Code string `json:"code"`
Severity string `json:"severity"` // "info" | "warn" | "crit"
Message string `json:"message"`
Fix string `json:"fix,omitempty"`
Endpoint string `json:"endpoint,omitempty"`
}
// Severities (string for stable JSON, independent of sdk.Status numeric values).
const (
SeverityInfo = "info"
SeverityWarn = "warn"
SeverityCrit = "crit"
)
// Issue codes.
const (
CodeNoSRV = "xmpp.no_srv"
CodeSRVServfail = "xmpp.srv.servfail"
CodeStartTLSMissing = "xmpp.starttls.missing"
CodeStartTLSNotRequired = "xmpp.starttls.not_required"
CodeStartTLSFailed = "xmpp.starttls.handshake_failed"
CodeTCPUnreachable = "xmpp.tcp.unreachable"
CodeSASLPlainOnly = "xmpp.sasl.plain_only"
CodeSASLNoSCRAM = "xmpp.sasl.no_scram"
CodeSASLNoSCRAMPlus = "xmpp.sasl.no_scram_plus"
CodeS2SNoAuth = "xmpp.s2s.no_auth"
CodeS2SProbeIncomplete = "xmpp.s2s.probe_incomplete"
CodeLegacyJabber = "xmpp.legacy_jabber"
CodeNoIPv6 = "xmpp.no_ipv6"
CodeNoDirectTLS = "xmpp.no_direct_tls"
CodeAllEndpointsDown = "xmpp.all_endpoints_down"
)