caldav: add DiscoverContextURL

This commit is contained in:
Simon Ser 2023-12-27 23:16:49 +01:00
parent 174622c1eb
commit 7e076258d6
3 changed files with 44 additions and 32 deletions

View File

@ -16,6 +16,12 @@ import (
"github.com/emersion/go-webdav/internal" "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. // Client provides access to a remote CardDAV server.
type Client struct { type Client struct {
*webdav.Client *webdav.Client

View File

@ -5,7 +5,6 @@ import (
"context" "context"
"fmt" "fmt"
"mime" "mime"
"net"
"net/http" "net/http"
"net/url" "net/url"
"strconv" "strconv"
@ -20,37 +19,7 @@ import (
// DiscoverContextURL performs a DNS-based CardDAV service discovery as // DiscoverContextURL performs a DNS-based CardDAV service discovery as
// described in RFC 6352 section 11. It returns the URL to the CardDAV server. // described in RFC 6352 section 11. It returns the URL to the CardDAV server.
func DiscoverContextURL(ctx context.Context, domain string) (string, error) { func DiscoverContextURL(ctx context.Context, domain string) (string, error) {
var resolver net.Resolver return internal.DiscoverContextURL(ctx, "carddavs", domain)
// 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
} }
// Client provides access to a remote CardDAV server. // Client provides access to a remote CardDAV server.

View File

@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"io" "io"
"mime" "mime"
"net"
"net/http" "net/http"
"net/url" "net/url"
"path" "path"
@ -14,6 +15,42 @@ import (
"unicode" "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. // HTTPClient performs HTTP requests. It's implemented by *http.Client.
type HTTPClient interface { type HTTPClient interface {
Do(req *http.Request) (*http.Response, error) Do(req *http.Request) (*http.Response, error)