2 * lib/krb5/os/locate_kdc.c
4 * Copyright 1990,2000,2001,2002 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.
27 * get socket addresses for KDC.
30 #include "fake-addrinfo.h"
34 #ifdef KRB5_DNS_LOOKUP
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include <arpa/nameser.h>
48 /* for old Unixes and friends ... */
49 #ifndef MAXHOSTNAMELEN
50 #define MAXHOSTNAMELEN 64
53 #define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1)
55 #if KRB5_DNS_LOOKUP_KDC
56 #define DEFAULT_LOOKUP_KDC 1
58 #define DEFAULT_LOOKUP_KDC 0
60 #if KRB5_DNS_LOOKUP_REALM
61 #define DEFAULT_LOOKUP_REALM 1
63 #define DEFAULT_LOOKUP_REALM 0
67 maybe_use_dns (krb5_context context, const char *name, int defalt)
73 code = profile_get_string(context->profile, "libdefaults",
75 if (value == 0 && code == 0)
76 code = profile_get_string(context->profile, "libdefaults",
77 "dns_fallback", 0, 0, &value);
84 use_dns = _krb5_conf_boolean(value);
85 profile_release_string(value);
90 _krb5_use_dns_kdc(krb5_context context)
92 return maybe_use_dns (context, "dns_lookup_kdc", DEFAULT_LOOKUP_KDC);
96 _krb5_use_dns_realm(krb5_context context)
98 return maybe_use_dns (context, "dns_lookup_realm", DEFAULT_LOOKUP_REALM);
101 #endif /* KRB5_DNS_LOOKUP */
103 static int get_port (const char *service, int stream, int defalt)
105 #if 0 /* Only used for "kerberos" and "kerberos-sec", and we want the
106 right port numbers even on the OSes that botch the entries in
107 /etc/services. So don't bother with the lookup, except maybe
108 to produce a warning. */
109 struct addrinfo hints = { 0 };
113 hints.ai_family = PF_INET;
114 hints.ai_socktype = stream ? SOCK_STREAM : SOCK_DGRAM;
115 err = getaddrinfo (NULL, service, &hints, &ai);
116 if (err == 0 && ai != 0) {
117 if (ai->ai_addr->sa_family == AF_INET) {
118 int port = ((struct sockaddr_in *)ai->ai_addr)->sin_port;
125 /* Any error - don't complain, just use default. */
126 return htons (defalt);
130 krb5int_grow_addrlist (struct addrlist *lp, int nmore)
133 int newspace = lp->space + nmore;
134 size_t newsize = newspace * sizeof (struct addrlist);
135 struct addrinfo **newaddrs;
137 /* NULL check a concession to SunOS4 compatibility for now; not
138 required for pure ANSI support. */
140 newaddrs = realloc (lp->addrs, newsize);
142 newaddrs = malloc (newsize);
144 if (newaddrs == NULL)
146 for (i = lp->space; i < newspace; i++)
148 lp->addrs = newaddrs;
149 lp->space = newspace;
152 #define grow_list krb5int_grow_addrlist
154 /* Free up everything pointed to by the addrlist structure, but don't
155 free the structure itself. */
157 krb5int_free_addrlist (struct addrlist *lp)
160 for (i = 0; i < lp->naddrs; i++)
161 freeaddrinfo (lp->addrs[i]);
164 lp->naddrs = lp->space = 0;
166 #define free_list krb5int_free_addrlist
168 static int translate_ai_error (int err)
177 /* All of these indicate bad inputs to getaddrinfo. */
180 /* Translate to standard errno code. */
183 /* Translate to standard errno code. */
185 #ifdef EAI_ADDRFAMILY
188 #if EAI_NODATA != EAI_NONAME
192 /* Name not known or no address data, but no error. Do
197 /* System error, obviously. */
201 /* An error code we haven't handled? */
207 static inline void Tprintf(const char *fmt, ...)
212 vfprintf(stderr, fmt, ap);
217 static int add_addrinfo_to_list (struct addrlist *lp, struct addrinfo *a)
221 switch (a->ai_socktype) {
223 Tprintf("\tdgram\n");
226 Tprintf("\tstream\n");
234 Tprintf("\tsocket type %d\n", a->ai_socktype);
238 if (lp->naddrs == lp->space) {
239 err = grow_list (lp, 1);
241 Tprintf ("grow_list failed %d\n", err);
245 lp->addrs[lp->naddrs++] = a;
247 Tprintf ("count is now %d\n", lp->naddrs);
251 #define add_host_to_list krb5int_add_host_to_list
254 krb5int_add_host_to_list (struct addrlist *lp, const char *hostname,
255 int port, int secport,
256 int socktype, int family)
258 struct addrinfo *addrs, *a, *anext, hint;
260 char portbuf[10], secportbuf[10];
262 Tprintf ("adding hostname %s, ports %d,%d, family %d, socktype %d\n",
263 hostname, ntohs (port), ntohs (secport),
266 memset(&hint, 0, sizeof(hint));
267 hint.ai_family = family;
268 hint.ai_socktype = socktype;
269 #ifdef AI_NUMERICSERV
270 hint.ai_flags = AI_NUMERICSERV;
272 sprintf(portbuf, "%d", ntohs(port));
273 sprintf(secportbuf, "%d", ntohs(secport));
274 err = getaddrinfo (hostname, portbuf, &hint, &addrs);
276 Tprintf ("\tgetaddrinfo(\"%s\", \"%s\", ...)\n\treturns %d: %s\n",
277 hostname, portbuf, err, gai_strerror (err));
278 return translate_ai_error (err);
281 for (a = addrs; a != 0 && err == 0; a = anext) {
283 err = add_addrinfo_to_list (lp, a);
285 if (err || secport == 0)
288 socktype = SOCK_DGRAM;
289 else if (socktype != SOCK_DGRAM)
291 hint.ai_family = AF_INET;
292 err = getaddrinfo (hostname, secportbuf, &hint, &addrs);
294 err = translate_ai_error (err);
297 for (a = addrs; a != 0 && err == 0; a = anext) {
299 err = add_addrinfo_to_list (lp, a);
303 freeaddrinfo (anext);
308 * returns count of number of addresses found
309 * if master is non-NULL, it is filled in with the index of
313 static krb5_error_code
314 krb5_locate_srv_conf_1(krb5_context context, const krb5_data *realm,
315 const char * name, struct addrlist *addrlist,
316 int get_masters, int socktype,
317 int udpport, int sec_udpport, int family)
319 const char *realm_srv_names[4];
320 char **masterlist, **hostlist, *host, *port, *cp;
321 krb5_error_code code;
322 int i, j, count, ismaster;
324 Tprintf ("looking in krb5.conf for realm %s entry %s; ports %d,%d\n",
325 realm->data, name, ntohs (udpport), ntohs (sec_udpport));
327 if ((host = malloc(realm->length + 1)) == NULL)
330 strncpy(host, realm->data, realm->length);
331 host[realm->length] = '\0';
336 realm_srv_names[0] = "realms";
337 realm_srv_names[1] = host;
338 realm_srv_names[2] = name;
339 realm_srv_names[3] = 0;
341 code = profile_get_values(context->profile, realm_srv_names, &hostlist);
344 Tprintf ("config file lookup failed: %s\n",
345 error_message(code));
346 if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
347 code = KRB5_REALM_UNKNOWN;
353 while (hostlist && hostlist[count])
355 Tprintf ("found %d entries under 'kdc'\n", count);
358 profile_free_list(hostlist);
360 addrlist->naddrs = 0;
365 realm_srv_names[0] = "realms";
366 realm_srv_names[1] = host;
367 realm_srv_names[2] = "admin_server";
368 realm_srv_names[3] = 0;
370 code = profile_get_values(context->profile, realm_srv_names,
376 for (i=0; masterlist[i]; i++) {
377 host = masterlist[i];
380 * Strip off excess whitespace
382 cp = strchr(host, ' ');
385 cp = strchr(host, '\t');
388 cp = strchr(host, ':');
397 /* at this point, if master is non-NULL, then either the master kdc
398 is required, and there is one, or the master kdc is not required,
399 and there may or may not be one. */
401 #ifdef HAVE_NETINET_IN_H
406 for (i=0; hostlist[i]; i++) {
410 Tprintf ("entry %d is '%s'\n", i, host);
412 * Strip off excess whitespace
414 cp = strchr(host, ' ');
417 cp = strchr(host, '\t');
420 port = strchr(host, ':');
428 for (j=0; masterlist[j]; j++) {
429 if (strcasecmp(hostlist[i], masterlist[j]) == 0) {
435 if (get_masters && !ismaster)
442 l = strtoul (port, &endptr, 10);
443 if (endptr == NULL || *endptr != 0)
448 /* L is unsigned, don't need to check <0. */
459 code = add_host_to_list (addrlist, hostlist[i], p1, p2,
462 code = add_host_to_list (addrlist, hostlist[i], p1, p2,
465 code = add_host_to_list (addrlist, hostlist[i], p1, p2,
466 SOCK_STREAM, family);
469 Tprintf ("error %d (%s) returned from add_host_to_list\n", code,
470 error_message (code));
472 profile_free_list (hostlist);
474 profile_free_list (masterlist);
480 profile_free_list(hostlist);
482 profile_free_list(masterlist);
488 static krb5_error_code
489 krb5_locate_srv_conf(krb5_context context, const krb5_data *realm,
490 const char *name, struct addrlist *al, int get_masters,
491 int udpport, int sec_udpport)
495 ret = krb5_locate_srv_conf_1 (context, realm, name, al,
496 get_masters, 0, udpport, sec_udpport, 0);
499 if (al->naddrs == 0) /* Couldn't resolve any KDC names */
500 return KRB5_REALM_CANT_RESOLVE;
505 #ifdef KRB5_DNS_LOOKUP
506 static krb5_error_code
507 krb5_locate_srv_dns_1 (const krb5_data *realm,
509 const char *protocol,
510 struct addrlist *addrlist,
513 struct srv_dns_entry *head = NULL;
514 struct srv_dns_entry *entry = NULL, *next;
515 krb5_error_code code = 0;
517 code = krb5int_make_srv_query_realm(realm, service, protocol, &head);
522 * Okay! Now we've got a linked list of entries sorted by
523 * priority. Start looking up A records and returning
530 /* Check for the "." case indicating no support. */
531 if (head->next == 0 && head->host[0] == 0) {
534 return KRB5_ERR_NO_SERVICE;
537 Tprintf ("walking answer list:\n");
538 for (entry = head; entry != NULL; entry = next) {
539 Tprintf ("\tport=%d host=%s\n", entry->port, entry->host);
541 code = add_host_to_list (addrlist, entry->host, htons (entry->port), 0,
542 (strcmp("_tcp", protocol)
544 : SOCK_STREAM), family);
557 krb5int_free_srv_dns_data(head);
563 * Wrapper function for the two backends
567 krb5int_locate_server (krb5_context context, const krb5_data *realm,
568 struct addrlist *addrlist,
570 const char *profname, const char *dnsname,
572 /* network order port numbers! */
573 int dflport1, int dflport2,
576 krb5_error_code code;
577 struct addrlist al = ADDRLIST_INIT;
582 * We always try the local file first
585 code = krb5_locate_srv_conf_1(context, realm, profname, &al, get_masters,
586 socktype, dflport1, dflport2, family);
588 #ifdef KRB5_DNS_LOOKUP
589 if (code && dnsname != 0) {
590 int use_dns = _krb5_use_dns_kdc(context);
593 if (socktype == SOCK_DGRAM || socktype == 0) {
594 code = krb5_locate_srv_dns_1(realm, dnsname, "_udp",
597 Tprintf("dns udp lookup returned error %d\n", code);
599 if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) {
600 code = krb5_locate_srv_dns_1(realm, dnsname, "_tcp",
603 Tprintf("dns tcp lookup returned error %d\n", code);
607 #endif /* KRB5_DNS_LOOKUP */
609 Tprintf ("krb5int_locate_server found %d addresses\n",
612 Tprintf ("krb5int_locate_server returning error code %d\n",
619 if (al.naddrs == 0) { /* No good servers */
622 return KRB5_REALM_CANT_RESOLVE;
629 krb5_locate_kdc(krb5_context context, const krb5_data *realm,
630 struct addrlist *addrlist,
631 int get_masters, int socktype, int family)
633 int udpport, sec_udpport;
635 udpport = get_port (KDC_PORTNAME, 0, KRB5_DEFAULT_PORT);
636 if (socktype == SOCK_STREAM)
639 sec_udpport = get_port (KDC_SECONDARY_PORTNAME, 0,
640 (udpport == htons (KRB5_DEFAULT_PORT)
641 ? KRB5_DEFAULT_SEC_PORT
642 : KRB5_DEFAULT_PORT));
643 if (sec_udpport == udpport)
647 return krb5int_locate_server(context, realm, addrlist, 0,
648 get_masters ? "master_kdc" : "kdc",
652 socktype, udpport, sec_udpport, family);