Fix some KRB5_CALLCONV botches that were causing trouble for Windows build
[krb5.git] / src / lib / krb5 / os / locate_kdc.c
1 /*
2  * lib/krb5/os/locate_kdc.c
3  *
4  * Copyright 1990,2000,2001,2002 by the 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 #define NEED_SOCKETS
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 <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 static int get_port (const char *service, int stream, int defalt)
104 {
105 #if 0 /* Only used for "kerberos" and "kerberos-sec", and we want the
106          right port numbers even on the OSes that botch the entries in
107          /etc/services.  So don't bother with the lookup, except maybe
108          to produce a warning.  */
109     struct addrinfo hints = { 0 };
110     struct addrinfo *ai;
111     int err;
112
113     hints.ai_family = PF_INET;
114     hints.ai_socktype = stream ? SOCK_STREAM : SOCK_DGRAM;
115     err = getaddrinfo (NULL, service, &hints, &ai);
116     if (err == 0 && ai != 0) {
117         if (ai->ai_addr->sa_family == AF_INET) {
118             int port = ((struct sockaddr_in *)ai->ai_addr)->sin_port;
119             freeaddrinfo (ai);
120             return port;
121         }
122         freeaddrinfo (ai);
123     }
124 #endif
125     /* Any error - don't complain, just use default.  */
126     return htons (defalt);
127 }
128
129 int
130 krb5int_grow_addrlist (struct addrlist *lp, int nmore)
131 {
132     int i;
133     int newspace = lp->space + nmore;
134     size_t newsize = newspace * sizeof (struct addrlist);
135     struct addrinfo **newaddrs;
136
137     /* NULL check a concession to SunOS4 compatibility for now; not
138        required for pure ANSI support.  */
139     if (lp->addrs)
140         newaddrs = realloc (lp->addrs, newsize);
141     else
142         newaddrs = malloc (newsize);
143
144     if (newaddrs == NULL)
145         return errno;
146     for (i = lp->space; i < newspace; i++)
147         newaddrs[i] = NULL;
148     lp->addrs = newaddrs;
149     lp->space = newspace;
150     return 0;
151 }
152 #define grow_list krb5int_grow_addrlist
153
154 /* Free up everything pointed to by the addrlist structure, but don't
155    free the structure itself.  */
156 void
157 krb5int_free_addrlist (struct addrlist *lp)
158 {
159     int i;
160     for (i = 0; i < lp->naddrs; i++)
161         freeaddrinfo (lp->addrs[i]);
162     free (lp->addrs);
163     lp->addrs = NULL;
164     lp->naddrs = lp->space = 0;
165 }
166 #define free_list krb5int_free_addrlist
167
168 static int translate_ai_error (int err)
169 {
170     switch (err) {
171     case 0:
172         return 0;
173 #ifdef EAI_ADDRFAMILY
174     case EAI_ADDRFAMILY:
175 #endif
176     case EAI_BADFLAGS:
177     case EAI_FAMILY:
178     case EAI_SOCKTYPE:
179     case EAI_SERVICE:
180         /* All of these indicate bad inputs to getaddrinfo.  */
181         return EINVAL;
182     case EAI_AGAIN:
183         /* Translate to standard errno code.  */
184         return EAGAIN;
185     case EAI_MEMORY:
186         /* Translate to standard errno code.  */
187         return ENOMEM;
188 #if EAI_NODATA != EAI_NONAME
189     case EAI_NODATA:
190 #endif
191     case EAI_NONAME:
192         /* Name not known or no address data, but no error.  Do
193            nothing more.  */
194         return 0;
195 #ifdef EAI_SYSTEM
196     case EAI_SYSTEM:
197         /* System error, obviously.  */
198         return errno;
199 #endif
200     default:
201         /* An error code we haven't handled?  */
202         return EINVAL;
203     }
204 }
205
206 static int add_addrinfo_to_list (struct addrlist *lp, struct addrinfo *a)
207 {
208     int err;
209
210 #ifdef TEST
211     switch (a->ai_socktype) {
212     case SOCK_DGRAM:
213         fprintf(stderr, "\tdgram\n");
214         break;
215     case SOCK_STREAM:
216         fprintf(stderr, "\tstream\n");
217         break;
218     case SOCK_RAW:
219         fprintf(stderr, "\traw\n");
220         break;
221     case 0:
222         break;
223     default:
224         fprintf(stderr, "\tsocket type %d\n", a->ai_socktype);
225         break;
226     }
227 #endif
228
229     if (lp->naddrs == lp->space) {
230         err = grow_list (lp, 1);
231         if (err) {
232 #ifdef TEST
233             fprintf (stderr, "grow_list failed %d\n", err);
234 #endif
235             return err;
236         }
237     }
238     lp->addrs[lp->naddrs++] = a;
239     a->ai_next = 0;
240 #ifdef TEST
241     fprintf (stderr, "count is now %d\n", lp->naddrs);
242 #endif
243     return 0;
244 }
245
246 #define add_host_to_list krb5int_add_host_to_list
247
248 int
249 krb5int_add_host_to_list (struct addrlist *lp, const char *hostname,
250                           int port, int secport,
251                           int socktype, int family)
252 {
253     struct addrinfo *addrs, *a, *anext, hint;
254     int err;
255     char portbuf[10], secportbuf[10];
256
257 #ifdef TEST
258     fprintf (stderr, "adding hostname %s, ports %d,%d\n", hostname,
259              ntohs (port), ntohs (secport));
260 #endif
261
262     memset(&hint, 0, sizeof(hint));
263     hint.ai_family = family;
264     hint.ai_socktype = socktype;
265     sprintf(portbuf, "%d", ntohs(port));
266     sprintf(secportbuf, "%d", ntohs(secport));
267     err = getaddrinfo (hostname, portbuf, &hint, &addrs);
268     if (err)
269         return translate_ai_error (err);
270     anext = 0;
271     for (a = addrs; a != 0 && err == 0; a = anext) {
272         anext = a->ai_next;
273         err = add_addrinfo_to_list (lp, a);
274     }
275     if (err || secport == 0)
276         goto egress;
277     if (socktype == 0)
278         socktype = SOCK_DGRAM;
279     else if (socktype != SOCK_DGRAM)
280         goto egress;
281     hint.ai_family = AF_INET;
282     err = getaddrinfo (hostname, secportbuf, &hint, &addrs);
283     if (err) {
284 #if 0
285         return translate_ai_error (err);
286 #else
287         goto egress;
288 #endif
289     }
290     for (a = addrs; a != 0 && err == 0; a = anext) {
291         anext = a->ai_next;
292         err = add_addrinfo_to_list (lp, a);
293     }
294 egress:
295     if (anext)
296         freeaddrinfo (anext);
297     return err;
298 }
299
300 /*
301  * returns count of number of addresses found
302  * if master is non-NULL, it is filled in with the index of
303  * the master kdc
304  */
305
306 static krb5_error_code
307 krb5_locate_srv_conf_1(krb5_context context, const krb5_data *realm,
308                        const char * name, struct addrlist *addrlist,
309                        int get_masters, int socktype,
310                        int udpport, int sec_udpport, int family)
311 {
312     const char  *realm_srv_names[4];
313     char **masterlist, **hostlist, *host, *port, *cp;
314     krb5_error_code code;
315     int i, j, count, ismaster;
316
317 #ifdef TEST
318     fprintf (stderr,
319              "looking in krb5.conf for realm %s entry %s; ports %d,%d\n",
320              realm->data, name, ntohs (udpport), ntohs (sec_udpport));
321 #endif
322
323     if ((host = malloc(realm->length + 1)) == NULL) 
324         return ENOMEM;
325
326     strncpy(host, realm->data, realm->length);
327     host[realm->length] = '\0';
328     hostlist = 0;
329
330     masterlist = NULL;
331
332     realm_srv_names[0] = "realms";
333     realm_srv_names[1] = host;
334     realm_srv_names[2] = name;
335     realm_srv_names[3] = 0;
336
337     code = profile_get_values(context->profile, realm_srv_names, &hostlist);
338
339     if (code) {
340 #ifdef TEST
341         fprintf (stderr, "config file lookup failed: %s\n",
342                  error_message(code));
343 #endif
344         if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
345             code = KRB5_REALM_UNKNOWN;
346         krb5_xfree(host);
347         return code;
348      }
349
350     count = 0;
351     while (hostlist && hostlist[count])
352             count++;
353 #ifdef TEST
354     fprintf (stderr, "found %d entries under 'kdc'\n", count);
355 #endif
356     
357     if (count == 0) {
358         profile_free_list(hostlist);
359         krb5_xfree(host);
360         addrlist->naddrs = 0;
361         return 0;
362     }
363     
364     if (get_masters) {
365         realm_srv_names[0] = "realms";
366         realm_srv_names[1] = host;
367         realm_srv_names[2] = "admin_server";
368         realm_srv_names[3] = 0;
369
370         code = profile_get_values(context->profile, realm_srv_names,
371                                   &masterlist);
372
373         krb5_xfree(host);
374
375         if (code == 0) {
376             for (i=0; masterlist[i]; i++) {
377                 host = masterlist[i];
378
379                 /*
380                  * Strip off excess whitespace
381                  */
382                 cp = strchr(host, ' ');
383                 if (cp)
384                     *cp = 0;
385                 cp = strchr(host, '\t');
386                 if (cp)
387                     *cp = 0;
388                 cp = strchr(host, ':');
389                 if (cp)
390                     *cp = 0;
391             }
392         }
393     } else {
394         krb5_xfree(host);
395     }
396
397     /* at this point, if master is non-NULL, then either the master kdc
398        is required, and there is one, or the master kdc is not required,
399        and there may or may not be one. */
400
401 #ifdef HAVE_NETINET_IN_H
402     if (sec_udpport)
403             count = count * 2;
404 #endif
405
406     for (i=0; hostlist[i]; i++) {
407         int p1, p2;
408
409         host = hostlist[i];
410 #ifdef TEST
411         fprintf (stderr, "entry %d is '%s'\n", i, host);
412 #endif
413         /*
414          * Strip off excess whitespace
415          */
416         cp = strchr(host, ' ');
417         if (cp)
418             *cp = 0;
419         cp = strchr(host, '\t');
420         if (cp)
421             *cp = 0;
422         port = strchr(host, ':');
423         if (port) {
424             *port = 0;
425             port++;
426         }
427
428         ismaster = 0;
429         if (masterlist) {
430             for (j=0; masterlist[j]; j++) {
431                 if (strcasecmp(hostlist[i], masterlist[j]) == 0) {
432                     ismaster = 1;
433                 }
434             }
435         }
436
437         if (get_masters && !ismaster)
438             continue;
439
440         if (port) {
441             unsigned long l;
442 #ifdef HAVE_STROUL
443             char *endptr;
444             l = strtoul (port, &endptr, 10);
445             if (endptr == NULL || *endptr != 0)
446                 return EINVAL;
447 #else
448             l = atoi (port);
449 #endif
450             /* L is unsigned, don't need to check <0.  */
451             if (l > 65535)
452                 return EINVAL;
453             p1 = htons (l);
454             p2 = 0;
455         } else {
456             p1 = udpport;
457             p2 = sec_udpport;
458         }
459
460         if (socktype != 0)
461             code = add_host_to_list (addrlist, hostlist[i], p1, p2,
462                                      socktype, family);
463         else {
464             code = add_host_to_list (addrlist, hostlist[i], p1, p2,
465                                      SOCK_DGRAM, family);
466             if (code == 0)
467                 code = add_host_to_list (addrlist, hostlist[i], p1, p2,
468                                          SOCK_STREAM, family);
469         }
470         if (code) {
471 #ifdef TEST
472             fprintf (stderr, "error %d returned from add_host_to_list\n", code);
473 #endif
474             if (hostlist)
475                 profile_free_list (hostlist);
476             if (masterlist)
477                 profile_free_list (masterlist);
478             return code;
479         }
480     }
481
482     if (hostlist)
483         profile_free_list(hostlist);
484     if (masterlist)
485         profile_free_list(masterlist);
486
487     return 0;
488 }
489
490 #ifdef TEST
491 static krb5_error_code
492 krb5_locate_srv_conf(krb5_context context, const krb5_data *realm,
493                      const char *name, struct addrlist *al, int get_masters,
494                      int udpport, int sec_udpport)
495 {
496     krb5_error_code ret;
497
498     ret = krb5_locate_srv_conf_1 (context, realm, name, al,
499                                   get_masters, 0, udpport, sec_udpport, 0);
500     if (ret)
501         return ret;
502     if (al->naddrs == 0)        /* Couldn't resolve any KDC names */
503         return KRB5_REALM_CANT_RESOLVE;
504     return 0;
505 }
506 #endif
507
508 #ifdef KRB5_DNS_LOOKUP
509
510 /*
511  * Lookup a KDC via DNS SRV records
512  */
513
514 static krb5_error_code
515 krb5_locate_srv_dns_1 (const krb5_data *realm,
516                        const char *service,
517                        const char *protocol,
518                        struct addrlist *addrlist,
519                        int family)
520 {
521     union {
522         unsigned char bytes[2048];
523         HEADER hdr;
524     } answer;
525     unsigned char *p=NULL;
526     char host[MAX_DNS_NAMELEN], *h;
527     int type, rrclass;
528     int priority, weight, size, len, numanswers, numqueries, rdlen;
529     unsigned short port;
530     const int hdrsize = sizeof(HEADER);
531     struct srv_dns_entry {
532         struct srv_dns_entry *next;
533         int priority;
534         int weight;
535         unsigned short port;
536         char *host;
537     };
538
539     struct srv_dns_entry *head = NULL;
540     struct srv_dns_entry *srv = NULL, *entry = NULL;
541     krb5_error_code code = 0;
542
543     /*
544      * First off, build a query of the form:
545      *
546      * service.protocol.realm
547      *
548      * which will most likely be something like:
549      *
550      * _kerberos._udp.REALM
551      *
552      */
553
554     if ( strlen(service) + strlen(protocol) + realm->length + 6 
555          > MAX_DNS_NAMELEN )
556         goto out;
557     sprintf(host, "%s.%s.%.*s", service, protocol, (int) realm->length,
558             realm->data);
559
560     /* Realm names don't (normally) end with ".", but if the query
561        doesn't end with "." and doesn't get an answer as is, the
562        resolv code will try appending the local domain.  Since the
563        realm names are absolutes, let's stop that.  
564
565        But only if a name has been specified.  If we are performing
566        a search on the prefix alone then the intention is to allow
567        the local domain or domain search lists to be expanded.  */
568
569     h = host + strlen (host);
570     if ((h > host) && (h[-1] != '.') && ((h - host + 1) < sizeof(host)))
571         strcpy (h, ".");
572
573 #ifdef TEST
574     fprintf (stderr, "sending DNS SRV query for %s\n", host);
575 #endif
576
577     size = res_search(host, C_IN, T_SRV, answer.bytes, sizeof(answer.bytes));
578
579     if ((size < hdrsize) || (size > sizeof(answer.bytes)))
580         goto out;
581
582     /*
583      * We got an answer!  First off, parse the header and figure out how
584      * many answers we got back.
585      */
586
587     p = answer.bytes;
588
589     numqueries = ntohs(answer.hdr.qdcount);
590     numanswers = ntohs(answer.hdr.ancount);
591
592     p += sizeof(HEADER);
593
594     /*
595      * We need to skip over all of the questions, so we have to iterate
596      * over every query record.  dn_expand() is able to tell us the size
597      * of compress DNS names, so we use it.
598      */
599
600 #define INCR_CHECK(x,y) x += y; if (x > size + answer.bytes) goto out
601 #define CHECK(x,y) if (x + y > size + answer.bytes) goto out
602 #define NTOHSP(x,y) x[0] << 8 | x[1]; x += y
603
604     while (numqueries--) {
605         len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
606         if (len < 0)
607             goto out;
608         INCR_CHECK(p, len + 4);
609     }
610
611     /*
612      * We're now pointing at the answer records.  Only process them if
613      * they're actually T_SRV records (they might be CNAME records,
614      * for instance).
615      *
616      * But in a DNS reply, if you get a CNAME you always get the associated
617      * "real" RR for that CNAME.  RFC 1034, 3.6.2:
618      *
619      * CNAME RRs cause special action in DNS software.  When a name server
620      * fails to find a desired RR in the resource set associated with the
621      * domain name, it checks to see if the resource set consists of a CNAME
622      * record with a matching class.  If so, the name server includes the CNAME
623      * record in the response and restarts the query at the domain name
624      * specified in the data field of the CNAME record.  The one exception to
625      * this rule is that queries which match the CNAME type are not restarted.
626      *
627      * In other words, CNAMEs do not need to be expanded by the client.
628      */
629
630     while (numanswers--) {
631
632         /* First is the name; use dn_expand to get the compressed size */
633         len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
634         if (len < 0)
635             goto out;
636         INCR_CHECK(p, len);
637
638         /* Next is the query type */
639         CHECK(p, 2);
640         type = NTOHSP(p,2);
641
642         /* Next is the query class; also skip over 4 byte TTL */
643         CHECK(p, 6);
644         rrclass = NTOHSP(p,6);
645
646         /* Record data length */
647
648         CHECK(p,2);
649         rdlen = NTOHSP(p,2);
650
651         /*
652          * If this is an SRV record, process it.  Record format is:
653          *
654          * Priority
655          * Weight
656          * Port
657          * Server name
658          */
659
660         if (rrclass == C_IN && type == T_SRV) {
661             CHECK(p,2);
662             priority = NTOHSP(p,2);
663             CHECK(p, 2);
664             weight = NTOHSP(p,2);
665             CHECK(p, 2);
666             port = NTOHSP(p,2);
667             len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
668             if (len < 0)
669                 goto out;
670             INCR_CHECK(p, len);
671
672             /*
673              * We got everything!  Insert it into our list, but make sure
674              * it's in the right order.  Right now we don't do anything
675              * with the weight field
676              */
677
678             srv = (struct srv_dns_entry *) malloc(sizeof(struct srv_dns_entry));
679             if (srv == NULL)
680                 goto out;
681         
682             srv->priority = priority;
683             srv->weight = weight;
684             srv->port = port;
685             srv->host = strdup(host);
686
687             if (head == NULL || head->priority > srv->priority) {
688                 srv->next = head;
689                 head = srv;
690             } else
691                 /*
692                  * This is confusing.  Only insert an entry into this
693                  * spot if:
694                  * The next person has a higher priority (lower priorities
695                  * are preferred).
696                  * Or
697                  * There is no next entry (we're at the end)
698                  */
699                 for (entry = head; entry != NULL; entry = entry->next)
700                     if ((entry->next &&
701                          entry->next->priority > srv->priority) ||
702                         entry->next == NULL) {
703                         srv->next = entry->next;
704                         entry->next = srv;
705                         break;
706                     }
707         } else
708             INCR_CHECK(p, rdlen);
709     }
710         
711     /*
712      * Okay!  Now we've got a linked list of entries sorted by
713      * priority.  Start looking up A records and returning
714      * addresses.
715      */
716
717     if (head == NULL)
718         goto out;
719
720 #ifdef TEST
721     fprintf (stderr, "walking answer list:\n");
722 #endif
723     for (entry = head; entry != NULL; entry = entry->next) {
724 #ifdef TEST
725         fprintf (stderr, "\tport=%d host=%s\n", entry->port, entry->host);
726 #endif
727         code = add_host_to_list (addrlist, entry->host, htons (entry->port), 0,
728                                  (strcmp("_tcp", protocol)
729                                   ? SOCK_DGRAM
730                                   : SOCK_STREAM), family);
731         if (code)
732             break;
733     }
734 #ifdef TEST
735     fprintf (stderr, "[end]\n");
736 #endif
737
738     for (entry = head; entry != NULL; ) {
739         free(entry->host);
740         entry->host = NULL;
741         srv = entry;
742         entry = entry->next;
743         free(srv);
744         srv = NULL;
745     }
746
747   out:
748     if (srv)
749         free(srv);
750
751     return code;
752 }
753
754 #ifdef TEST
755 static krb5_error_code
756 krb5_locate_srv_dns(const krb5_data *realm,
757                     const char *service, const char *protocol,
758                     struct addrlist *al)
759 {
760     return krb5_locate_srv_dns_1 (realm, service, protocol, al, 0);
761 }
762 #endif
763 #endif /* KRB5_DNS_LOOKUP */
764
765 /*
766  * Wrapper function for the two backends
767  */
768
769 krb5_error_code
770 krb5int_locate_server (krb5_context context, const krb5_data *realm,
771                        struct addrlist *addrlist,
772                        int get_masters,
773                        const char *profname, const char *dnsname,
774                        int socktype,
775                        /* network order port numbers! */
776                        int dflport1, int dflport2,
777                        int family)
778 {
779     krb5_error_code code;
780     struct addrlist al = ADDRLIST_INIT;
781
782     *addrlist = al;
783
784     /*
785      * We always try the local file first
786      */
787
788     code = krb5_locate_srv_conf_1(context, realm, profname, &al, get_masters,
789                                   socktype, dflport1, dflport2, family);
790
791 #ifdef KRB5_DNS_LOOKUP
792     if (code && dnsname != 0) {
793         int use_dns = _krb5_use_dns_kdc(context);
794         if (use_dns) {
795             code = 0;
796             if (socktype == SOCK_DGRAM || socktype == 0) {
797                 code = krb5_locate_srv_dns_1(realm, dnsname, "_udp",
798                                              &al, family);
799 #ifdef TEST
800                 if (code)
801                     fprintf(stderr, "dns udp lookup returned error %d\n",
802                             code);
803 #endif
804             }
805             if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) {
806                 code = krb5_locate_srv_dns_1(realm, dnsname, "_tcp",
807                                              &al, family);
808 #ifdef TEST
809                 if (code)
810                     fprintf(stderr, "dns tcp lookup returned error %d\n",
811                             code);
812 #endif
813             }
814         }
815     }
816 #endif /* KRB5_DNS_LOOKUP */
817 #ifdef TEST
818     if (code == 0)
819         fprintf (stderr, "krb5int_locate_server found %d addresses\n",
820                  al.naddrs);
821     else
822         fprintf (stderr, "krb5int_locate_server returning error code %d\n",
823                  code);
824 #endif
825     if (code != 0) {
826         if (al.space)
827             free_list (&al);
828         return code;
829     }
830     if (al.naddrs == 0) {       /* No good servers */
831         if (al.space)
832             free_list (&al);
833         return KRB5_REALM_CANT_RESOLVE;
834     }
835     *addrlist = al;
836     return 0;
837 }
838
839 krb5_error_code
840 krb5_locate_kdc(krb5_context context, const krb5_data *realm,
841                 struct addrlist *addrlist,
842                 int get_masters, int socktype, int family)
843 {
844     int udpport, sec_udpport;
845
846     udpport = get_port (KDC_PORTNAME, 0, KRB5_DEFAULT_PORT);
847     if (socktype == SOCK_STREAM)
848         sec_udpport = 0;
849     else {
850         sec_udpport = get_port (KDC_SECONDARY_PORTNAME, 0,
851                                 (udpport == htons (KRB5_DEFAULT_PORT)
852                                  ? KRB5_DEFAULT_SEC_PORT
853                                  : KRB5_DEFAULT_PORT));
854         if (sec_udpport == udpport)
855             sec_udpport = 0;
856     }
857
858     return krb5int_locate_server(context, realm, addrlist, get_masters, "kdc",
859                                  (get_masters
860                                   ? "_kerberos-master"
861                                   : "_kerberos"),
862                                  socktype, udpport, sec_udpport, family);
863 }