Initial commit
CalDAV and CardDAV checkers sharing a single Go module. Discovery follows RFC 6764 (/.well-known + SRV/TXT), authenticated probes cover principal, home-set, collections and a minimal REPORT query on top of go-webdav. Common shape in internal/dav/; CalDAV adds a scheduling rule. Surfaces its context URL (and each secure-SRV target) as TLS endpoints via the EndpointDiscoverer interface, so the dedicated TLS checker can pick them up without re-parsing observations. HTML report foregrounds common misconfigs (well-known returning 200, missing SRV, plaintext-only SRV, missing DAV capability, skipped auth phase) as action-item callouts before the full phase breakdown.
This commit is contained in:
commit
7d5535fddf
39 changed files with 3179 additions and 0 deletions
85
internal/dav/endpoints.go
Normal file
85
internal/dav/endpoints.go
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
package dav
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
sdk "git.happydns.org/checker-sdk-go/checker"
|
||||
tlsct "git.happydns.org/checker-tls/contract"
|
||||
)
|
||||
|
||||
// DiscoverEntries hands TLS endpoints to downstream checkers. SRV targets
|
||||
// are emitted alongside the context URL because they're the names operators
|
||||
// must actually put on the certificate, and they often differ from the
|
||||
// queried domain. SNI is always equal to Host: unlike XMPP (RFC 6120
|
||||
// §13.7.2.1), CalDAV/CardDAV has no source-vs-target split.
|
||||
func DiscoverEntries(obs *Observation) []sdk.DiscoveryEntry {
|
||||
if obs == nil || obs.Discovery.ContextURL == "" {
|
||||
return nil
|
||||
}
|
||||
var out []sdk.DiscoveryEntry
|
||||
seen := map[string]struct{}{}
|
||||
|
||||
add := func(host string, port uint16) {
|
||||
if host == "" || port == 0 {
|
||||
return
|
||||
}
|
||||
key := host + ":" + strconv.Itoa(int(port))
|
||||
if _, dup := seen[key]; dup {
|
||||
return
|
||||
}
|
||||
seen[key] = struct{}{}
|
||||
entry, err := tlsct.NewEntry(tlsct.TLSEndpoint{
|
||||
Host: host,
|
||||
Port: port,
|
||||
SNI: host,
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("checker-dav: contract.NewEntry(%s:%d): %v", host, port, err)
|
||||
return
|
||||
}
|
||||
out = append(out, entry)
|
||||
}
|
||||
|
||||
if host, port, ok := hostPortFromURL(obs.Discovery.ContextURL); ok {
|
||||
add(host, port)
|
||||
}
|
||||
|
||||
// Every SRV target is reachable via priority/weight, so each one needs
|
||||
// its own valid certificate.
|
||||
for _, r := range obs.Discovery.SecureSRV {
|
||||
port := r.Port
|
||||
if port == 0 {
|
||||
port = 443
|
||||
}
|
||||
add(r.Target, port)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func hostPortFromURL(raw string) (host string, port uint16, ok bool) {
|
||||
u, err := url.Parse(raw)
|
||||
if err != nil {
|
||||
return "", 0, false
|
||||
}
|
||||
host = u.Hostname()
|
||||
if host == "" {
|
||||
return "", 0, false
|
||||
}
|
||||
if p := u.Port(); p != "" {
|
||||
n, convErr := strconv.ParseUint(p, 10, 16)
|
||||
if convErr != nil {
|
||||
return "", 0, false
|
||||
}
|
||||
return host, uint16(n), true
|
||||
}
|
||||
switch u.Scheme {
|
||||
case "https":
|
||||
return host, 443, true
|
||||
case "http":
|
||||
return host, 80, true
|
||||
}
|
||||
return "", 0, false
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue