1781Sgtb /*
2781Sgtb * lib/krb5/os/dnssrv.c
3781Sgtb *
4781Sgtb * Copyright 1990,2000,2001,2002,2003 by the Massachusetts Institute of Technology.
5781Sgtb * All Rights Reserved.
6781Sgtb *
7781Sgtb * Export of this software from the United States of America may
8781Sgtb * require a specific license from the United States Government.
9781Sgtb * It is the responsibility of any person or organization contemplating
10781Sgtb * export to obtain such a license before exporting.
11781Sgtb *
12781Sgtb * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13781Sgtb * distribute this software and its documentation for any purpose and
14781Sgtb * without fee is hereby granted, provided that the above copyright
15781Sgtb * notice appear in all copies and that both that copyright notice and
16781Sgtb * this permission notice appear in supporting documentation, and that
17781Sgtb * the name of M.I.T. not be used in advertising or publicity pertaining
18781Sgtb * to distribution of the software without specific, written prior
19781Sgtb * permission. Furthermore if you modify this software you must label
20781Sgtb * your software as modified software and not distribute it in such a
21781Sgtb * fashion that it might be confused with the original M.I.T. software.
22781Sgtb * M.I.T. makes no representations about the suitability of
23781Sgtb * this software for any purpose. It is provided "as is" without express
24781Sgtb * or implied warranty.
25781Sgtb *
26781Sgtb *
27781Sgtb * do DNS SRV RR queries
28781Sgtb */
29781Sgtb
30*7934SMark.Phalan@Sun.COM #include "autoconf.h"
31781Sgtb #ifdef KRB5_DNS_LOOKUP
32781Sgtb
33781Sgtb #include "dnsglue.h"
34781Sgtb
35781Sgtb /*
36781Sgtb * Lookup a KDC via DNS SRV records
37781Sgtb */
38781Sgtb
krb5int_free_srv_dns_data(struct srv_dns_entry * p)39781Sgtb void krb5int_free_srv_dns_data (struct srv_dns_entry *p)
40781Sgtb {
41781Sgtb struct srv_dns_entry *next;
42781Sgtb while (p) {
43781Sgtb next = p->next;
44781Sgtb free(p->host);
45781Sgtb free(p);
46781Sgtb p = next;
47781Sgtb }
48781Sgtb }
49781Sgtb
50781Sgtb /* Do DNS SRV query, return results in *answers.
51781Sgtb
52781Sgtb Make best effort to return all the data we can. On memory or
53781Sgtb decoding errors, just return what we've got. Always return 0,
54781Sgtb currently. */
55781Sgtb
56781Sgtb krb5_error_code
krb5int_make_srv_query_realm(const krb5_data * realm,const char * service,const char * protocol,struct srv_dns_entry ** answers)57781Sgtb krb5int_make_srv_query_realm(const krb5_data *realm,
58781Sgtb const char *service,
59781Sgtb const char *protocol,
60781Sgtb struct srv_dns_entry **answers)
61781Sgtb {
62781Sgtb const unsigned char *p = NULL, *base = NULL;
63781Sgtb char host[MAXDNAME], *h;
64781Sgtb int size, ret, rdlen, nlen;
65781Sgtb unsigned short priority, weight, port;
66781Sgtb struct krb5int_dns_state *ds = NULL;
67781Sgtb
68781Sgtb struct srv_dns_entry *head = NULL;
69781Sgtb struct srv_dns_entry *srv = NULL, *entry = NULL;
70781Sgtb
71781Sgtb /*
72781Sgtb * First off, build a query of the form:
73781Sgtb *
74781Sgtb * service.protocol.realm
75781Sgtb *
76781Sgtb * which will most likely be something like:
77781Sgtb *
78781Sgtb * _kerberos._udp.REALM
79781Sgtb *
80781Sgtb */
81781Sgtb
82781Sgtb if (memchr(realm->data, 0, realm->length))
83781Sgtb return 0;
84781Sgtb if ( strlen(service) + strlen(protocol) + realm->length + 6
85781Sgtb > MAXDNAME )
86781Sgtb return 0;
87781Sgtb sprintf(host, "%s.%s.%.*s", service, protocol, (int) realm->length,
88781Sgtb realm->data);
89781Sgtb
90781Sgtb /* Realm names don't (normally) end with ".", but if the query
91781Sgtb doesn't end with "." and doesn't get an answer as is, the
92781Sgtb resolv code will try appending the local domain. Since the
93781Sgtb realm names are absolutes, let's stop that.
94781Sgtb
95781Sgtb But only if a name has been specified. If we are performing
96781Sgtb a search on the prefix alone then the intention is to allow
97781Sgtb the local domain or domain search lists to be expanded. */
98781Sgtb
99781Sgtb h = host + strlen (host);
100781Sgtb if ((h[-1] != '.') && ((h - host + 1) < sizeof(host)))
101781Sgtb strcpy (h, ".");
102781Sgtb
103781Sgtb #ifdef TEST
104781Sgtb fprintf (stderr, "sending DNS SRV query for %s\n", host);
105781Sgtb #endif
106781Sgtb
107781Sgtb size = krb5int_dns_init(&ds, host, C_IN, T_SRV);
108781Sgtb if (size < 0)
109781Sgtb goto out;
110781Sgtb
111781Sgtb for (;;) {
112781Sgtb ret = krb5int_dns_nextans(ds, &base, &rdlen);
113781Sgtb if (ret < 0 || base == NULL)
114781Sgtb goto out;
115781Sgtb
116781Sgtb p = base;
117781Sgtb
118781Sgtb SAFE_GETUINT16(base, rdlen, p, 2, priority, out);
119781Sgtb SAFE_GETUINT16(base, rdlen, p, 2, weight, out);
120781Sgtb SAFE_GETUINT16(base, rdlen, p, 2, port, out);
121781Sgtb
122781Sgtb /*
123781Sgtb * RFC 2782 says the target is never compressed in the reply;
124781Sgtb * do we believe that? We need to flatten it anyway, though.
125781Sgtb */
126781Sgtb nlen = krb5int_dns_expand(ds, p, host, sizeof(host));
127781Sgtb if (nlen < 0 || !INCR_OK(base, rdlen, p, nlen))
128781Sgtb goto out;
129781Sgtb
130781Sgtb /*
131781Sgtb * We got everything! Insert it into our list, but make sure
132781Sgtb * it's in the right order. Right now we don't do anything
133781Sgtb * with the weight field
134781Sgtb */
135781Sgtb
136781Sgtb srv = (struct srv_dns_entry *) malloc(sizeof(struct srv_dns_entry));
137781Sgtb if (srv == NULL)
138781Sgtb goto out;
139781Sgtb
140781Sgtb srv->priority = priority;
141781Sgtb srv->weight = weight;
142781Sgtb srv->port = port;
143781Sgtb /* The returned names are fully qualified. Don't let the
144781Sgtb local resolver code do domain search path stuff. */
145781Sgtb if (strlen(host) + 2 < sizeof(host))
146781Sgtb strcat(host, ".");
147781Sgtb srv->host = strdup(host);
148781Sgtb if (srv->host == NULL) {
149781Sgtb free(srv);
150781Sgtb goto out;
151781Sgtb }
152781Sgtb
153781Sgtb if (head == NULL || head->priority > srv->priority) {
154781Sgtb srv->next = head;
155781Sgtb head = srv;
156781Sgtb } else {
157781Sgtb /*
158781Sgtb * This is confusing. Only insert an entry into this
159781Sgtb * spot if:
160781Sgtb * The next person has a higher priority (lower priorities
161781Sgtb * are preferred).
162781Sgtb * Or
163781Sgtb * There is no next entry (we're at the end)
164781Sgtb */
165781Sgtb for (entry = head; entry != NULL; entry = entry->next) {
166781Sgtb if ((entry->next &&
167781Sgtb entry->next->priority > srv->priority) ||
168781Sgtb entry->next == NULL) {
169781Sgtb srv->next = entry->next;
170781Sgtb entry->next = srv;
171781Sgtb break;
172781Sgtb }
173781Sgtb }
174781Sgtb }
175781Sgtb }
176781Sgtb
177781Sgtb out:
178781Sgtb if (ds != NULL) {
179781Sgtb krb5int_dns_fini(ds);
180781Sgtb ds = NULL;
181781Sgtb }
182781Sgtb *answers = head;
183781Sgtb return 0;
184781Sgtb }
185781Sgtb #endif
186