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 }