Merge from plugin branch
[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-plugin.h"
547
548 static const char objdir[] = LIBDIR "/krb5/plugins/libkrb5";
549
550 struct module_callback_data {
551     int out_of_mem;
552     struct addrlist *lp;
553 };
554
555 static int
556 module_callback (void *cbdata, int socktype, struct sockaddr *sa)
557 {
558     struct module_callback_data *d = cbdata;
559     struct {
560         struct addrinfo ai;
561         union {
562             struct sockaddr_in sin;
563             struct sockaddr_in6 sin6;
564         } u;
565     } *x;
566
567     if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
568         return 0;
569     if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
570         return 0;
571     x = malloc (sizeof (*x));
572     if (x == 0) {
573         d->out_of_mem = 1;
574         return 1;
575     }
576     memset(x, 0, sizeof (*x));
577     x->ai.ai_addr = (struct sockaddr *) &x->u;
578     x->ai.ai_socktype = socktype;
579     x->ai.ai_family = sa->sa_family;
580     if (sa->sa_family == AF_INET) {
581         x->u.sin = *(struct sockaddr_in *)sa;
582         x->ai.ai_addrlen = sizeof(struct sockaddr_in);
583     }
584     if (sa->sa_family == AF_INET6) {
585         x->u.sin6 = *(struct sockaddr_in6 *)sa;
586         x->ai.ai_addrlen = sizeof(struct sockaddr_in6);
587     }
588     if (add_addrinfo_to_list (d->lp, &x->ai, free, x) != 0) {
589         /* Assumes only error is ENOMEM.  */
590         d->out_of_mem = 1;
591         return 1;
592     }
593     return 0;
594 }
595
596 static krb5_error_code
597 module_locate_server (krb5_context ctx, const krb5_data *realm,
598                       struct addrlist *addrlist,
599                       enum locate_service_type svc, int socktype, int family)
600 {
601     struct krb5plugin_service_locate_result *res = NULL;
602     krb5_error_code code;
603     struct krb5plugin_service_locate_ftable *vtbl = NULL;
604     void **ptrs;
605     int i;
606     struct module_callback_data cbdata = { 0, addrlist };
607
608     Tprintf("in module_locate_server\n");
609     if (!PLUGIN_DIR_OPEN (&ctx->libkrb5_plugins)) {
610         code = krb5int_open_plugin_dir (objdir, &ctx->libkrb5_plugins);
611         if (code)
612             return KRB5_PLUGIN_NO_HANDLE;
613     }
614
615     code = krb5int_get_plugin_dir_data (&ctx->libkrb5_plugins, "service_locator", &ptrs);
616     if (code) {
617         Tprintf("error looking up plugin symbols: %s\n", error_message(code));
618         return KRB5_PLUGIN_NO_HANDLE;
619     }
620
621     for (i = 0; ptrs[i]; i++) {
622         void *blob;
623
624         vtbl = ptrs[i];
625         Tprintf("element %d is %p\n", i, ptrs[i]);
626
627         /* For now, don't keep the plugin data alive.  For long-lived
628            contexts, it may be desirable to change that later.  */
629         code = vtbl->init(ctx, &blob);
630         if (code)
631             continue;
632
633         code = vtbl->lookup(blob, svc, realm->data, socktype, family,
634                             module_callback, &cbdata);
635         vtbl->fini(blob);
636         if (code == KRB5_PLUGIN_NO_HANDLE) {
637             /* Module passes, keep going.  */
638             /* XXX */
639             Tprintf("plugin doesn't handle this realm (KRB5_PLUGIN_NO_HANDLE)\n");
640             continue;
641         }
642         if (code != 0) {
643             /* Module encountered an actual error.  */
644             Tprintf("plugin lookup routine returned error %d: %s\n",
645                     code, error_message(code));
646             krb5int_free_plugin_dir_data (ptrs);
647             return code;
648         }
649         break;
650     }
651     if (ptrs[i] == NULL) {
652         Tprintf("ran off end of plugin list\n");
653         krb5int_free_plugin_dir_data (ptrs);
654         return KRB5_PLUGIN_NO_HANDLE;
655     }
656     Tprintf("stopped with plugin #%d, res=%p\n", i, res);
657
658     /* Got something back, yippee.  */
659     Tprintf("now have %d addrs in list %p\n", addrlist->naddrs, addrlist);
660     print_addrlist(addrlist);
661     krb5int_free_plugin_dir_data (ptrs);
662     return 0;
663 }
664
665 static krb5_error_code
666 prof_locate_server (krb5_context context, const krb5_data *realm,
667                     struct addrlist *addrlist,
668                     enum locate_service_type svc, int socktype, int family)
669 {
670     const char *profname;
671     int dflport1, dflport2 = 0;
672     struct servent *serv;
673
674     switch (svc) {
675     case locate_service_kdc:
676         profname = "kdc";
677         /* We used to use /etc/services for these, but enough systems
678            have old, crufty, wrong settings that this is probably
679            better.  */
680     kdc_ports:
681         dflport1 = htons(KRB5_DEFAULT_PORT);
682         dflport2 = htons(KRB5_DEFAULT_SEC_PORT);
683         break;
684     case locate_service_master_kdc:
685         profname = "master_kdc";
686         goto kdc_ports;
687     case locate_service_kadmin:
688         profname = "admin_server";
689         dflport1 = htons(DEFAULT_KADM5_PORT);
690         break;
691     case locate_service_krb524:
692         profname = "krb524_server";
693         serv = getservbyname(KRB524_SERVICE, "udp");
694         dflport1 = serv ? serv->s_port : htons (KRB524_PORT);
695         break;
696     case locate_service_kpasswd:
697         profname = "kpasswd_server";
698         dflport1 = htons(DEFAULT_KPASSWD_PORT);
699         break;
700     default:
701         return EBUSY;           /* XXX */
702     }
703
704     return krb5_locate_srv_conf_1 (context, realm, profname, addrlist,
705                                    0, socktype,
706                                    dflport1, dflport2, family);
707 }
708
709 static krb5_error_code
710 dns_locate_server (krb5_context context, const krb5_data *realm,
711                    struct addrlist *addrlist,
712                    enum locate_service_type svc, int socktype, int family)
713 {
714     const char *dnsname;
715     int use_dns = _krb5_use_dns_kdc(context);
716     krb5_error_code code;
717
718     if (!use_dns)
719         return KRB5_PLUGIN_NO_HANDLE;
720
721     switch (svc) {
722     case locate_service_kdc:
723         dnsname = "_kerberos";
724         break;
725     case locate_service_master_kdc:
726         dnsname = "_kerberos-master";
727         break;
728     case locate_service_kadmin:
729         dnsname = "_kerberos-adm";
730         break;
731     case locate_service_krb524:
732         dnsname = "_krb524";
733         break;
734     case locate_service_kpasswd:
735         dnsname = "_kpasswd";
736         break;
737     default:
738         return KRB5_PLUGIN_NO_HANDLE;
739     }
740
741     code = 0;
742     if (socktype == SOCK_DGRAM || socktype == 0) {
743         code = krb5_locate_srv_dns_1(realm, dnsname, "_udp", addrlist, family);
744         if (code)
745             Tprintf("dns udp lookup returned error %d\n", code);
746     }
747     if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) {
748         code = krb5_locate_srv_dns_1(realm, dnsname, "_tcp", addrlist, family);
749         if (code)
750             Tprintf("dns tcp lookup returned error %d\n", code);
751     }
752     return code;
753 }
754
755 /*
756  * Wrapper function for the various backends
757  */
758
759 krb5_error_code
760 krb5int_locate_server (krb5_context context, const krb5_data *realm,
761                        struct addrlist *addrlist,
762                        enum locate_service_type svc,
763                        int socktype, int family)
764 {
765     krb5_error_code code;
766     struct addrlist al = ADDRLIST_INIT;
767
768     *addrlist = al;
769
770     code = module_locate_server(context, realm, &al, svc, socktype, family);
771     Tprintf("module_locate_server returns %d\n", code);
772     if (code != KRB5_PLUGIN_NO_HANDLE) {
773         *addrlist = al;
774         return code;
775     }
776
777     /*
778      * We always try the local file before DNS
779      */
780
781     code = prof_locate_server(context, realm, &al, svc, socktype, family);
782
783     /* We could put more heuristics here, like looking up a hostname
784        of "kerberos."+REALM, etc.  */
785
786 #ifdef KRB5_DNS_LOOKUP
787     if (code) {
788         krb5_error_code code2;
789         code2 = dns_locate_server(context, realm, &al, svc, socktype, family);
790         if (code2 != KRB5_PLUGIN_NO_HANDLE)
791             code = code2;
792     }
793 #endif /* KRB5_DNS_LOOKUP */
794     if (code == 0)
795         Tprintf ("krb5int_locate_server found %d addresses\n",
796                  al.naddrs);
797     else
798         Tprintf ("krb5int_locate_server returning error code %d/%s\n",
799                  code, error_message(code));
800     if (code != 0) {
801         if (al.space)
802             free_list (&al);
803         return code;
804     }
805     if (al.naddrs == 0) {       /* No good servers */
806         if (al.space)
807             free_list (&al);
808         return KRB5_REALM_CANT_RESOLVE;
809     }
810     *addrlist = al;
811     return 0;
812 }
813
814 krb5_error_code
815 krb5_locate_kdc(krb5_context context, const krb5_data *realm,
816                 struct addrlist *addrlist,
817                 int get_masters, int socktype, int family)
818 {
819     return krb5int_locate_server(context, realm, addrlist,
820                                  (get_masters
821                                   ? locate_service_master_kdc
822                                   : locate_service_kdc),
823                                  socktype, family);
824 }