1 /*
2  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
3  */
4 
5 /*
6  * lib/krb5/os/locate_kdc.c
7  *
8  * Copyright 1990,2000,2001,2002,2003,2004,2006 Massachusetts Institute of Technology.
9  * All Rights Reserved.
10  *
11  * Export of this software from the United States of America may
12  *   require a specific license from the United States Government.
13  *   It is the responsibility of any person or organization contemplating
14  *   export to obtain such a license before exporting.
15  *
16  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
17  * distribute this software and its documentation for any purpose and
18  * without fee is hereby granted, provided that the above copyright
19  * notice appear in all copies and that both that copyright notice and
20  * this permission notice appear in supporting documentation, and that
21  * the name of M.I.T. not be used in advertising or publicity pertaining
22  * to distribution of the software without specific, written prior
23  * permission.  Furthermore if you modify this software you must label
24  * your software as modified software and not distribute it in such a
25  * fashion that it might be confused with the original M.I.T. software.
26  * M.I.T. makes no representations about the suitability of
27  * this software for any purpose.  It is provided "as is" without express
28  * or implied warranty.
29  *
30  *
31  * get socket addresses for KDC.
32  */
33 
34 #include "fake-addrinfo.h"
35 #include "k5-int.h"
36 #include "os-proto.h"
37 #include <stdio.h>
38 #ifdef KRB5_DNS_LOOKUP
39 #ifdef WSHELPER
40 #include <wshelper.h>
41 #else /* WSHELPER */
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
44 #include <arpa/nameser.h>
45 #include <resolv.h>
46 #include <netdb.h>
47 #endif /* WSHELPER */
48 #ifndef T_SRV
49 #define T_SRV 33
50 #endif /* T_SRV */
51 
52 /* for old Unixes and friends ... */
53 #ifndef MAXHOSTNAMELEN
54 #define MAXHOSTNAMELEN 64
55 #endif
56 
57 #define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1)
58 
59 /* Solaris Kerberos: default to dns lookup for the KDC but not the realm */
60 #define DEFAULT_LOOKUP_KDC 1
61 #define DEFAULT_LOOKUP_REALM 0
62 
63 static int
64 maybe_use_dns (krb5_context context, const char *name, int defalt)
65 {
66     krb5_error_code code;
67     char * value = NULL;
68     int use_dns = 0;
69 
70     code = profile_get_string(context->profile, "libdefaults",
71                               name, 0, 0, &value);
72     if (value == 0 && code == 0)
73 	code = profile_get_string(context->profile, "libdefaults",
74 				  "dns_fallback", 0, 0, &value);
75     if (code)
76         return defalt;
77 
78     if (value == 0)
79 	return defalt;
80 
81     use_dns = _krb5_conf_boolean(value);
82     profile_release_string(value);
83     return use_dns;
84 }
85 
86 int
87 _krb5_use_dns_kdc(krb5_context context)
88 {
89     return maybe_use_dns (context, "dns_lookup_kdc", DEFAULT_LOOKUP_KDC);
90 }
91 
92 int
93 _krb5_use_dns_realm(krb5_context context)
94 {
95     return maybe_use_dns (context, "dns_lookup_realm", DEFAULT_LOOKUP_REALM);
96 }
97 
98 #endif /* KRB5_DNS_LOOKUP */
99 
100 int
101 krb5int_grow_addrlist (struct addrlist *lp, int nmore)
102 {
103     int i;
104     int newspace = lp->space + nmore;
105     size_t newsize = newspace * sizeof (*lp->addrs);
106     void *newaddrs;
107 
108     newaddrs = realloc (lp->addrs, newsize);
109     if (newaddrs == NULL)
110 	return errno;
111     lp->addrs = newaddrs;
112     for (i = lp->space; i < newspace; i++) {
113 	lp->addrs[i].ai = NULL;
114 	lp->addrs[i].freefn = NULL;
115 	lp->addrs[i].data = NULL;
116     }
117     lp->space = newspace;
118     return 0;
119 }
120 #define grow_list krb5int_grow_addrlist
121 
122 /* Free up everything pointed to by the addrlist structure, but don't
123    free the structure itself.  */
124 void
125 krb5int_free_addrlist (struct addrlist *lp)
126 {
127     int i;
128     for (i = 0; i < lp->naddrs; i++)
129 	if (lp->addrs[i].freefn)
130 	    (lp->addrs[i].freefn)(lp->addrs[i].data);
131     free (lp->addrs);
132     lp->addrs = NULL;
133     lp->naddrs = lp->space = 0;
134 }
135 #define free_list krb5int_free_addrlist
136 
137 static int translate_ai_error (int err)
138 {
139     switch (err) {
140     case 0:
141 	return 0;
142     case EAI_BADFLAGS:
143     case EAI_FAMILY:
144     case EAI_SOCKTYPE:
145     case EAI_SERVICE:
146 	/* All of these indicate bad inputs to getaddrinfo.  */
147 	return EINVAL;
148     case EAI_AGAIN:
149 	/* Translate to standard errno code.  */
150 	return EAGAIN;
151     case EAI_MEMORY:
152 	/* Translate to standard errno code.  */
153 	return ENOMEM;
154 #ifdef EAI_ADDRFAMILY
155     case EAI_ADDRFAMILY:
156 #endif
157 #if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
158     case EAI_NODATA:
159 #endif
160     case EAI_NONAME:
161 	/* Name not known or no address data, but no error.  Do
162 	   nothing more.  */
163 	return 0;
164 #ifdef EAI_OVERFLOW
165     case EAI_OVERFLOW:
166 	/* An argument buffer overflowed.  */
167 	return EINVAL;		/* XXX */
168 #endif
169 #ifdef EAI_SYSTEM
170     case EAI_SYSTEM:
171 	/* System error, obviously.  */
172 	return errno;
173 #endif
174     default:
175 	/* An error code we haven't handled?  */
176 	return EINVAL;
177     }
178 }
179 
180 /* Solaris Kerberos: want dbg messages to syslog */
181 #include <stdarg.h>
182 static inline void Tprintf(const char *fmt, ...)
183 {
184 #ifdef TEST
185     va_list ap;
186     char err_str[2048];
187 
188     va_start(ap, fmt);
189     vsnprintf(err_str, sizeof (err_str), fmt, args);
190     syslog(LOG_DEBUG, err_str);
191     va_end(ap);
192 #endif
193 }
194 
195 #if 0
196 extern void krb5int_debug_fprint(const char *, ...);
197 #define dprint krb5int_debug_fprint
198 #define print_addrlist krb5int_print_addrlist
199 extern void print_addrlist (const struct addrlist *a);
200 #else
201 static inline void dprint(const char *fmt, ...) { }
202 static inline void print_addrlist(const struct addrlist *a) { }
203 #endif
204 
205 static int add_addrinfo_to_list (struct addrlist *lp, struct addrinfo *a,
206 				 void (*freefn)(void *), void *data)
207 {
208     int err;
209 
210     dprint("\tadding %p=%A to %p (naddrs=%d space=%d)\n", a, a, lp,
211 	   lp->naddrs, lp->space);
212 
213     if (lp->naddrs == lp->space) {
214 	err = grow_list (lp, 1);
215 	if (err) {
216 	    Tprintf ("grow_list failed %d\n", err);
217 	    return err;
218 	}
219     }
220     Tprintf("setting element %d\n", lp->naddrs);
221     lp->addrs[lp->naddrs].ai = a;
222     lp->addrs[lp->naddrs].freefn = freefn;
223     lp->addrs[lp->naddrs].data = data;
224     lp->naddrs++;
225     Tprintf ("\tcount is now %d: ", lp->naddrs);
226     print_addrlist(lp);
227     Tprintf("\n");
228     return 0;
229 }
230 
231 #define add_host_to_list krb5int_add_host_to_list
232 
233 static void call_freeaddrinfo(void *data)
234 {
235     /* Strict interpretation of the C standard says we can't assume
236        that the ABI for f(void*) and f(struct foo *) will be
237        compatible.  Use this stub just to be paranoid.  */
238     freeaddrinfo(data);
239 }
240 
241 int
242 krb5int_add_host_to_list (struct addrlist *lp, const char *hostname,
243 			  int port, int secport,
244 			  int socktype, int family)
245 {
246     struct addrinfo *addrs, *a, *anext, hint;
247     int err;
248     char portbuf[10], secportbuf[10];
249     void (*freefn)(void *);
250 
251     Tprintf ("adding hostname %s, ports %d,%d, family %d, socktype %d\n",
252 	     hostname, ntohs (port), ntohs (secport),
253 	     family, socktype);
254 
255     memset(&hint, 0, sizeof(hint));
256     hint.ai_family = family;
257     hint.ai_socktype = socktype;
258 #ifdef AI_NUMERICSERV
259     hint.ai_flags = AI_NUMERICSERV;
260 #endif
261     sprintf(portbuf, "%d", ntohs(port));
262     sprintf(secportbuf, "%d", ntohs(secport));
263     err = getaddrinfo (hostname, portbuf, &hint, &addrs);
264     if (err) {
265 	Tprintf ("\tgetaddrinfo(\"%s\", \"%s\", ...)\n\treturns %d: %s\n",
266 		 hostname, portbuf, err, gai_strerror (err));
267 	return translate_ai_error (err);
268     }
269     freefn = call_freeaddrinfo;
270     anext = 0;
271     for (a = addrs; a != 0 && err == 0; a = anext, freefn = 0) {
272 	anext = a->ai_next;
273 	err = add_addrinfo_to_list (lp, a, freefn, a);
274     }
275     if (err || secport == 0)
276 	goto egress;
277     if (socktype == 0)
278 	socktype = SOCK_DGRAM;
279     else if (socktype != SOCK_DGRAM)
280 	goto egress;
281     hint.ai_family = AF_INET;
282     err = getaddrinfo (hostname, secportbuf, &hint, &addrs);
283     if (err) {
284 	err = translate_ai_error (err);
285 	goto egress;
286     }
287     freefn = call_freeaddrinfo;
288     for (a = addrs; a != 0 && err == 0; a = anext, freefn = 0) {
289 	anext = a->ai_next;
290 	err = add_addrinfo_to_list (lp, a, freefn, a);
291     }
292 egress:
293     /* Solaris Kerberos */
294     if (anext)
295 	freeaddrinfo (anext);
296     return err;
297 }
298 
299 /*
300  * returns count of number of addresses found
301  * if master is non-NULL, it is filled in with the index of
302  * the master kdc
303  */
304 
305 static krb5_error_code
306 krb5_locate_srv_conf_1(krb5_context context, const krb5_data *realm,
307 		       const char * name, struct addrlist *addrlist,
308 		       int get_masters, int socktype,
309 		       int udpport, int sec_udpport, int family)
310 {
311     const char	*realm_srv_names[4];
312     char **masterlist, **hostlist, *host, *port, *cp;
313     krb5_error_code code;
314     int i, j, count, ismaster;
315 
316     Tprintf ("looking in krb5.conf for realm %s entry %s; ports %d,%d\n",
317 	     realm->data, name, ntohs (udpport), ntohs (sec_udpport));
318 
319     if ((host = malloc(realm->length + 1)) == NULL)
320 	return ENOMEM;
321 
322     strncpy(host, realm->data, realm->length);
323     host[realm->length] = '\0';
324     hostlist = 0;
325 
326     masterlist = NULL;
327 
328     realm_srv_names[0] = "realms";
329     realm_srv_names[1] = host;
330     realm_srv_names[2] = name;
331     realm_srv_names[3] = 0;
332 
333     code = profile_get_values(context->profile, realm_srv_names, &hostlist);
334 
335     if (code) {
336 	Tprintf ("config file lookup failed: %s\n",
337 		 error_message(code));
338         if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
339 	    code = KRB5_REALM_UNKNOWN;
340  	krb5_xfree(host);
341   	return code;
342      }
343 
344     count = 0;
345     while (hostlist && hostlist[count])
346 	    count++;
347     Tprintf ("found %d entries under 'kdc'\n", count);
348 
349     if (count == 0) {
350         profile_free_list(hostlist);
351 	krb5_xfree(host);
352 	addrlist->naddrs = 0;
353 	return 0;
354     }
355 
356     if (get_masters) {
357 	realm_srv_names[0] = "realms";
358 	realm_srv_names[1] = host;
359 	realm_srv_names[2] = "admin_server";
360 	realm_srv_names[3] = 0;
361 
362 	code = profile_get_values(context->profile, realm_srv_names,
363 				  &masterlist);
364 
365 	krb5_xfree(host);
366 
367 	if (code == 0) {
368 	    for (i=0; masterlist[i]; i++) {
369 		host = masterlist[i];
370 
371 		/*
372 		 * Strip off excess whitespace
373 		 */
374 		cp = strchr(host, ' ');
375 		if (cp)
376 		    *cp = 0;
377 		cp = strchr(host, '\t');
378 		if (cp)
379 		    *cp = 0;
380 		cp = strchr(host, ':');
381 		if (cp)
382 		    *cp = 0;
383 	    }
384 	}
385     } else {
386 	krb5_xfree(host);
387     }
388 
389     /* at this point, if master is non-NULL, then either the master kdc
390        is required, and there is one, or the master kdc is not required,
391        and there may or may not be one. */
392 
393 #ifdef HAVE_NETINET_IN_H
394     if (sec_udpport)
395 	    count = count * 2;
396 #endif
397 
398     for (i=0; hostlist[i]; i++) {
399 	int p1, p2;
400 
401 	host = hostlist[i];
402 	Tprintf ("entry %d is '%s'\n", i, host);
403 	/*
404 	 * Strip off excess whitespace
405 	 */
406 	cp = strchr(host, ' ');
407 	if (cp)
408 	    *cp = 0;
409 	cp = strchr(host, '\t');
410 	if (cp)
411 	    *cp = 0;
412 	port = strchr(host, ':');
413 	if (port) {
414 	    *port = 0;
415 	    port++;
416 	}
417 
418 	ismaster = 0;
419 	if (masterlist) {
420 	    for (j=0; masterlist[j]; j++) {
421 		if (strcasecmp(hostlist[i], masterlist[j]) == 0) {
422 		    ismaster = 1;
423 		}
424 	    }
425 	}
426 
427 	if (get_masters && !ismaster)
428 	    continue;
429 
430 	if (port) {
431 	    unsigned long l;
432 #ifdef HAVE_STROUL
433 	    char *endptr;
434 	    l = strtoul (port, &endptr, 10);
435 	    if (endptr == NULL || *endptr != 0)
436 		return EINVAL;
437 #else
438 	    l = atoi (port);
439 #endif
440 	    /* L is unsigned, don't need to check <0.  */
441 	    if (l > 65535)
442 		return EINVAL;
443 	    p1 = htons (l);
444 	    p2 = 0;
445 	} else {
446 	    p1 = udpport;
447 	    p2 = sec_udpport;
448 	}
449 
450 	if (socktype != 0)
451 	    code = add_host_to_list (addrlist, hostlist[i], p1, p2,
452 				     socktype, family);
453 	else {
454 	    code = add_host_to_list (addrlist, hostlist[i], p1, p2,
455 				     SOCK_DGRAM, family);
456 	    if (code == 0)
457 		code = add_host_to_list (addrlist, hostlist[i], p1, p2,
458 					 SOCK_STREAM, family);
459 	}
460 	if (code) {
461 	    Tprintf ("error %d (%s) returned from add_host_to_list\n", code,
462 		     error_message (code));
463 	    if (hostlist)
464 		profile_free_list (hostlist);
465 	    if (masterlist)
466 		profile_free_list (masterlist);
467 	    return code;
468 	}
469     }
470 
471     if (hostlist)
472         profile_free_list(hostlist);
473     if (masterlist)
474         profile_free_list(masterlist);
475 
476     return 0;
477 }
478 
479 #ifdef TEST
480 static krb5_error_code
481 krb5_locate_srv_conf(krb5_context context, const krb5_data *realm,
482 		     const char *name, struct addrlist *al, int get_masters,
483 		     int udpport, int sec_udpport)
484 {
485     krb5_error_code ret;
486 
487     ret = krb5_locate_srv_conf_1 (context, realm, name, al,
488 				  get_masters, 0, udpport, sec_udpport, 0);
489     if (ret)
490 	return ret;
491     if (al->naddrs == 0)	/* Couldn't resolve any KDC names */
492 	return KRB5_REALM_CANT_RESOLVE;
493     return 0;
494 }
495 #endif
496 
497 #ifdef KRB5_DNS_LOOKUP
498 static krb5_error_code
499 krb5_locate_srv_dns_1 (const krb5_data *realm,
500 		       const char *service,
501 		       const char *protocol,
502 		       struct addrlist *addrlist,
503 		       int family)
504 {
505     struct srv_dns_entry *head = NULL;
506     struct srv_dns_entry *entry = NULL, *next;
507     krb5_error_code code = 0;
508 
509     code = krb5int_make_srv_query_realm(realm, service, protocol, &head);
510     if (code)
511 	return 0;
512 
513     /*
514      * Okay!  Now we've got a linked list of entries sorted by
515      * priority.  Start looking up A records and returning
516      * addresses.
517      */
518 
519     if (head == NULL)
520 	return 0;
521 
522     /* Check for the "." case indicating no support.  */
523     if (head->next == 0 && head->host[0] == 0) {
524 	free(head->host);
525 	free(head);
526 	return KRB5_ERR_NO_SERVICE;
527     }
528 
529     Tprintf ("walking answer list:\n");
530     for (entry = head; entry != NULL; entry = next) {
531 	Tprintf ("\tport=%d host=%s\n", entry->port, entry->host);
532 	next = entry->next;
533 	code = add_host_to_list (addrlist, entry->host, htons (entry->port), 0,
534 				 (strcmp("_tcp", protocol)
535 				  ? SOCK_DGRAM
536 				  : SOCK_STREAM), family);
537 	if (code) {
538 	    break;
539 	}
540 	if (entry == head) {
541 	    free(entry->host);
542 	    free(entry);
543 	    head = next;
544 	    entry = 0;
545 	}
546     }
547     Tprintf ("[end]\n");
548 
549     krb5int_free_srv_dns_data(head);
550     return code;
551 }
552 #endif
553 
554 #include <locate_plugin.h>
555 
556 #if TARGET_OS_MAC
557 static const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/libkrb5", NULL }; /* should be a list */
558 #else
559 static const char *objdirs[] = { LIBDIR "/krb5/plugins/libkrb5", NULL };
560 #endif
561 
562 struct module_callback_data {
563     int out_of_mem;
564     struct addrlist *lp;
565 };
566 
567 static int
568 module_callback (void *cbdata, int socktype, struct sockaddr *sa)
569 {
570     struct module_callback_data *d = cbdata;
571     struct {
572 	struct addrinfo ai;
573 	union {
574 	    struct sockaddr_in sin;
575 	    struct sockaddr_in6 sin6;
576 	} u;
577     } *x;
578 
579     if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
580 	return 0;
581     if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
582 	return 0;
583     x = malloc (sizeof (*x));
584     if (x == 0) {
585 	d->out_of_mem = 1;
586 	return 1;
587     }
588     memset(x, 0, sizeof (*x));
589     x->ai.ai_addr = (struct sockaddr *) &x->u;
590     x->ai.ai_socktype = socktype;
591     x->ai.ai_family = sa->sa_family;
592     if (sa->sa_family == AF_INET) {
593 	x->u.sin = *(struct sockaddr_in *)sa;
594 	x->ai.ai_addrlen = sizeof(struct sockaddr_in);
595     }
596     if (sa->sa_family == AF_INET6) {
597 	x->u.sin6 = *(struct sockaddr_in6 *)sa;
598 	x->ai.ai_addrlen = sizeof(struct sockaddr_in6);
599     }
600     if (add_addrinfo_to_list (d->lp, &x->ai, free, x) != 0) {
601 	/* Assumes only error is ENOMEM.  */
602 	d->out_of_mem = 1;
603 	return 1;
604     }
605     return 0;
606 }
607 
608 static krb5_error_code
609 module_locate_server (krb5_context ctx, const krb5_data *realm,
610 		      struct addrlist *addrlist,
611 		      enum locate_service_type svc, int socktype, int family)
612 {
613     struct krb5plugin_service_locate_result *res = NULL;
614     krb5_error_code code;
615     struct krb5plugin_service_locate_ftable *vtbl = NULL;
616     void **ptrs;
617     int i;
618     struct module_callback_data cbdata = { 0, };
619 
620     Tprintf("in module_locate_server\n");
621     cbdata.lp = addrlist;
622     if (!PLUGIN_DIR_OPEN (&ctx->libkrb5_plugins)) {
623 
624 	code = krb5int_open_plugin_dirs (objdirs, NULL, &ctx->libkrb5_plugins,
625 					 &ctx->err);
626 	if (code)
627 	    return KRB5_PLUGIN_NO_HANDLE;
628     }
629 
630     code = krb5int_get_plugin_dir_data (&ctx->libkrb5_plugins,
631 					"service_locator", &ptrs, &ctx->err);
632     if (code) {
633 	Tprintf("error looking up plugin symbols: %s\n",
634 		krb5_get_error_message(ctx, code));
635 	return KRB5_PLUGIN_NO_HANDLE;
636     }
637 
638     for (i = 0; ptrs[i]; i++) {
639 	void *blob;
640 
641 	vtbl = ptrs[i];
642 	Tprintf("element %d is %p\n", i, ptrs[i]);
643 
644 	/* For now, don't keep the plugin data alive.  For long-lived
645 	   contexts, it may be desirable to change that later.  */
646 	code = vtbl->init(ctx, &blob);
647 	if (code)
648 	    continue;
649 
650 	code = vtbl->lookup(blob, svc, realm->data, socktype, family,
651 			    module_callback, &cbdata);
652 	vtbl->fini(blob);
653 	if (code == KRB5_PLUGIN_NO_HANDLE) {
654 	    /* Module passes, keep going.  */
655 	    /* XXX */
656 	    Tprintf("plugin doesn't handle this realm (KRB5_PLUGIN_NO_HANDLE)\n");
657 	    continue;
658 	}
659 	if (code != 0) {
660 	    /* Module encountered an actual error.  */
661 	    Tprintf("plugin lookup routine returned error %d: %s\n",
662 		    code, error_message(code));
663 	    krb5int_free_plugin_dir_data (ptrs);
664 	    return code;
665 	}
666 	break;
667     }
668     if (ptrs[i] == NULL) {
669 	Tprintf("ran off end of plugin list\n");
670 	krb5int_free_plugin_dir_data (ptrs);
671 	return KRB5_PLUGIN_NO_HANDLE;
672     }
673     Tprintf("stopped with plugin #%d, res=%p\n", i, res);
674 
675     /* Got something back, yippee.  */
676     Tprintf("now have %d addrs in list %p\n", addrlist->naddrs, addrlist);
677     print_addrlist(addrlist);
678     krb5int_free_plugin_dir_data (ptrs);
679     return 0;
680 }
681 
682 static krb5_error_code
683 prof_locate_server (krb5_context context, const krb5_data *realm,
684 		    struct addrlist *addrlist,
685 		    enum locate_service_type svc, int socktype, int family)
686 {
687     const char *profname;
688     int dflport1, dflport2 = 0;
689     struct servent *serv;
690 
691     switch (svc) {
692     case locate_service_kdc:
693 	profname = "kdc";
694 	/* We used to use /etc/services for these, but enough systems
695 	   have old, crufty, wrong settings that this is probably
696 	   better.  */
697     kdc_ports:
698 	dflport1 = htons(KRB5_DEFAULT_PORT);
699 	dflport2 = htons(KRB5_DEFAULT_SEC_PORT);
700 	break;
701     case locate_service_master_kdc:
702 	profname = "master_kdc";
703 	goto kdc_ports;
704     case locate_service_kadmin:
705 	profname = "admin_server";
706 	dflport1 = htons(DEFAULT_KADM5_PORT);
707 	break;
708     case locate_service_krb524:
709 	profname = "krb524_server";
710 	serv = getservbyname(KRB524_SERVICE, "udp");
711 	dflport1 = serv ? serv->s_port : htons (KRB524_PORT);
712 	break;
713     case locate_service_kpasswd:
714 	profname = "kpasswd_server";
715 	dflport1 = htons(DEFAULT_KPASSWD_PORT);
716 	break;
717     default:
718 	return EBUSY;		/* XXX */
719     }
720 
721     return krb5_locate_srv_conf_1 (context, realm, profname, addrlist,
722 				   0, socktype,
723 				   dflport1, dflport2, family);
724 }
725 
726 static krb5_error_code
727 dns_locate_server (krb5_context context, const krb5_data *realm,
728 		   struct addrlist *addrlist,
729 		   enum locate_service_type svc, int socktype, int family)
730 {
731     const char *dnsname;
732     int use_dns = _krb5_use_dns_kdc(context);
733     krb5_error_code code;
734 
735     if (!use_dns)
736 	return KRB5_PLUGIN_NO_HANDLE;
737 
738     switch (svc) {
739     case locate_service_kdc:
740 	dnsname = "_kerberos";
741 	break;
742     case locate_service_master_kdc:
743 	dnsname = "_kerberos-master";
744 	break;
745     case locate_service_kadmin:
746 	dnsname = "_kerberos-adm";
747 	break;
748     case locate_service_krb524:
749 	dnsname = "_krb524";
750 	break;
751     case locate_service_kpasswd:
752 	dnsname = "_kpasswd";
753 	break;
754     default:
755 	return KRB5_PLUGIN_NO_HANDLE;
756     }
757 
758     code = 0;
759     if (socktype == SOCK_DGRAM || socktype == 0) {
760 	code = krb5_locate_srv_dns_1(realm, dnsname, "_udp", addrlist, family);
761 	if (code)
762 	    Tprintf("dns udp lookup returned error %d\n", code);
763     }
764     if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) {
765 	code = krb5_locate_srv_dns_1(realm, dnsname, "_tcp", addrlist, family);
766 	if (code)
767 	    Tprintf("dns tcp lookup returned error %d\n", code);
768     }
769     return code;
770 }
771 
772 /*
773  * Wrapper function for the various backends
774  */
775 
776 krb5_error_code
777 krb5int_locate_server (krb5_context context, const krb5_data *realm,
778 		       struct addrlist *addrlist,
779 		       enum locate_service_type svc,
780 		       int socktype, int family)
781 {
782     krb5_error_code code;
783     struct addrlist al = ADDRLIST_INIT;
784 
785     *addrlist = al;
786 
787     code = module_locate_server(context, realm, &al, svc, socktype, family);
788     Tprintf("module_locate_server returns %d\n", code);
789     if (code == KRB5_PLUGIN_NO_HANDLE) {
790 	/*
791 	 * We always try the local file before DNS.  Note that there
792 	 * is no way to indicate "service not available" via the
793 	 * config file.
794 	 */
795 
796 	code = prof_locate_server(context, realm, &al, svc, socktype, family);
797 
798 	/*
799 	 * Solaris Kerberos:
800 	 * If kpasswd_server has not been configured and dns_lookup_kdc -
801 	 * dns_fallback are not configured then admin_server should
802 	 * be inferenced, per krb5.conf(4).
803 	 */
804 	if (code && svc == locate_service_kpasswd &&
805 	    !maybe_use_dns(context, "dns_lookup_kdc", 0)) {
806 		code = krb5_locate_srv_conf_1(context, realm, "admin_server",
807 		    &al, 0, socktype, htons(DEFAULT_KPASSWD_PORT), 0, family);
808 	}
809 
810 #ifdef KRB5_DNS_LOOKUP
811 	/*
812 	 * Solaris Kerberos:
813 	 * There is no point in trying to locate the KDC in DNS if "realm"
814 	 * is empty.
815 	 */
816 	/* Try DNS for all profile errors?  */
817 	if (code && !krb5_is_referral_realm(realm)) {
818 	    krb5_error_code code2;
819 	    code2 = dns_locate_server(context, realm, &al, svc, socktype,
820 				      family);
821 
822 	    /*
823 	     * Solaris Kerberos:
824 	     * If an entry for _kerberos-master. does not exist (checked for
825 	     * above) but _kpasswd. does then treat that as an entry for the
826 	     * master KDC (but use port 88 not the kpasswd port). MS AD creates
827 	     * kpasswd entries by default in DNS.
828 	     */
829 	    if (code2 == 0 && svc == locate_service_master_kdc &&
830 		al.naddrs == 0) {
831 
832 		/* Look for _kpasswd._tcp|udp */
833 		code2 = dns_locate_server(context, realm, &al,
834 		    locate_service_kpasswd, socktype, family);
835 
836 		/* Set the port to 88 instead of the kpasswd port */
837 		if (code2 == 0 ) {
838 		    int i;
839 		    struct addrinfo *a;
840 
841 		    for (i = 0; i < al.naddrs; i++) {
842 			if (al.addrs[i].ai->ai_family == AF_INET)
843 			    for (a = al.addrs[i].ai; a != NULL; a = a->ai_next)
844 				((struct sockaddr_in *)a->ai_addr)->sin_port =
845 				    htons(KRB5_DEFAULT_PORT);
846 
847 			if (al.addrs[i].ai->ai_family == AF_INET6)
848 			    for (a = al.addrs[i].ai; a != NULL; a = a->ai_next)
849 				((struct sockaddr_in6 *)a->ai_addr)->sin6_port =
850 				    htons(KRB5_DEFAULT_PORT);
851 		    }
852 		}
853 	    }
854 
855 	    if (code2 != KRB5_PLUGIN_NO_HANDLE)
856 		code = code2;
857 	}
858 #endif /* KRB5_DNS_LOOKUP */
859 
860 	/* We could put more heuristics here, like looking up a hostname
861 	   of "kerberos."+REALM, etc.  */
862     }
863     if (code == 0)
864 	Tprintf ("krb5int_locate_server found %d addresses\n",
865 		 al.naddrs);
866     else
867 	Tprintf ("krb5int_locate_server returning error code %d/%s\n",
868 		 code, error_message(code));
869     if (code != 0) {
870 	if (al.space)
871 	    free_list (&al);
872 	return code;
873     }
874     if (al.naddrs == 0) {	/* No good servers */
875 	if (al.space)
876 	    free_list (&al);
877 	krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE,
878 			       "Cannot resolve network address for KDC in realm %.*s",
879 			       realm->length, realm->data);
880 
881 	return KRB5_REALM_CANT_RESOLVE;
882     }
883     *addrlist = al;
884     return 0;
885 }
886 
887 krb5_error_code
888 krb5_locate_kdc(krb5_context context, const krb5_data *realm,
889 		struct addrlist *addrlist,
890 		int get_masters, int socktype, int family)
891 {
892     return krb5int_locate_server(context, realm, addrlist,
893 				 (get_masters
894 				  ? locate_service_master_kdc
895 				  : locate_service_kdc),
896 				 socktype, family);
897 }
898 
899 /*
900  * Solaris Kerberos: for backward compat.  Avoid using this
901  * function!
902  */
903 krb5_error_code
904 krb5_get_servername(krb5_context context,
905     const krb5_data *realm,
906     const char *name, const char *proto,
907     char *srvhost,
908     unsigned short *port)
909 {
910     krb5_error_code code = KRB5_REALM_UNKNOWN;
911 
912 #ifdef KRB5_DNS_LOOKUP
913     {
914 	int use_dns = _krb5_use_dns_kdc(context);
915 
916 	if (use_dns) {
917 	    struct srv_dns_entry *head = NULL;
918 
919 	    code = krb5int_make_srv_query_realm(realm, name, proto, &head);
920 	    if (code)
921 		return (code);
922 
923 	    if (head == NULL)
924 		return KRB5_REALM_CANT_RESOLVE;
925 
926 	    *port = head->port;
927 	    (void) strlcpy(srvhost, head->host, MAX_DNS_NAMELEN);
928 
929 #ifdef DEBUG
930 	    fprintf (stderr, "krb5_get_servername svrhost %s, port %d\n",
931 		srvhost, *port);
932 #endif
933 	    krb5int_free_srv_dns_data(head);
934 	}
935     }
936 #endif /* KRB5_DNS_LOOKUP */
937 
938     return (code);
939 }
940