Quote realm name in error message.
[krb5.git] / src / lib / krb5 / os / locate_kdc.c
1 /*
2  * lib/krb5/os/locate_kdc.c
3  *
4  * Copyright 1990,2000,2001,2002,2003,2004,2006,2008 Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
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.
11  * 
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.
25  * 
26  *
27  * get socket addresses for KDC.
28  */
29
30 #include "fake-addrinfo.h"
31 #include "k5-int.h"
32 #include "os-proto.h"
33 #include <stdio.h>
34 #ifdef KRB5_DNS_LOOKUP
35 #ifdef WSHELPER
36 #include <wshelper.h>
37 #else /* WSHELPER */
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include <arpa/nameser.h>
41 #include <resolv.h>
42 #include <netdb.h>
43 #endif /* WSHELPER */
44 #ifndef T_SRV
45 #define T_SRV 33
46 #endif /* T_SRV */
47
48 /* for old Unixes and friends ... */
49 #ifndef MAXHOSTNAMELEN
50 #define MAXHOSTNAMELEN 64
51 #endif
52
53 #define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1)
54
55 #if KRB5_DNS_LOOKUP_KDC
56 #define DEFAULT_LOOKUP_KDC 1
57 #else
58 #define DEFAULT_LOOKUP_KDC 0
59 #endif
60 #if KRB5_DNS_LOOKUP_REALM
61 #define DEFAULT_LOOKUP_REALM 1
62 #else
63 #define DEFAULT_LOOKUP_REALM 0
64 #endif
65
66 static int
67 maybe_use_dns (krb5_context context, const char *name, int defalt)
68 {
69     krb5_error_code code;
70     char * value = NULL;
71     int use_dns = 0;
72
73     code = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
74                               name, 0, 0, &value);
75     if (value == 0 && code == 0)
76         code = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
77                                   KRB5_CONF_DNS_FALLBACK, 0, 0, &value);
78     if (code)
79         return defalt;
80
81     if (value == 0)
82         return defalt;
83
84     use_dns = _krb5_conf_boolean(value);
85     profile_release_string(value);
86     return use_dns;
87 }
88
89 int
90 _krb5_use_dns_kdc(krb5_context context)
91 {
92     return maybe_use_dns (context, KRB5_CONF_DNS_LOOKUP_KDC, DEFAULT_LOOKUP_KDC);
93 }
94
95 int
96 _krb5_use_dns_realm(krb5_context context)
97 {
98     return maybe_use_dns (context, KRB5_CONF_DNS_LOOKUP_REALM, DEFAULT_LOOKUP_REALM);
99 }
100
101 #endif /* KRB5_DNS_LOOKUP */
102
103 int
104 krb5int_grow_addrlist (struct addrlist *lp, int nmore)
105 {
106     int i;
107     int newspace = lp->space + nmore;
108     size_t newsize = newspace * sizeof (*lp->addrs);
109     void *newaddrs;
110
111     newaddrs = realloc (lp->addrs, newsize);
112     if (newaddrs == NULL)
113         return ENOMEM;
114     lp->addrs = newaddrs;
115     for (i = lp->space; i < newspace; i++) {
116         lp->addrs[i].ai = NULL;
117         lp->addrs[i].freefn = NULL;
118         lp->addrs[i].data = NULL;
119     }
120     lp->space = newspace;
121     return 0;
122 }
123 #define grow_list krb5int_grow_addrlist
124
125 /* Free up everything pointed to by the addrlist structure, but don't
126    free the structure itself.  */
127 void
128 krb5int_free_addrlist (struct addrlist *lp)
129 {
130     int i;
131     for (i = 0; i < lp->naddrs; i++)
132         if (lp->addrs[i].freefn)
133             (lp->addrs[i].freefn)(lp->addrs[i].data);
134     free (lp->addrs);
135     lp->addrs = NULL;
136     lp->naddrs = lp->space = 0;
137 }
138 #define free_list krb5int_free_addrlist
139
140 static int translate_ai_error (int err)
141 {
142     switch (err) {
143     case 0:
144         return 0;
145     case EAI_BADFLAGS:
146     case EAI_FAMILY:
147     case EAI_SOCKTYPE:
148     case EAI_SERVICE:
149         /* All of these indicate bad inputs to getaddrinfo.  */
150         return EINVAL;
151     case EAI_AGAIN:
152         /* Translate to standard errno code.  */
153         return EAGAIN;
154     case EAI_MEMORY:
155         /* Translate to standard errno code.  */
156         return ENOMEM;
157 #ifdef EAI_ADDRFAMILY
158     case EAI_ADDRFAMILY:
159 #endif
160 #if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
161     case EAI_NODATA:
162 #endif
163     case EAI_NONAME:
164         /* Name not known or no address data, but no error.  Do
165            nothing more.  */
166         return 0;
167 #ifdef EAI_OVERFLOW
168     case EAI_OVERFLOW:
169         /* An argument buffer overflowed.  */
170         return EINVAL;          /* XXX */
171 #endif
172 #ifdef EAI_SYSTEM
173     case EAI_SYSTEM:
174         /* System error, obviously.  */
175         return errno;
176 #endif
177     default:
178         /* An error code we haven't handled?  */
179         return EINVAL;
180     }
181 }
182
183 #include <stdarg.h>
184 static inline void Tprintf(const char *fmt, ...)
185 {
186 #ifdef TEST
187     va_list ap;
188     va_start(ap, fmt);
189     vfprintf(stderr, fmt, ap);
190     va_end(ap);
191 #endif
192 }
193
194 #if 0
195 extern void krb5int_debug_fprint(const char *, ...);
196 #define dprint krb5int_debug_fprint
197 #define print_addrlist krb5int_print_addrlist
198 extern void print_addrlist (const struct addrlist *a);
199 #else
200 static inline void dprint(const char *fmt, ...) { }
201 static inline void print_addrlist(const struct addrlist *a) { }
202 #endif
203
204 static int add_addrinfo_to_list (struct addrlist *lp, struct addrinfo *a,
205                                  void (*freefn)(void *), void *data)
206 {
207     int err;
208
209     dprint("\tadding %p=%A to %p (naddrs=%d space=%d)\n", a, a, lp,
210            lp->naddrs, lp->space);
211
212     if (lp->naddrs == lp->space) {
213         err = grow_list (lp, 1);
214         if (err) {
215             Tprintf ("grow_list failed %d\n", err);
216             return err;
217         }
218     }
219     Tprintf("setting element %d\n", lp->naddrs);
220     lp->addrs[lp->naddrs].ai = a;
221     lp->addrs[lp->naddrs].freefn = freefn;
222     lp->addrs[lp->naddrs].data = data;
223     lp->naddrs++;
224     Tprintf ("\tcount is now %d: ", lp->naddrs);
225     print_addrlist(lp);
226     Tprintf("\n");
227     return 0;
228 }
229
230 #define add_host_to_list krb5int_add_host_to_list
231
232 static void call_freeaddrinfo(void *data)
233 {
234     /* Strict interpretation of the C standard says we can't assume
235        that the ABI for f(void*) and f(struct foo *) will be
236        compatible.  Use this stub just to be paranoid.  */
237     freeaddrinfo(data);
238 }
239
240 int
241 krb5int_add_host_to_list (struct addrlist *lp, const char *hostname,
242                           int port, int secport,
243                           int socktype, int family)
244 {
245     struct addrinfo *addrs, *a, *anext, hint;
246     int err;
247     char portbuf[10], secportbuf[10];
248     void (*freefn)(void *);
249
250     Tprintf ("adding hostname %s, ports %d,%d, family %d, socktype %d\n",
251              hostname, ntohs (port), ntohs (secport),
252              family, socktype);
253
254     memset(&hint, 0, sizeof(hint));
255     hint.ai_family = family;
256     hint.ai_socktype = socktype;
257 #ifdef AI_NUMERICSERV
258     hint.ai_flags = AI_NUMERICSERV;
259 #endif
260     if (snprintf(portbuf, sizeof(portbuf), "%d", ntohs(port)) >= sizeof(portbuf))
261         /* XXX */
262         return EINVAL;
263     if (snprintf(secportbuf, sizeof(secportbuf), "%d", ntohs(secport)) >= sizeof(secportbuf))
264         return EINVAL;
265     err = getaddrinfo (hostname, portbuf, &hint, &addrs);
266     if (err) {
267         Tprintf ("\tgetaddrinfo(\"%s\", \"%s\", ...)\n\treturns %d: %s\n",
268                  hostname, portbuf, err, gai_strerror (err));
269         return translate_ai_error (err);
270     }
271     freefn = call_freeaddrinfo;
272     anext = 0;
273     for (a = addrs; a != 0 && err == 0; a = anext, freefn = 0) {
274         anext = a->ai_next;
275         err = add_addrinfo_to_list (lp, a, freefn, a);
276     }
277     if (err || secport == 0)
278         goto egress;
279     if (socktype == 0)
280         socktype = SOCK_DGRAM;
281     else if (socktype != SOCK_DGRAM)
282         goto egress;
283     hint.ai_family = AF_INET;
284     err = getaddrinfo (hostname, secportbuf, &hint, &addrs);
285     if (err) {
286         err = translate_ai_error (err);
287         goto egress;
288     }
289     freefn = call_freeaddrinfo;
290     for (a = addrs; a != 0 && err == 0; a = anext, freefn = 0) {
291         anext = a->ai_next;
292         err = add_addrinfo_to_list (lp, a, freefn, a);
293     }
294 egress:
295     /* XXX Memory leaks possible here if add_addrinfo_to_list fails.  */
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] = KRB5_CONF_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         free(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         free(host);
352         addrlist->naddrs = 0;
353         return 0;
354     }
355     
356     if (get_masters) {
357         realm_srv_names[0] = KRB5_CONF_REALMS;
358         realm_srv_names[1] = host;
359         realm_srv_names[2] = KRB5_CONF_ADMIN_SERVER;
360         realm_srv_names[3] = 0;
361
362         code = profile_get_values(context->profile, realm_srv_names,
363                                   &masterlist);
364
365         free(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         free(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 <krb5/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 #ifdef KRB5_USE_INET6
576             struct sockaddr_in6 sin6;
577 #endif
578         } u;
579     } *x;
580
581     if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
582         return 0;
583     if (sa->sa_family != AF_INET
584 #ifdef KRB5_USE_INET6
585         && sa->sa_family != AF_INET6
586 #endif
587         )
588         return 0;
589     x = calloc (1, sizeof (*x));
590     if (x == 0) {
591         d->out_of_mem = 1;
592         return 1;
593     }
594     x->ai.ai_addr = (struct sockaddr *) &x->u;
595     x->ai.ai_socktype = socktype;
596     x->ai.ai_family = sa->sa_family;
597     if (sa->sa_family == AF_INET) {
598         x->u.sin = *(struct sockaddr_in *)sa;
599         x->ai.ai_addrlen = sizeof(struct sockaddr_in);
600     }
601 #ifdef KRB5_USE_INET6
602     if (sa->sa_family == AF_INET6) {
603         x->u.sin6 = *(struct sockaddr_in6 *)sa;
604         x->ai.ai_addrlen = sizeof(struct sockaddr_in6);
605     }
606 #endif
607     if (add_addrinfo_to_list (d->lp, &x->ai, free, x) != 0) {
608         /* Assumes only error is ENOMEM.  */
609         d->out_of_mem = 1;
610         return 1;
611     }
612     return 0;
613 }
614
615 static krb5_error_code
616 module_locate_server (krb5_context ctx, const krb5_data *realm,
617                       struct addrlist *addrlist,
618                       enum locate_service_type svc, int socktype, int family)
619 {
620     struct krb5plugin_service_locate_result *res = NULL;
621     krb5_error_code code;
622     struct krb5plugin_service_locate_ftable *vtbl = NULL;
623     void **ptrs;
624     char *realmz;               /* NUL-terminated realm */
625     int i;
626     struct module_callback_data cbdata = { 0, };
627     const char *msg;
628
629     Tprintf("in module_locate_server\n");
630     cbdata.lp = addrlist;
631     if (!PLUGIN_DIR_OPEN (&ctx->libkrb5_plugins)) {
632         
633         code = krb5int_open_plugin_dirs (objdirs, NULL, &ctx->libkrb5_plugins,
634                                          &ctx->err);
635         if (code)
636             return KRB5_PLUGIN_NO_HANDLE;
637     }
638
639     code = krb5int_get_plugin_dir_data (&ctx->libkrb5_plugins,
640                                         "service_locator", &ptrs, &ctx->err);
641     if (code) {
642         Tprintf("error looking up plugin symbols: %s\n",
643                 (msg = krb5_get_error_message(ctx, code)));
644         krb5_free_error_message(ctx, msg);
645         return KRB5_PLUGIN_NO_HANDLE;
646     }
647
648     if (realm->length >= UINT_MAX) {
649         krb5int_free_plugin_dir_data(ptrs);
650         return ENOMEM;
651     }
652     realmz = malloc(realm->length + 1);
653     if (realmz == NULL) {
654         krb5int_free_plugin_dir_data(ptrs);
655         return ENOMEM;
656     }
657     memcpy(realmz, realm->data, realm->length);
658     realmz[realm->length] = '\0';
659     for (i = 0; ptrs[i]; i++) {
660         void *blob;
661
662         vtbl = ptrs[i];
663         Tprintf("element %d is %p\n", i, ptrs[i]);
664
665         /* For now, don't keep the plugin data alive.  For long-lived
666            contexts, it may be desirable to change that later.  */
667         code = vtbl->init(ctx, &blob);
668         if (code)
669             continue;
670
671         code = vtbl->lookup(blob, svc, realmz, socktype, family,
672                             module_callback, &cbdata);
673         vtbl->fini(blob);
674         if (code == KRB5_PLUGIN_NO_HANDLE) {
675             /* Module passes, keep going.  */
676             /* XXX */
677             Tprintf("plugin doesn't handle this realm (KRB5_PLUGIN_NO_HANDLE)\n");
678             continue;
679         }
680         if (code != 0) {
681             /* Module encountered an actual error.  */
682             Tprintf("plugin lookup routine returned error %d: %s\n",
683                     code, error_message(code));
684             free(realmz);
685             krb5int_free_plugin_dir_data (ptrs);
686             return code;
687         }
688         break;
689     }
690     if (ptrs[i] == NULL) {
691         Tprintf("ran off end of plugin list\n");
692         free(realmz);
693         krb5int_free_plugin_dir_data (ptrs);
694         return KRB5_PLUGIN_NO_HANDLE;
695     }
696     Tprintf("stopped with plugin #%d, res=%p\n", i, res);
697
698     /* Got something back, yippee.  */
699     Tprintf("now have %d addrs in list %p\n", addrlist->naddrs, addrlist);
700     print_addrlist(addrlist);
701     free(realmz);
702     krb5int_free_plugin_dir_data (ptrs);
703     return 0;
704 }
705
706 static krb5_error_code
707 prof_locate_server (krb5_context context, const krb5_data *realm,
708                     struct addrlist *addrlist,
709                     enum locate_service_type svc, int socktype, int family)
710 {
711     const char *profname;
712     int dflport1, dflport2 = 0;
713     struct servent *serv;
714
715     switch (svc) {
716     case locate_service_kdc:
717         profname = KRB5_CONF_KDC;
718         /* We used to use /etc/services for these, but enough systems
719            have old, crufty, wrong settings that this is probably
720            better.  */
721     kdc_ports:
722         dflport1 = htons(KRB5_DEFAULT_PORT);
723         dflport2 = htons(KRB5_DEFAULT_SEC_PORT);
724         break;
725     case locate_service_master_kdc:
726         profname = KRB5_CONF_MASTER_KDC;
727         goto kdc_ports;
728     case locate_service_kadmin:
729         profname = KRB5_CONF_ADMIN_SERVER;
730         dflport1 = htons(DEFAULT_KADM5_PORT);
731         break;
732     case locate_service_krb524:
733         profname = KRB5_CONF_KRB524_SERVER;
734         serv = getservbyname(KRB524_SERVICE, "udp");
735         dflport1 = serv ? serv->s_port : htons (KRB524_PORT);
736         break;
737     case locate_service_kpasswd:
738         profname = KRB5_CONF_KPASSWD_SERVER;
739         dflport1 = htons(DEFAULT_KPASSWD_PORT);
740         break;
741     default:
742         return EBUSY;           /* XXX */
743     }
744
745     return krb5_locate_srv_conf_1 (context, realm, profname, addrlist,
746                                    0, socktype,
747                                    dflport1, dflport2, family);
748 }
749
750 static krb5_error_code
751 dns_locate_server (krb5_context context, const krb5_data *realm,
752                    struct addrlist *addrlist,
753                    enum locate_service_type svc, int socktype, int family)
754 {
755     const char *dnsname;
756     int use_dns = _krb5_use_dns_kdc(context);
757     krb5_error_code code;
758
759     if (!use_dns)
760         return KRB5_PLUGIN_NO_HANDLE;
761
762     switch (svc) {
763     case locate_service_kdc:
764         dnsname = "_kerberos";
765         break;
766     case locate_service_master_kdc:
767         dnsname = "_kerberos-master";
768         break;
769     case locate_service_kadmin:
770         dnsname = "_kerberos-adm";
771         break;
772     case locate_service_krb524:
773         dnsname = "_krb524";
774         break;
775     case locate_service_kpasswd:
776         dnsname = "_kpasswd";
777         break;
778     default:
779         return KRB5_PLUGIN_NO_HANDLE;
780     }
781
782     code = 0;
783     if (socktype == SOCK_DGRAM || socktype == 0) {
784         code = krb5_locate_srv_dns_1(realm, dnsname, "_udp", addrlist, family);
785         if (code)
786             Tprintf("dns udp lookup returned error %d\n", code);
787     }
788     if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) {
789         code = krb5_locate_srv_dns_1(realm, dnsname, "_tcp", addrlist, family);
790         if (code)
791             Tprintf("dns tcp lookup returned error %d\n", code);
792     }
793     return code;
794 }
795
796 /*
797  * Wrapper function for the various backends
798  */
799
800 krb5_error_code
801 krb5int_locate_server (krb5_context context, const krb5_data *realm,
802                        struct addrlist *addrlist,
803                        enum locate_service_type svc,
804                        int socktype, int family)
805 {
806     krb5_error_code code;
807     struct addrlist al = ADDRLIST_INIT;
808
809     *addrlist = al;
810
811     if (realm == NULL || realm->data == NULL || realm->data[0] == 0) {
812         krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE,
813                                "Cannot find KDC for invalid realm name \"\"");
814         return KRB5_REALM_CANT_RESOLVE;
815     }
816
817     code = module_locate_server(context, realm, &al, svc, socktype, family);
818     Tprintf("module_locate_server returns %d\n", code);
819     if (code == KRB5_PLUGIN_NO_HANDLE) {
820         /*
821          * We always try the local file before DNS.  Note that there
822          * is no way to indicate "service not available" via the
823          * config file.
824          */
825
826         code = prof_locate_server(context, realm, &al, svc, socktype, family);
827
828 #ifdef KRB5_DNS_LOOKUP
829         if (code) {             /* Try DNS for all profile errors?  */
830             krb5_error_code code2;
831             code2 = dns_locate_server(context, realm, &al, svc, socktype,
832                                       family);
833             if (code2 != KRB5_PLUGIN_NO_HANDLE)
834                 code = code2;
835         }
836 #endif /* KRB5_DNS_LOOKUP */
837
838         /* We could put more heuristics here, like looking up a hostname
839            of "kerberos."+REALM, etc.  */
840     }
841     if (code == 0)
842         Tprintf ("krb5int_locate_server found %d addresses\n",
843                  al.naddrs);
844     else
845         Tprintf ("krb5int_locate_server returning error code %d/%s\n",
846                  code, error_message(code));
847     if (code != 0) {
848         if (al.space)
849             free_list (&al);
850         return code;
851     }
852     if (al.naddrs == 0) {       /* No good servers */
853         if (al.space)
854             free_list (&al);
855         krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE,
856                                "Cannot resolve network address for KDC in realm \"%.*s\"",
857                                realm->length, realm->data);
858                                
859         return KRB5_REALM_CANT_RESOLVE;
860     }
861     *addrlist = al;
862     return 0;
863 }
864
865 krb5_error_code
866 krb5_locate_kdc(krb5_context context, const krb5_data *realm,
867                 struct addrlist *addrlist,
868                 int get_masters, int socktype, int family)
869 {
870     return krb5int_locate_server(context, realm, addrlist,
871                                  (get_masters
872                                   ? locate_service_master_kdc
873                                   : locate_service_kdc),
874                                  socktype, family);
875 }