1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/os/dnssrv.c - Perform DNS SRV queries */
4 * Copyright 1990,2000,2001,2002,2003 by the Massachusetts Institute of Technology.
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
28 #ifdef KRB5_DNS_LOOKUP
33 * Lookup a KDC via DNS SRV records
37 krb5int_free_srv_dns_data (struct srv_dns_entry *p)
39 struct srv_dns_entry *next;
48 /* Construct a DNS label of the form "service.[protocol.]realm.", placing the
49 * result into fixed_buf. protocol may be NULL. */
50 static krb5_error_code
51 prepare_lookup_buf(const krb5_data *realm, const char *service,
52 const char *protocol, char *fixed_buf, size_t bufsize)
56 if (memchr(realm->data, 0, realm->length))
59 k5_buf_init_fixed(&buf, fixed_buf, bufsize);
60 k5_buf_add_fmt(&buf, "%s.", service);
62 k5_buf_add_fmt(&buf, "%s.", protocol);
63 k5_buf_add_len(&buf, realm->data, realm->length);
66 * Realm names don't (normally) end with ".", but if the query doesn't end
67 * with "." and doesn't get an answer as is, the resolv code will try
68 * appending the local domain. Since the realm names are absolutes, let's
72 if (buf.len > 0 && ((char *)buf.data)[buf.len - 1] != '.')
73 k5_buf_add(&buf, ".");
75 return k5_buf_status(&buf);
78 /* Insert new into the list *head, ordering by priority. Weight is not
81 place_srv_entry(struct srv_dns_entry **head, struct srv_dns_entry *new)
83 struct srv_dns_entry *entry;
85 if (*head == NULL || (*head)->priority > new->priority) {
91 for (entry = *head; entry != NULL; entry = entry->next) {
93 * Insert an entry into the next spot if there is no next entry (we're
94 * at the end), or if the next entry has a higher priority (lower
95 * priorities are preferred).
97 if (entry->next == NULL || entry->next->priority > new->priority) {
98 new->next = entry->next;
105 /* Query the URI RR, collecting weight, priority, and target. */
107 k5_make_uri_query(const krb5_data *realm, const char *service,
108 struct srv_dns_entry **answers)
110 const unsigned char *p = NULL, *base = NULL;
112 int size, ret, rdlen;
113 unsigned short priority, weight;
114 struct krb5int_dns_state *ds = NULL;
115 struct srv_dns_entry *head = NULL, *uri = NULL;
119 /* Construct service.realm. */
120 ret = prepare_lookup_buf(realm, service, NULL, host, sizeof(host));
124 size = krb5int_dns_init(&ds, host, C_IN, T_URI);
129 ret = krb5int_dns_nextans(ds, &base, &rdlen);
130 if (ret < 0 || base == NULL)
135 SAFE_GETUINT16(base, rdlen, p, 2, priority, out);
136 SAFE_GETUINT16(base, rdlen, p, 2, weight, out);
138 uri = k5alloc(sizeof(*uri), &ret);
142 uri->priority = priority;
143 uri->weight = weight;
144 /* rdlen - 4 bytes remain after the priority and weight. */
145 uri->host = k5memdup0(p, rdlen - 4, &ret);
146 if (uri->host == NULL) {
151 place_srv_entry(&head, uri);
155 krb5int_dns_fini(ds);
161 * Do DNS SRV query, return results in *answers.
163 * Make a best effort to return all the data we can. On memory or decoding
164 * errors, just return what we've got. Always return 0, currently.
168 krb5int_make_srv_query_realm(const krb5_data *realm,
170 const char *protocol,
171 struct srv_dns_entry **answers)
173 const unsigned char *p = NULL, *base = NULL;
175 int size, ret, rdlen, nlen;
176 unsigned short priority, weight, port;
177 struct krb5int_dns_state *ds = NULL;
178 struct srv_dns_entry *head = NULL, *srv = NULL;
181 * First off, build a query of the form:
183 * service.protocol.realm
185 * which will most likely be something like:
187 * _kerberos._udp.REALM
191 ret = prepare_lookup_buf(realm, service, protocol, host, sizeof(host));
196 fprintf(stderr, "sending DNS SRV query for %s\n", host);
199 size = krb5int_dns_init(&ds, host, C_IN, T_SRV);
204 ret = krb5int_dns_nextans(ds, &base, &rdlen);
205 if (ret < 0 || base == NULL)
210 SAFE_GETUINT16(base, rdlen, p, 2, priority, out);
211 SAFE_GETUINT16(base, rdlen, p, 2, weight, out);
212 SAFE_GETUINT16(base, rdlen, p, 2, port, out);
215 * RFC 2782 says the target is never compressed in the reply;
216 * do we believe that? We need to flatten it anyway, though.
218 nlen = krb5int_dns_expand(ds, p, host, sizeof(host));
219 if (nlen < 0 || !INCR_OK(base, rdlen, p, nlen))
223 * We got everything! Insert it into our list, but make sure
224 * it's in the right order. Right now we don't do anything
225 * with the weight field
228 srv = malloc(sizeof(struct srv_dns_entry));
232 srv->priority = priority;
233 srv->weight = weight;
235 /* The returned names are fully qualified. Don't let the
236 * local resolver code do domain search path stuff. */
237 if (asprintf(&srv->host, "%s.", host) < 0) {
242 place_srv_entry(&head, srv);
246 krb5int_dns_fini(ds);