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