Home:Professional:iCalendar:Autolocation

CalDAV Autolocation

CalDAV autolocation diagram

Creative Commons License

This diagram attempts to make sense of the CalDAV service autolocation process described in RFC 6764 Locating Services for Calendaring Extensions to WebDAV (CalDAV) and vCard Extensions to WebDAV (CardDAV) and, to a lesser extent, RFC 4791. The RFC is, in my opinion, not clear for a number of reasons; primarily in that the autolocation process is described haphazardly in bits and pieces that are scattered throughout the document. The diagram is my attempt to bring the separate pieces together.

Another problem with RFC 6764 is that the concept of context path, while central to the autolocation process, is never really clearly defined and only alluded to— in double quotes, as if the term is somehow slightly embarrassing and best avoided among polite company. In fact, the CalDAV context path can be understood simply as the starting point for the CalDAV service —the front door, so to speak— on a server that may be hosting other services alongside it. A service responding at the context path is, strictly speaking, offering the CalDAV protocol, but you shouldn't expect much to be happening there other than property queries about the calendar service in general and the current-user-principal in particular.

In addition there is the related concept of initial context path which is even more obliquely referred to. This is also a path on the server, whose only function is to support an HTTP redirect to the actual context path. No CalDAV service is present at this initial context path.

Lastly, I find that the RFC contradicts itself in how it prescribes whether a path determined from a DNS TXT path lookup should be interpreted as a ‘context path’ or merely as an ‘initial context path’.

Perhaps this is nitpicking but, to me, this adds to the overall confusion of an already complex set of interlocking autolocation processes described in the RFC. I hope my diagram is useful in clarifying it.

① Starting Point: Domain Name

This starting point represents the mostan extreme starting point in autolocation where only some kind of domain name is known, without knowing whether the calendar is even served from that domain: for example, hekster.org or icloud.com. This situation is described in RFC 6764 §6 which actually assumes some kind of URI from which the domain name can be extracted.

Note that we're disregarding at this point the username and credentials which are nearly completely orthogonal to service location.

hekster.org
icloud.com

② Use DNS SRV to Locate Service Host Name and Port

Following § 3 we first look up a DNS SRV record that specifies the hostname and port of a CalDAV server for the domain. Note on the right that for my personal domain, an SRV record is available which shows that the CalDAV server for that domain is actually located at port 2080 on server213.web-hosting.com. This isn't too surprising since I'm renting space from a hosting provider and undoubtedly sharing server hardware with many other tenants.

Perhaps more predicably, icloud.com redirects to a subdomain.

$ dig -t SRV _caldavs._tcp.hekster.org
...
;; ANSWER SECTION:
_caldavs._tcp.hekster.org. 1200 IN      SRV     0 0 2080 server213.web-hosting.com.
$ dig -t SRV _caldavs._tcp.icloud.com
...
;; ANSWER SECTION:
_caldavs._tcp.icloud.com. 3506  IN      SRV     0 0 443 caldav.icloud.com.

③ Use DNS TXT to Locate Service Context Path

We continue with DNS-based autolocation for my personal domain. §4 describes the notion of a ‘context path’ which is an HTTP path on the HTTP server where an actual CalDAV service can be found. This accomodates situations where the server may be hosting other, non-CalDAV, HTTP services alongside. We're looking for, and find, a path key that specifies the context path. In this case, the context path is simply the HTTP root of the server.

Since icloud.com has no TXT record, we will have to jump to step ⑥; this is also the case if there had been a record but no path key. We'll return to this later.

$ dig -t TXT _caldavs._tcp.hekster.org
...
;; ANSWER SECTION:
_caldavs._tcp.hekster.org. 1200 IN      TXT     "path=/"
$ dig -t TXT _caldavs._tcp.icloud.com
...

④ Find Current User Principal Path

RFC 5397 WebDAV Current Principal Extension defines a ‘current user principal’ resource; this should be interpreted as ‘the principal resource’ for “the currently authenticated user.” The path to the current user principal resource can be queried as a WebDAV property at the CalDAV context path we found before, as dictated by RFC 6764 §5.

Since this is a resource explicitly associated with a specific user, you should expect to have to authenticate here; which is why we provide a username and password. These must have been obtained somewhere, which isn't explcitly shown in the diagram since credentials are really a feature of the service itself and not of autolocation. Again, expect the specific value of the ‘current user principal’ property to differ from user to user.

curl -X PROPFIND \
	--header 'Content-Type: application/xml' \
	--header 'Depth: 0' \
	--user 'the-user-name:the-password' \
	--data @- \
	https://server213.web-hosting.com:2080/ <<EOF
<?xml version='1.0' encoding='UTF-8'?>
<D:propfind xmlns:D="DAV:">
	<D:prop>
		<D:current-user-principal/>
	</D:prop>
</D:propfind>
EOF
<?xml version='1.0' encoding='UTF-8'?>
<multistatus xmlns='DAV:'>
  <response>
    <href>/</href>
    <propstat>
      <prop>
        <current-user-principal>
          <href>/principals/__uids__/01234567-89AB-CDEF-0123-456789ABCDEF/</href>
        </current-user-principal>
      </prop>
      <status>HTTP/1.1 200 OK</status>
    </propstat>
  </response>
</multistatus>

While §4 states that

The value of the [DNS TXT path] key MUST be the actual "context path" to the corresponding service on the server
implying that HTTP redirects are not to be expected, this is contradicted in §6.3 which says
If present, the value of the “path” key is used for the initial “context path”.
(note initial) which would imply the possibility of redirects, and §5 requires unequivocally that
clients MUST properly handle HTTP redirect responses for the [current-user-principal PROPFIND] request.
See ⑥ for a description of the concept of an ‘initial context path’.

Also, on the matter of error handling RFC 6764 is somewhat confusing. According to §3;

If the initial "context path" derived from a TXT record generates HTTP errors when targeted by requests, the client SHOULD repeat its "bootstrapping" procedure using the appropriate ".well-known" URI instead.
and go to ⑦. However §5 states that
If the server returns a 404 ("Not Found") HTTP status response to the request on the initial "context path", clients MAY try repeating the request on the "root" URI "/" or prompt the user for a suitable path
suggesting, in addition, ⑨ and ⑪.

⑤ Well-Known Service Port

Returning now to the case where DNS-based autolocation is completely absent, §6.2 says to use “some other heuristic” to find the service host and port. In the absence of specific direction from the RFC, it seems to me most prudent to try the domain name provided by the user in ① and the well-known port 443. Note that I'm completely ignoring non-HTTPS service in this description: a strict reading of the specification might require consideration of plain HTTP, but I think modern-day expectations and the (in)security implications render the notion of dealing with personal information (or really, anything at all) over HTTP obsolete.

⑥ Well-Known Initial Context Path

Continuing with the case where DNS-based autolocation is absent (failure of ②), a context path cannot be determined from DNS (failure of ③), or reading the ‘current user principal’ at the DNS-located context path results in HTTP errors (failure of ④), §6.3 provides for a fixed ‘well-known’ path /.well-known/caldav, applying the more general mechanism defined in RFC 8615 Well-Known Uniform Resource Identifiers (URIs)

The path may be used to make HTTP requests on, but the request may not be answered there and rather must redirect elsewhere. For this reason the well-known path is referred to as merely an ‘initial context path’ which will redirect to the ‘actual context path’

⑦ Find Current User Principal Path Using an Initial Context Path

Finding the ‘current user principal’ property from an ‘initial’ context path is essentially the same as from an ‘actual’ path (as in ④) except that, as mentioned in ⑥, HTTP redirects are expected and must be handled.

Looking at the response now for icloud.com we see that, contrary to the admonition of §6.5 that

The server MUST redirect HTTP requests for that resource to the actual "context path" using one of the available mechanisms provided by HTTP
and that
servers MUST NOT locate the actual CalDAV or CardDAV service endpoint at the ".well-known" URI
that iCloud does exactly that. I see this as confirmation that RFC 6764 is confusing at best and can only be properly read as a loose collection of heuristics.

Note also that iCloud returns the ‘current user principal’ in both the href of the current-user-principal property and the href of the response. Since we're querying for a property value, we expect the result as well, according to the rules of WebDAV, to be returned in the property. The href of the response is supposed to be related to the resource on which the property request was made. Note also that this is exactly how the response was returned in ④; so I think that iCloud is here again in error. In this case I don't think the WebDAV specification can be blamed; possibly it's done to support broken clients, or maybe it's just a bug. In any case, make sure to use the correct href.

curl -X PROPFIND \
        --dump-header - \
        --header 'Content-Type: application/xml' \
        --header 'Depth: 0' \
	--user 'the-user-name:the-password' \
        --data @- \
        https://caldav.icloud.com:443/.well-known/caldav/ <<EOF
<?xml version='1.0' encoding='UTF-8'?>
<D:propfind xmlns:D="DAV:">
        <D:prop>
                <D:current-user-principal/>
        </D:prop>
</D:propfind>
EOF
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<multistatus xmlns="DAV:">
<response xmlns="DAV:">
	<href>/123456789/principal/</href>
	<propstat>
		<prop>
			<current-user-principal xmlns="DAV:">
				<href xmlns="DAV:">/123456789/principal/</href>
			</current-user-principal>
		</prop>
		<status>HTTP/1.1 200 OK</status>
	</propstat>
</response>

⑧ User Provides Domain Name and Port

TCP errors in the previous step (i.e., failure to even establish a connection) suggest that we either still do not have the port or the initial context path right. The RFC points here and there to prompting a user in such cases. If this is possible and the user provides a hostname and a port, the procedure can be continued from this step. Of course, if the user provided an explicit port number from the outset, this implies that the domain name that was also provided was actually the service hostname and that we should start here in any case and sidestep DNS autolocation.

With an explicit hostname and port number but without the initial context path, we can (re)try ⑥ and ⑦.

⑨ User Provides Domain Name and Port, with Well-Known Initial Context Path Fails

HTTP errors in step ⑦ (i.e., connection was established but we're not getting the expected service response) suggests that the ‘well-known’ initial context path is not right. §6.5 says that in the specific case of 404/‘not found’ we can retry at the root. This would cover a really spartan server configuration without DNS or HTTP autolocation that just locates CalDAV at the root. I haven't encountered one of those yet.

⑩ Find Current User Principal Path Using a Root Initial Context Path

This is like ⑦, but if we still cannot make even a TCP connection then the user-provided hostname and port, and guessed path are wrong. The diagram terminates in this case, but a program with a user interface might of course retry prompting the user rather than failing.

Establishing a connection but failing on the HTTP request suggests that we may have the hostname and port right but that the path is still wrong. If we have a user interface, and we haven't already prompted the user or we don't mind prompting again, we can go to ⑪ and do so in the hope of obtaining a path for the initial context.

⑪ User Provides Domain Name, Port, and Initial Context Path

This step represents the state where input from or prompting of the user provided an explicit hostname, path, and possibly a port number. In this case we sidestep all of the the abovementioned autolocation.

⑫ Find Current User Principal Path Using Explicit Host and Path

This is again the same as ⑦, except that any failures here require us to give up or go back to the user for different information. Note that since we must continue to expect and support HTTP redirection from the explicitly provided initial context path, there is technically potentially still some ‘autolocation’ going on here.

All pages under this domain © Copyright 1999-2023 by: Ben Hekster