Update internal plugin interface to add an errinfo structure to "open"
[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 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, "libdefaults",
74                               name, 0, 0, &value);
75     if (value == 0 && code == 0)
76         code = profile_get_string(context->profile, "libdefaults",
77                                   "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, "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, "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 errno;
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 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_SYSTEM
168     case EAI_SYSTEM:
169         /* System error, obviously.  */
170         return errno;
171 #endif
172     default:
173         /* An error code we haven't handled?  */
174         return EINVAL;
175     }
176 }
177
178 #include <stdarg.h>
179 static inline void Tprintf(const char *fmt, ...)
180 {
181 #ifdef TEST
182     va_list ap;
183     va_start(ap, fmt);
184     vfprintf(stderr, fmt, ap);
185     va_end(ap);
186 #endif
187 }
188
189 #if 0
190 extern void krb5int_debug_fprint(const char *, ...);
191 #define dprint krb5int_debug_fprint
192 #define print_addrlist krb5int_print_addrlist
193 extern void print_addrlist (const struct addrlist *a);
194 #else
195 static inline void dprint(const char *fmt, ...) { }
196 static inline void print_addrlist(const struct addrlist *a) { }
197 #endif
198
199 static int add_addrinfo_to_list (struct addrlist *lp, struct addrinfo *a,
200                                  void (*freefn)(void *), void *data)
201 {
202     int err;
203
204     dprint("\tadding %p=%A to %p (naddrs=%d space=%d)\n", a, a, lp,
205            lp->naddrs, lp->space);
206
207     if (lp->naddrs == lp->space) {
208         err = grow_list (lp, 1);
209         if (err) {
210             Tprintf ("grow_list failed %d\n", err);
211             return err;
212         }
213     }
214     Tprintf("setting element %d\n", lp->naddrs);
215     lp->addrs[lp->naddrs].ai = a;
216     lp->addrs[lp->naddrs].freefn = freefn;
217     lp->addrs[lp->naddrs].data = data;
218     lp->naddrs++;
219     Tprintf ("\tcount is now %d: ", lp->naddrs);
220     print_addrlist(lp);
221     Tprintf("\n");
222     return 0;
223 }
224
225 #define add_host_to_list krb5int_add_host_to_list
226
227 static void call_freeaddrinfo(void *data)
228 {
229     /* Strict interpretation of the C standard says we can't assume
230        that the ABI for f(void*) and f(struct foo *) will be
231        compatible.  Use this stub just to be paranoid.  */
232     freeaddrinfo(data);
233 }
234
235 int
236 krb5int_add_host_to_list (struct addrlist *lp, const char *hostname,
237                           int port, int secport,
238                           int socktype, int family)
239 {
240     struct addrinfo *addrs, *a, *anext, hint;
241     int err;
242     char portbuf[10], secportbuf[10];
243     void (*freefn)(void *);
244
245     Tprintf ("adding hostname %s, ports %d,%d, family %d, socktype %d\n",
246              hostname, ntohs (port), ntohs (secport),
247              family, socktype);
248
249     memset(&hint, 0, sizeof(hint));
250     hint.ai_family = family;
251     hint.ai_socktype = socktype;
252 #ifdef AI_NUMERICSERV
253     hint.ai_flags = AI_NUMERICSERV;
254 #endif
255     sprintf(portbuf, "%d", ntohs(port));
256     sprintf(secportbuf, "%d", ntohs(secport));
257     err = getaddrinfo (hostname, portbuf, &hint, &addrs);
258     if (err) {
259         Tprintf ("\tgetaddrinfo(\"%s\", \"%s\", ...)\n\treturns %d: %s\n",
260                  hostname, portbuf, err, gai_strerror (err));
261         return translate_ai_error (err);
262     }
263     freefn = call_freeaddrinfo;
264     anext = 0;
265     for (a = addrs; a != 0 && err == 0; a = anext, freefn = 0) {
266         anext = a->ai_next;
267         err = add_addrinfo_to_list (lp, a, freefn, a);
268     }
269     if (err || secport == 0)
270         goto egress;
271     if (socktype == 0)
272         socktype = SOCK_DGRAM;
273     else if (socktype != SOCK_DGRAM)
274         goto egress;
275     hint.ai_family = AF_INET;
276     err = getaddrinfo (hostname, secportbuf, &hint, &addrs);
277     if (err) {
278         err = translate_ai_error (err);
279         goto egress;
280     }
281     freefn = call_freeaddrinfo;
282     for (a = addrs; a != 0 && err == 0; a = anext, freefn = 0) {
283         anext = a->ai_next;
284         err = add_addrinfo_to_list (lp, a, freefn, a);
285     }
286 egress:
287     /* XXX Memory leaks possible here if add_addrinfo_to_list fails.  */
288     return err;
289 }
290
291 /*
292  * returns count of number of addresses found
293  * if master is non-NULL, it is filled in with the index of
294  * the master kdc
295  */
296
297 static krb5_error_code
298 krb5_locate_srv_conf_1(krb5_context context, const krb5_data *realm,
299                        const char * name, struct addrlist *addrlist,
300                        int get_masters, int socktype,
301                        int udpport, int sec_udpport, int family)
302 {
303     const char  *realm_srv_names[4];
304     char **masterlist, **hostlist, *host, *port, *cp;
305     krb5_error_code code;
306     int i, j, count, ismaster;
307
308     Tprintf ("looking in krb5.conf for realm %s entry %s; ports %d,%d\n",
309              realm->data, name, ntohs (udpport), ntohs (sec_udpport));
310
311     if ((host = malloc(realm->length + 1)) == NULL) 
312         return ENOMEM;
313
314     strncpy(host, realm->data, realm->length);
315     host[realm->length] = '\0';
316     hostlist = 0;
317
318     masterlist = NULL;
319
320     realm_srv_names[0] = "realms";
321     realm_srv_names[1] = host;
322     realm_srv_names[2] = name;
323     realm_srv_names[3] = 0;
324
325     code = profile_get_values(context->profile, realm_srv_names, &hostlist);
326
327     if (code) {
328         Tprintf ("config file lookup failed: %s\n",
329                  error_message(code));
330         if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
331             code = KRB5_REALM_UNKNOWN;
332         krb5_xfree(host);
333         return code;
334      }
335
336     count = 0;
337     while (hostlist && hostlist[count])
338             count++;
339     Tprintf ("found %d entries under 'kdc'\n", count);
340     
341     if (count == 0) {
342         profile_free_list(hostlist);
343         krb5_xfree(host);
344         addrlist->naddrs = 0;
345         return 0;
346     }
347     
348     if (get_masters) {
349         realm_srv_names[0] = "realms";
350         realm_srv_names[1] = host;
351         realm_srv_names[2] = "admin_server";
352         realm_srv_names[3] = 0;
353
354         code = profile_get_values(context->profile, realm_srv_names,
355                                   &masterlist);
356
357         krb5_xfree(host);
358
359         if (code == 0) {
360             for (i=0; masterlist[i]; i++) {
361                 host = masterlist[i];
362
363                 /*
364                  * Strip off excess whitespace
365                  */
366                 cp = strchr(host, ' ');
367                 if (cp)
368                     *cp = 0;
369                 cp = strchr(host, '\t');
370                 if (cp)
371                     *cp = 0;
372                 cp = strchr(host, ':');
373                 if (cp)
374                     *cp = 0;
375             }
376         }
377     } else {
378         krb5_xfree(host);
379     }
380
381     /* at this point, if master is non-NULL, then either the master kdc
382        is required, and there is one, or the master kdc is not required,
383        and there may or may not be one. */
384
385 #ifdef HAVE_NETINET_IN_H
386     if (sec_udpport)
387             count = count * 2;
388 #endif
389
390     for (i=0; hostlist[i]; i++) {
391         int p1, p2;
392
393         host = hostlist[i];
394         Tprintf ("entry %d is '%s'\n", i, host);
395         /*
396          * Strip off excess whitespace
397          */
398         cp = strchr(host, ' ');
399         if (cp)
400             *cp = 0;
401         cp = strchr(host, '\t');
402         if (cp)
403             *cp = 0;
404         port = strchr(host, ':');
405         if (port) {
406             *port = 0;
407             port++;
408         }
409
410         ismaster = 0;
411         if (masterlist) {
412             for (j=0; masterlist[j]; j++) {
413                 if (strcasecmp(hostlist[i], masterlist[j]) == 0) {
414                     ismaster = 1;
415                 }
416             }
417         }
418
419         if (get_masters && !ismaster)
420             continue;
421
422         if (port) {
423             unsigned long l;
424 #ifdef HAVE_STROUL
425             char *endptr;
426             l = strtoul (port, &endptr, 10);
427             if (endptr == NULL || *endptr != 0)
428                 return EINVAL;
429 #else
430             l = atoi (port);
431 #endif
432             /* L is unsigned, don't need to check <0.  */
433             if (l > 65535)
434                 return EINVAL;
435             p1 = htons (l);
436             p2 = 0;
437         } else {
438             p1 = udpport;
439             p2 = sec_udpport;
440         }
441
442         if (socktype != 0)
443             code = add_host_to_list (addrlist, hostlist[i], p1, p2,
444                                      socktype, family);
445         else {
446             code = add_host_to_list (addrlist, hostlist[i], p1, p2,
447                                      SOCK_DGRAM, family);
448             if (code == 0)
449                 code = add_host_to_list (addrlist, hostlist[i], p1, p2,
450                                          SOCK_STREAM, family);
451         }
452         if (code) {
453             Tprintf ("error %d (%s) returned from add_host_to_list\n", code,
454                      error_message (code));
455             if (hostlist)
456                 profile_free_list (hostlist);
457             if (masterlist)
458                 profile_free_list (masterlist);
459             return code;
460         }
461     }
462
463     if (hostlist)
464         profile_free_list(hostlist);
465     if (masterlist)
466         profile_free_list(masterlist);
467
468     return 0;
469 }
470
471 #ifdef TEST
472 static krb5_error_code
473 krb5_locate_srv_conf(krb5_context context, const krb5_data *realm,
474                      const char *name, struct addrlist *al, int get_masters,
475                      int udpport, int sec_udpport)
476 {
477     krb5_error_code ret;
478
479     ret = krb5_locate_srv_conf_1 (context, realm, name, al,
480                                   get_masters, 0, udpport, sec_udpport, 0);
481     if (ret)
482         return ret;
483     if (al->naddrs == 0)        /* Couldn't resolve any KDC names */
484         return KRB5_REALM_CANT_RESOLVE;
485     return 0;
486 }
487 #endif
488
489 #ifdef KRB5_DNS_LOOKUP
490 static krb5_error_code
491 krb5_locate_srv_dns_1 (const krb5_data *realm,
492                        const char *service,
493                        const char *protocol,
494                        struct addrlist *addrlist,
495                        int family)
496 {
497     struct srv_dns_entry *head = NULL;
498     struct srv_dns_entry *entry = NULL, *next;
499     krb5_error_code code = 0;
500
501     code = krb5int_make_srv_query_realm(realm, service, protocol, &head);
502     if (code)
503         return 0;
504
505     /*
506      * Okay!  Now we've got a linked list of entries sorted by
507      * priority.  Start looking up A records and returning
508      * addresses.
509      */
510
511     if (head == NULL)
512         return 0;
513
514     /* Check for the "." case indicating no support.  */
515     if (head->next == 0 && head->host[0] == 0) {
516         free(head->host);
517         free(head);
518         return KRB5_ERR_NO_SERVICE;
519     }
520
521     Tprintf ("walking answer list:\n");
522     for (entry = head; entry != NULL; entry = next) {
523         Tprintf ("\tport=%d host=%s\n", entry->port, entry->host);
524         next = entry->next;
525         code = add_host_to_list (addrlist, entry->host, htons (entry->port), 0,
526                                  (strcmp("_tcp", protocol)
527                                   ? SOCK_DGRAM
528                                   : SOCK_STREAM), family);
529         if (code) {
530             break;
531         }
532         if (entry == head) {
533             free(entry->host);
534             free(entry);
535             head = next;
536             entry = 0;
537         }
538     }
539     Tprintf ("[end]\n");
540
541     krb5int_free_srv_dns_data(head);
542     return code;
543 }
544 #endif
545
546 #include "k5-locate.h"
547
548 #ifdef KFM_FRAMEWORK_PLUGIN_DIR
549 static const char objdir[] = KFM_FRAMEWORK_PLUGIN_DIR ;
550 #else
551 static const char objdir[] = LIBDIR "/krb5/plugins/libkrb5";
552 #endif
553
554 struct module_callback_data {
555     int out_of_mem;
556     struct addrlist *lp;
557 };
558
559 static int
560 module_callback (void *cbdata, int socktype, struct sockaddr *sa)
561 {
562     struct module_callback_data *d = cbdata;
563     struct {
564         struct addrinfo ai;
565         union {
566             struct sockaddr_in sin;
567             struct sockaddr_in6 sin6;
568         } u;
569     } *x;
570
571     if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
572         return 0;
573     if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
574         return 0;
575     x = malloc (sizeof (*x));
576     if (x == 0) {
577         d->out_of_mem = 1;
578         return 1;
579     }
580     memset(x, 0, sizeof (*x));
581     x->ai.ai_addr = (struct sockaddr *) &x->u;
582     x->ai.ai_socktype = socktype;
583     x->ai.ai_family = sa->sa_family;
584     if (sa->sa_family == AF_INET) {
585         x->u.sin = *(struct sockaddr_in *)sa;
586         x->ai.ai_addrlen = sizeof(struct sockaddr_in);
587     }
588     if (sa->sa_family == AF_INET6) {
589         x->u.sin6 = *(struct sockaddr_in6 *)sa;
590         x->ai.ai_addrlen = sizeof(struct sockaddr_in6);
591     }
592     if (add_addrinfo_to_list (d->lp, &x->ai, free, x) != 0) {
593         /* Assumes only error is ENOMEM.  */
594         d->out_of_mem = 1;
595         return 1;
596     }
597     return 0;
598 }
599
600 static krb5_error_code
601 module_locate_server (krb5_context ctx, const krb5_data *realm,
602                       struct addrlist *addrlist,
603                       enum locate_service_type svc, int socktype, int family)
604 {
605     struct krb5plugin_service_locate_result *res = NULL;
606     krb5_error_code code;
607     struct krb5plugin_service_locate_ftable *vtbl = NULL;
608     void **ptrs;
609     int i;
610     struct module_callback_data cbdata = { 0, };
611
612     Tprintf("in module_locate_server\n");
613     cbdata.lp = addrlist;
614     if (!PLUGIN_DIR_OPEN (&ctx->libkrb5_plugins)) {
615         code = krb5int_open_plugin_dir (objdir, &ctx->libkrb5_plugins,
616                                         &ctx->err);
617         if (code)
618             return KRB5_PLUGIN_NO_HANDLE;
619     }
620
621     code = krb5int_get_plugin_dir_data (&ctx->libkrb5_plugins,
622                                         "service_locator", &ptrs, &ctx->err);
623     if (code) {
624         Tprintf("error looking up plugin symbols: %s\n",
625                 krb5_get_error_message(ctx, code));
626         return KRB5_PLUGIN_NO_HANDLE;
627     }
628
629     for (i = 0; ptrs[i]; i++) {
630         void *blob;
631
632         vtbl = ptrs[i];
633         Tprintf("element %d is %p\n", i, ptrs[i]);
634
635         /* For now, don't keep the plugin data alive.  For long-lived
636            contexts, it may be desirable to change that later.  */
637         code = vtbl->init(ctx, &blob);
638         if (code)
639             continue;
640
641         code = vtbl->lookup(blob, svc, realm->data, socktype, family,
642                             module_callback, &cbdata);
643         vtbl->fini(blob);
644         if (code == KRB5_PLUGIN_NO_HANDLE) {
645             /* Module passes, keep going.  */
646             /* XXX */
647             Tprintf("plugin doesn't handle this realm (KRB5_PLUGIN_NO_HANDLE)\n");
648             continue;
649         }
650         if (code != 0) {
651             /* Module encountered an actual error.  */
652             Tprintf("plugin lookup routine returned error %d: %s\n",
653                     code, error_message(code));
654             krb5int_free_plugin_dir_data (ptrs);
655             return code;
656         }
657         break;
658     }
659     if (ptrs[i] == NULL) {
660         Tprintf("ran off end of plugin list\n");
661         krb5int_free_plugin_dir_data (ptrs);
662         return KRB5_PLUGIN_NO_HANDLE;
663     }
664     Tprintf("stopped with plugin #%d, res=%p\n", i, res);
665
666     /* Got something back, yippee.  */
667     Tprintf("now have %d addrs in list %p\n", addrlist->naddrs, addrlist);
668     print_addrlist(addrlist);
669     krb5int_free_plugin_dir_data (ptrs);
670     return 0;
671 }
672
673 static krb5_error_code
674 prof_locate_server (krb5_context context, const krb5_data *realm,
675                     struct addrlist *addrlist,
676                     enum locate_service_type svc, int socktype, int family)
677 {
678     const char *profname;
679     int dflport1, dflport2 = 0;
680     struct servent *serv;
681
682     switch (svc) {
683     case locate_service_kdc:
684         profname = "kdc";
685         /* We used to use /etc/services for these, but enough systems
686            have old, crufty, wrong settings that this is probably
687            better.  */
688     kdc_ports:
689         dflport1 = htons(KRB5_DEFAULT_PORT);
690         dflport2 = htons(KRB5_DEFAULT_SEC_PORT);
691         break;
692     case locate_service_master_kdc:
693         profname = "master_kdc";
694         goto kdc_ports;
695     case locate_service_kadmin:
696         profname = "admin_server";
697         dflport1 = htons(DEFAULT_KADM5_PORT);
698         break;
699     case locate_service_krb524:
700         profname = "krb524_server";
701         serv = getservbyname(KRB524_SERVICE, "udp");
702         dflport1 = serv ? serv->s_port : htons (KRB524_PORT);
703         break;
704     case locate_service_kpasswd:
705         profname = "kpasswd_server";
706         dflport1 = htons(DEFAULT_KPASSWD_PORT);
707         break;
708     default:
709         return EBUSY;           /* XXX */
710     }
711
712     return krb5_locate_srv_conf_1 (context, realm, profname, addrlist,
713                                    0, socktype,
714                                    dflport1, dflport2, family);
715 }
716
717 static krb5_error_code
718 dns_locate_server (krb5_context context, const krb5_data *realm,
719                    struct addrlist *addrlist,
720                    enum locate_service_type svc, int socktype, int family)
721 {
722     const char *dnsname;
723     int use_dns = _krb5_use_dns_kdc(context);
724     krb5_error_code code;
725
726     if (!use_dns)
727         return KRB5_PLUGIN_NO_HANDLE;
728
729     switch (svc) {
730     case locate_service_kdc:
731         dnsname = "_kerberos";
732         break;
733     case locate_service_master_kdc:
734         dnsname = "_kerberos-master";
735         break;
736     case locate_service_kadmin:
737         dnsname = "_kerberos-adm";
738         break;
739     case locate_service_krb524:
740         dnsname = "_krb524";
741         break;
742     case locate_service_kpasswd:
743         dnsname = "_kpasswd";
744         break;
745     default:
746         return KRB5_PLUGIN_NO_HANDLE;
747     }
748
749     code = 0;
750     if (socktype == SOCK_DGRAM || socktype == 0) {
751         code = krb5_locate_srv_dns_1(realm, dnsname, "_udp", addrlist, family);
752         if (code)
753             Tprintf("dns udp lookup returned error %d\n", code);
754     }
755     if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) {
756         code = krb5_locate_srv_dns_1(realm, dnsname, "_tcp", addrlist, family);
757         if (code)
758             Tprintf("dns tcp lookup returned error %d\n", code);
759     }
760     return code;
761 }
762
763 /*
764  * Wrapper function for the various backends
765  */
766
767 krb5_error_code
768 krb5int_locate_server (krb5_context context, const krb5_data *realm,
769                        struct addrlist *addrlist,
770                        enum locate_service_type svc,
771                        int socktype, int family)
772 {
773     krb5_error_code code;
774     struct addrlist al = ADDRLIST_INIT;
775
776     *addrlist = al;
777
778     code = module_locate_server(context, realm, &al, svc, socktype, family);
779     Tprintf("module_locate_server returns %d\n", code);
780     if (code != KRB5_PLUGIN_NO_HANDLE) {
781         *addrlist = al;
782         return code;
783     }
784
785     /*
786      * We always try the local file before DNS
787      */
788
789     code = prof_locate_server(context, realm, &al, svc, socktype, family);
790
791     /* We could put more heuristics here, like looking up a hostname
792        of "kerberos."+REALM, etc.  */
793
794 #ifdef KRB5_DNS_LOOKUP
795     if (code) {
796         krb5_error_code code2;
797         code2 = dns_locate_server(context, realm, &al, svc, socktype, family);
798         if (code2 != KRB5_PLUGIN_NO_HANDLE)
799             code = code2;
800     }
801 #endif /* KRB5_DNS_LOOKUP */
802     if (code == 0)
803         Tprintf ("krb5int_locate_server found %d addresses\n",
804                  al.naddrs);
805     else
806         Tprintf ("krb5int_locate_server returning error code %d/%s\n",
807                  code, error_message(code));
808     if (code != 0) {
809         if (al.space)
810             free_list (&al);
811         return code;
812     }
813     if (al.naddrs == 0) {       /* No good servers */
814         if (al.space)
815             free_list (&al);
816         return KRB5_REALM_CANT_RESOLVE;
817     }
818     *addrlist = al;
819     return 0;
820 }
821
822 krb5_error_code
823 krb5_locate_kdc(krb5_context context, const krb5_data *realm,
824                 struct addrlist *addrlist,
825                 int get_masters, int socktype, int family)
826 {
827     return krb5int_locate_server(context, realm, addrlist,
828                                  (get_masters
829                                   ? locate_service_master_kdc
830                                   : locate_service_kdc),
831                                  socktype, family);
832 }