Implement context path discovery for CalDav/CardDav endpoint
This change set implements the "context path" discovery for the CalDav/CardDav endpoints. This basically implements the bootstrapping process as defined in RFC6764 section 6, point 2 and 3. What's missing in this implementation is the fallback that is described in point 3, subpoint 3, which says that if the context path discovered in the TXT RR is not reachable the .well-known URI should be used instead. I propose to implement this in a future iteration.
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
@@ -16,7 +17,9 @@ import (
|
||||
)
|
||||
|
||||
// DiscoverContextURL performs a DNS-based CardDAV/CalDAV service discovery as
|
||||
// described in RFC 6352 section 11. It returns the URL to the CardDAV server.
|
||||
// described in RFC 6764. It returns the URL to the CardDAV/CalDAV server.
|
||||
// Specifically it implements points 2 and 3 from the bootstrapping procedure
|
||||
// defined in RFC 6764 section 6.
|
||||
func DiscoverContextURL(ctx context.Context, service, domain string) (string, error) {
|
||||
var resolver net.Resolver
|
||||
|
||||
@@ -31,23 +34,52 @@ func DiscoverContextURL(ctx context.Context, service, domain string) (string, er
|
||||
}
|
||||
|
||||
if len(addrs) == 0 {
|
||||
return "", fmt.Errorf("webdav: domain doesn't have an SRV record")
|
||||
return "", errors.New("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")
|
||||
return "", errors.New("webdav: empty target in SRV record")
|
||||
}
|
||||
|
||||
// TODO: perform a TXT lookup, check for a "path" key in the response
|
||||
u := url.URL{Scheme: "https"}
|
||||
txtName := fmt.Sprintf("_%ss._tcp.%s", service, domain)
|
||||
txtRecords, err := resolver.LookupTXT(ctx, txtName)
|
||||
if dnsErr, ok := err.(*net.DNSError); ok {
|
||||
if dnsErr.IsTemporary {
|
||||
return "", err
|
||||
}
|
||||
} else if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var path string
|
||||
switch len(txtRecords) {
|
||||
case 0:
|
||||
path = "/.well-known/" + service
|
||||
case 1:
|
||||
record := txtRecords[0]
|
||||
if !strings.HasPrefix(record, "path=") {
|
||||
return "", fmt.Errorf("webdav: TXT record for %s does not contain the path key", txtName)
|
||||
}
|
||||
|
||||
path = strings.TrimPrefix(record, "path=")
|
||||
if path == "" {
|
||||
return "", fmt.Errorf("webdav: empty path for %s TXT record", txtName)
|
||||
}
|
||||
default: // more than 1
|
||||
return "", fmt.Errorf("webdav: more than one entry found on %s discovery TXT record", txtName)
|
||||
}
|
||||
|
||||
u := url.URL{
|
||||
Scheme: "https",
|
||||
Path: path,
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user