diff --git a/caldav/client.go b/caldav/client.go index d0e2cbb..e19e84c 100644 --- a/caldav/client.go +++ b/caldav/client.go @@ -16,6 +16,12 @@ import ( "github.com/emersion/go-webdav/internal" ) +// DiscoverContextURL performs a DNS-based CardDAV service discovery as +// described in RFC 6352 section 11. It returns the URL to the CardDAV server. +func DiscoverContextURL(ctx context.Context, domain string) (string, error) { + return internal.DiscoverContextURL(ctx, "caldavs", domain) +} + // Client provides access to a remote CardDAV server. type Client struct { *webdav.Client diff --git a/carddav/client.go b/carddav/client.go index b18b42a..aa6dfce 100644 --- a/carddav/client.go +++ b/carddav/client.go @@ -5,7 +5,6 @@ import ( "context" "fmt" "mime" - "net" "net/http" "net/url" "strconv" @@ -20,37 +19,7 @@ import ( // DiscoverContextURL performs a DNS-based CardDAV service discovery as // described in RFC 6352 section 11. It returns the URL to the CardDAV server. func DiscoverContextURL(ctx context.Context, domain string) (string, error) { - var resolver net.Resolver - - // Only lookup carddavs (not carddav), plaintext connections are insecure - _, addrs, err := resolver.LookupSRV(ctx, "carddavs", "tcp", domain) - if dnsErr, ok := err.(*net.DNSError); ok { - if dnsErr.IsTemporary { - return "", err - } - } else if err != nil { - return "", err - } - - if len(addrs) == 0 { - return "", fmt.Errorf("carddav: domain doesn't have an SRV record") - } - addr := addrs[0] - - target := strings.TrimSuffix(addr.Target, ".") - if target == "" { - return "", fmt.Errorf("carddav: empty target in SRV record") - } - - // TODO: perform a TXT lookup, check for a "path" key in the response - u := url.URL{Scheme: "https"} - if addr.Port == 443 { - u.Host = target - } else { - u.Host = fmt.Sprintf("%v:%v", target, addr.Port) - } - u.Path = "/.well-known/carddav" - return u.String(), nil + return internal.DiscoverContextURL(ctx, "carddavs", domain) } // Client provides access to a remote CardDAV server. diff --git a/internal/client.go b/internal/client.go index 39449ef..718f436 100644 --- a/internal/client.go +++ b/internal/client.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "mime" + "net" "net/http" "net/url" "path" @@ -14,6 +15,42 @@ import ( "unicode" ) +// DiscoverContextURL performs a DNS-based CardDAV/CalDAV service discovery as +// described in RFC 6352 section 11. It returns the URL to the CardDAV server. +func DiscoverContextURL(ctx context.Context, service, domain string) (string, error) { + var resolver net.Resolver + + // Only lookup TLS records, plaintext connections are insecure + _, addrs, err := resolver.LookupSRV(ctx, service+"s", "tcp", domain) + if dnsErr, ok := err.(*net.DNSError); ok { + if dnsErr.IsTemporary { + return "", err + } + } else if err != nil { + return "", err + } + + if len(addrs) == 0 { + return "", fmt.Errorf("webdav: domain doesn't have an SRV record") + } + addr := addrs[0] + + target := strings.TrimSuffix(addr.Target, ".") + if target == "" { + return "", fmt.Errorf("webdav: empty target in SRV record") + } + + // TODO: perform a TXT lookup, check for a "path" key in the response + u := url.URL{Scheme: "https"} + if addr.Port == 443 { + u.Host = target + } else { + u.Host = fmt.Sprintf("%v:%v", target, addr.Port) + } + u.Path = "/.well-known/" + service + return u.String(), nil +} + // HTTPClient performs HTTP requests. It's implemented by *http.Client. type HTTPClient interface { Do(req *http.Request) (*http.Response, error)