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