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.
31 #include "fake-addrinfo.h"
35 #ifdef KRB5_DNS_LOOKUP
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41 #include <arpa/nameser.h>
49 /* for old Unixes and friends ... */
50 #ifndef MAXHOSTNAMELEN
51 #define MAXHOSTNAMELEN 64
54 #define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1)
56 #if KRB5_DNS_LOOKUP_KDC
57 #define DEFAULT_LOOKUP_KDC 1
59 #define DEFAULT_LOOKUP_KDC 0
61 #if KRB5_DNS_LOOKUP_REALM
62 #define DEFAULT_LOOKUP_REALM 1
64 #define DEFAULT_LOOKUP_REALM 0
68 maybe_use_dns (krb5_context context, const char *name, int defalt)
74 code = profile_get_string(context->profile, "libdefaults",
76 if (value == 0 && code == 0)
77 code = profile_get_string(context->profile, "libdefaults",
78 "dns_fallback", 0, 0, &value);
85 use_dns = _krb5_conf_boolean(value);
86 profile_release_string(value);
91 _krb5_use_dns_kdc(krb5_context context)
93 return maybe_use_dns (context, "dns_lookup_kdc", DEFAULT_LOOKUP_KDC);
97 _krb5_use_dns_realm(krb5_context context)
99 return maybe_use_dns (context, "dns_lookup_realm", DEFAULT_LOOKUP_REALM);
102 #endif /* KRB5_DNS_LOOKUP */
104 static int get_port (const char *service, int stream, int defalt)
106 #if 0 /* Only used for "kerberos" and "kerberos-sec", and we want the
107 right port numbers even on the OSes that botch the entries in
108 /etc/services. So don't bother with the lookup, except maybe
109 to produce a warning. */
110 struct addrinfo hints = { 0 };
114 hints.ai_family = PF_INET;
115 hints.ai_socktype = stream ? SOCK_STREAM : SOCK_DGRAM;
116 err = getaddrinfo (NULL, service, &hints, &ai);
117 if (err == 0 && ai != 0) {
118 if (ai->ai_addr->sa_family == AF_INET) {
119 int port = ((struct sockaddr_in *)ai->ai_addr)->sin_port;
126 /* Any error - don't complain, just use default. */
127 return htons (defalt);
131 krb5int_grow_addrlist (struct addrlist *lp, int nmore)
134 int newspace = lp->space + nmore;
135 size_t newsize = newspace * sizeof (struct addrlist);
136 struct addrinfo **newaddrs;
138 /* NULL check a concession to SunOS4 compatibility for now; not
139 required for pure ANSI support. */
141 newaddrs = realloc (lp->addrs, newsize);
143 newaddrs = malloc (newsize);
145 if (newaddrs == NULL)
147 for (i = lp->space; i < newspace; i++)
149 lp->addrs = newaddrs;
150 lp->space = newspace;
153 #define grow_list krb5int_grow_addrlist
155 /* Free up everything pointed to by the addrlist structure, but don't
156 free the structure itself. */
158 krb5int_free_addrlist (struct addrlist *lp)
161 for (i = 0; i < lp->naddrs; i++)
162 freeaddrinfo (lp->addrs[i]);
165 lp->naddrs = lp->space = 0;
167 #define free_list krb5int_free_addrlist
169 static int translate_ai_error (int err)
178 /* All of these indicate bad inputs to getaddrinfo. */
181 /* Translate to standard errno code. */
184 /* Translate to standard errno code. */
186 #ifdef EAI_ADDRFAMILY
189 #if EAI_NODATA != EAI_NONAME
193 /* Name not known or no address data, but no error. Do
198 /* System error, obviously. */
202 /* An error code we haven't handled? */
207 static int add_addrinfo_to_list (struct addrlist *lp, struct addrinfo *a)
212 switch (a->ai_socktype) {
214 fprintf(stderr, "\tdgram\n");
217 fprintf(stderr, "\tstream\n");
220 fprintf(stderr, "\traw\n");
225 fprintf(stderr, "\tsocket type %d\n", a->ai_socktype);
230 if (lp->naddrs == lp->space) {
231 err = grow_list (lp, 1);
234 fprintf (stderr, "grow_list failed %d\n", err);
239 lp->addrs[lp->naddrs++] = a;
242 fprintf (stderr, "count is now %d\n", lp->naddrs);
247 #define add_host_to_list krb5int_add_host_to_list
250 krb5int_add_host_to_list (struct addrlist *lp, const char *hostname,
251 int port, int secport,
252 int socktype, int family)
254 struct addrinfo *addrs, *a, *anext, hint;
256 char portbuf[10], secportbuf[10];
259 fprintf (stderr, "adding hostname %s, ports %d,%d\n", hostname,
260 ntohs (port), ntohs (secport));
263 memset(&hint, 0, sizeof(hint));
264 hint.ai_family = family;
265 hint.ai_socktype = socktype;
266 sprintf(portbuf, "%d", ntohs(port));
267 sprintf(secportbuf, "%d", ntohs(secport));
268 err = getaddrinfo (hostname, portbuf, &hint, &addrs);
270 return translate_ai_error (err);
272 for (a = addrs; a != 0 && err == 0; a = anext) {
274 err = add_addrinfo_to_list (lp, a);
276 if (err || secport == 0)
279 socktype = SOCK_DGRAM;
280 else if (socktype != SOCK_DGRAM)
282 hint.ai_family = AF_INET;
283 err = getaddrinfo (hostname, secportbuf, &hint, &addrs);
285 err = translate_ai_error (err);
288 for (a = addrs; a != 0 && err == 0; a = anext) {
290 err = add_addrinfo_to_list (lp, a);
294 freeaddrinfo (anext);
299 * returns count of number of addresses found
300 * if master is non-NULL, it is filled in with the index of
304 static krb5_error_code
305 krb5_locate_srv_conf_1(krb5_context context, const krb5_data *realm,
306 const char * name, struct addrlist *addrlist,
307 int get_masters, int socktype,
308 int udpport, int sec_udpport, int family)
310 const char *realm_srv_names[4];
311 char **masterlist, **hostlist, *host, *port, *cp;
312 krb5_error_code code;
313 int i, j, count, ismaster;
317 "looking in krb5.conf for realm %s entry %s; ports %d,%d\n",
318 realm->data, name, ntohs (udpport), ntohs (sec_udpport));
321 if ((host = malloc(realm->length + 1)) == NULL)
324 strncpy(host, realm->data, realm->length);
325 host[realm->length] = '\0';
330 realm_srv_names[0] = "realms";
331 realm_srv_names[1] = host;
332 realm_srv_names[2] = name;
333 realm_srv_names[3] = 0;
335 code = profile_get_values(context->profile, realm_srv_names, &hostlist);
339 fprintf (stderr, "config file lookup failed: %s\n",
340 error_message(code));
342 if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
343 code = KRB5_REALM_UNKNOWN;
349 while (hostlist && hostlist[count])
352 fprintf (stderr, "found %d entries under 'kdc'\n", count);
356 profile_free_list(hostlist);
358 addrlist->naddrs = 0;
363 realm_srv_names[0] = "realms";
364 realm_srv_names[1] = host;
365 realm_srv_names[2] = "admin_server";
366 realm_srv_names[3] = 0;
368 code = profile_get_values(context->profile, realm_srv_names,
374 for (i=0; masterlist[i]; i++) {
375 host = masterlist[i];
378 * Strip off excess whitespace
380 cp = strchr(host, ' ');
383 cp = strchr(host, '\t');
386 cp = strchr(host, ':');
395 /* at this point, if master is non-NULL, then either the master kdc
396 is required, and there is one, or the master kdc is not required,
397 and there may or may not be one. */
399 #ifdef HAVE_NETINET_IN_H
404 for (i=0; hostlist[i]; i++) {
409 fprintf (stderr, "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);
470 fprintf (stderr, "error %d returned from add_host_to_list\n", code);
473 profile_free_list (hostlist);
475 profile_free_list (masterlist);
481 profile_free_list(hostlist);
483 profile_free_list(masterlist);
489 static krb5_error_code
490 krb5_locate_srv_conf(krb5_context context, const krb5_data *realm,
491 const char *name, struct addrlist *al, int get_masters,
492 int udpport, int sec_udpport)
496 ret = krb5_locate_srv_conf_1 (context, realm, name, al,
497 get_masters, 0, udpport, sec_udpport, 0);
500 if (al->naddrs == 0) /* Couldn't resolve any KDC names */
501 return KRB5_REALM_CANT_RESOLVE;
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;
538 fprintf (stderr, "walking answer list:\n");
540 for (entry = head; entry != NULL; entry = next) {
542 fprintf (stderr, "\tport=%d host=%s\n", entry->port, entry->host);
545 code = add_host_to_list (addrlist, entry->host, htons (entry->port), 0,
546 (strcmp("_tcp", protocol)
548 : SOCK_STREAM), family);
559 fprintf (stderr, "[end]\n");
562 krb5int_free_srv_dns_data(head);
567 * Wrapper function for the two backends
571 krb5int_locate_server (krb5_context context, const krb5_data *realm,
572 struct addrlist *addrlist,
574 const char *profname, const char *dnsname,
576 /* network order port numbers! */
577 int dflport1, int dflport2,
580 krb5_error_code code;
581 struct addrlist al = ADDRLIST_INIT;
586 * We always try the local file first
589 code = krb5_locate_srv_conf_1(context, realm, profname, &al, get_masters,
590 socktype, dflport1, dflport2, family);
592 #ifdef KRB5_DNS_LOOKUP
593 if (code && dnsname != 0) {
594 int use_dns = _krb5_use_dns_kdc(context);
597 if (socktype == SOCK_DGRAM || socktype == 0) {
598 code = krb5_locate_srv_dns_1(realm, dnsname, "_udp",
602 fprintf(stderr, "dns udp lookup returned error %d\n",
606 if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) {
607 code = krb5_locate_srv_dns_1(realm, dnsname, "_tcp",
611 fprintf(stderr, "dns tcp lookup returned error %d\n",
617 #endif /* KRB5_DNS_LOOKUP */
620 fprintf (stderr, "krb5int_locate_server found %d addresses\n",
623 fprintf (stderr, "krb5int_locate_server returning error code %d\n",
631 if (al.naddrs == 0) { /* No good servers */
634 return KRB5_REALM_CANT_RESOLVE;
641 krb5_locate_kdc(krb5_context context, const krb5_data *realm,
642 struct addrlist *addrlist,
643 int get_masters, int socktype, int family)
645 int udpport, sec_udpport;
647 udpport = get_port (KDC_PORTNAME, 0, KRB5_DEFAULT_PORT);
648 if (socktype == SOCK_STREAM)
651 sec_udpport = get_port (KDC_SECONDARY_PORTNAME, 0,
652 (udpport == htons (KRB5_DEFAULT_PORT)
653 ? KRB5_DEFAULT_SEC_PORT
654 : KRB5_DEFAULT_PORT));
655 if (sec_udpport == udpport)
659 return krb5int_locate_server(context, realm, addrlist, 0,
660 get_masters ? "master_kdc" : "kdc",
664 socktype, udpport, sec_udpport, family);