package dav import ( "net/url" "strconv" sdk "git.happydns.org/checker-sdk-go/checker" ) // DiscoverEndpoints derives TLS endpoints worth handing off to downstream // checkers (notably the dedicated TLS checker) from a completed Observation. // // A CalDAV/CardDAV context URL always implies a direct-TLS HTTPS endpoint, so // we emit a single `tls` entry for the resolved context URL's host:port. If // the endpoint was reached via SRV, we also surface each SRV target as its // own endpoint — those are the names operators actually need certificates on, // and they may differ from the queried domain. func DiscoverEndpoints(obs *Observation) []sdk.DiscoveredEndpoint { if obs == nil || obs.Discovery.ContextURL == "" { return nil } var out []sdk.DiscoveredEndpoint seen := map[string]struct{}{} add := func(host string, port uint16, sni string) { if host == "" || port == 0 { return } key := host + ":" + strconv.Itoa(int(port)) if _, dup := seen[key]; dup { return } seen[key] = struct{}{} ep := sdk.DiscoveredEndpoint{ Type: "tls", Host: host, Port: port, } if sni != "" && sni != host { ep.SNI = sni } out = append(out, ep) } // Primary endpoint: the resolved context URL. if host, port, ok := hostPortFromURL(obs.Discovery.ContextURL); ok { add(host, port, "") } // Secondary endpoints: every TLS SRV target. Clients may connect to any // of them per weight/priority, and all of them need a valid certificate. for _, r := range obs.Discovery.SecureSRV { port := r.Port if port == 0 { port = 443 } add(r.Target, port, "") } return out } // hostPortFromURL extracts the (host, port) pair from an absolute URL. The // port defaults to 443 for https and 80 for http. Returns ok=false for // malformed URLs so callers can silently skip them. 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 }