* locate_kdc.c (make_srv_query_realm): Punt if strdup fails. Always return
[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     case EAI_BADFLAGS:
174     case EAI_FAMILY:
175     case EAI_SOCKTYPE:
176     case EAI_SERVICE:
177         /* All of these indicate bad inputs to getaddrinfo.  */
178         return EINVAL;
179     case EAI_AGAIN:
180         /* Translate to standard errno code.  */
181         return EAGAIN;
182     case EAI_MEMORY:
183         /* Translate to standard errno code.  */
184         return ENOMEM;
185 #ifdef EAI_ADDRFAMILY
186     case EAI_ADDRFAMILY:
187 #endif
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         err = translate_ai_error (err);
285         goto egress;
286     }
287     for (a = addrs; a != 0 && err == 0; a = anext) {
288         anext = a->ai_next;
289         err = add_addrinfo_to_list (lp, a);
290     }
291 egress:
292     if (anext)
293         freeaddrinfo (anext);
294     return err;
295 }
296
297 /*
298  * returns count of number of addresses found
299  * if master is non-NULL, it is filled in with the index of
300  * the master kdc
301  */
302
303 static krb5_error_code
304 krb5_locate_srv_conf_1(krb5_context context, const krb5_data *realm,
305                        const char * name, struct addrlist *addrlist,
306                        int get_masters, int socktype,
307                        int udpport, int sec_udpport, int family)
308 {
309     const char  *realm_srv_names[4];
310     char **masterlist, **hostlist, *host, *port, *cp;
311     krb5_error_code code;
312     int i, j, count, ismaster;
313
314 #ifdef TEST
315     fprintf (stderr,
316              "looking in krb5.conf for realm %s entry %s; ports %d,%d\n",
317              realm->data, name, ntohs (udpport), ntohs (sec_udpport));
318 #endif
319
320     if ((host = malloc(realm->length + 1)) == NULL) 
321         return ENOMEM;
322
323     strncpy(host, realm->data, realm->length);
324     host[realm->length] = '\0';
325     hostlist = 0;
326
327     masterlist = NULL;
328
329     realm_srv_names[0] = "realms";
330     realm_srv_names[1] = host;
331     realm_srv_names[2] = name;
332     realm_srv_names[3] = 0;
333
334     code = profile_get_values(context->profile, realm_srv_names, &hostlist);
335
336     if (code) {
337 #ifdef TEST
338         fprintf (stderr, "config file lookup failed: %s\n",
339                  error_message(code));
340 #endif
341         if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
342             code = KRB5_REALM_UNKNOWN;
343         krb5_xfree(host);
344         return code;
345      }
346
347     count = 0;
348     while (hostlist && hostlist[count])
349             count++;
350 #ifdef TEST
351     fprintf (stderr, "found %d entries under 'kdc'\n", count);
352 #endif
353     
354     if (count == 0) {
355         profile_free_list(hostlist);
356         krb5_xfree(host);
357         addrlist->naddrs = 0;
358         return 0;
359     }
360     
361     if (get_masters) {
362         realm_srv_names[0] = "realms";
363         realm_srv_names[1] = host;
364         realm_srv_names[2] = "admin_server";
365         realm_srv_names[3] = 0;
366
367         code = profile_get_values(context->profile, realm_srv_names,
368                                   &masterlist);
369
370         krb5_xfree(host);
371
372         if (code == 0) {
373             for (i=0; masterlist[i]; i++) {
374                 host = masterlist[i];
375
376                 /*
377                  * Strip off excess whitespace
378                  */
379                 cp = strchr(host, ' ');
380                 if (cp)
381                     *cp = 0;
382                 cp = strchr(host, '\t');
383                 if (cp)
384                     *cp = 0;
385                 cp = strchr(host, ':');
386                 if (cp)
387                     *cp = 0;
388             }
389         }
390     } else {
391         krb5_xfree(host);
392     }
393
394     /* at this point, if master is non-NULL, then either the master kdc
395        is required, and there is one, or the master kdc is not required,
396        and there may or may not be one. */
397
398 #ifdef HAVE_NETINET_IN_H
399     if (sec_udpport)
400             count = count * 2;
401 #endif
402
403     for (i=0; hostlist[i]; i++) {
404         int p1, p2;
405
406         host = hostlist[i];
407 #ifdef TEST
408         fprintf (stderr, "entry %d is '%s'\n", i, host);
409 #endif
410         /*
411          * Strip off excess whitespace
412          */
413         cp = strchr(host, ' ');
414         if (cp)
415             *cp = 0;
416         cp = strchr(host, '\t');
417         if (cp)
418             *cp = 0;
419         port = strchr(host, ':');
420         if (port) {
421             *port = 0;
422             port++;
423         }
424
425         ismaster = 0;
426         if (masterlist) {
427             for (j=0; masterlist[j]; j++) {
428                 if (strcasecmp(hostlist[i], masterlist[j]) == 0) {
429                     ismaster = 1;
430                 }
431             }
432         }
433
434         if (get_masters && !ismaster)
435             continue;
436
437         if (port) {
438             unsigned long l;
439 #ifdef HAVE_STROUL
440             char *endptr;
441             l = strtoul (port, &endptr, 10);
442             if (endptr == NULL || *endptr != 0)
443                 return EINVAL;
444 #else
445             l = atoi (port);
446 #endif
447             /* L is unsigned, don't need to check <0.  */
448             if (l > 65535)
449                 return EINVAL;
450             p1 = htons (l);
451             p2 = 0;
452         } else {
453             p1 = udpport;
454             p2 = sec_udpport;
455         }
456
457         if (socktype != 0)
458             code = add_host_to_list (addrlist, hostlist[i], p1, p2,
459                                      socktype, family);
460         else {
461             code = add_host_to_list (addrlist, hostlist[i], p1, p2,
462                                      SOCK_DGRAM, family);
463             if (code == 0)
464                 code = add_host_to_list (addrlist, hostlist[i], p1, p2,
465                                          SOCK_STREAM, family);
466         }
467         if (code) {
468 #ifdef TEST
469             fprintf (stderr, "error %d returned from add_host_to_list\n", code);
470 #endif
471             if (hostlist)
472                 profile_free_list (hostlist);
473             if (masterlist)
474                 profile_free_list (masterlist);
475             return code;
476         }
477     }
478
479     if (hostlist)
480         profile_free_list(hostlist);
481     if (masterlist)
482         profile_free_list(masterlist);
483
484     return 0;
485 }
486
487 #ifdef TEST
488 static krb5_error_code
489 krb5_locate_srv_conf(krb5_context context, const krb5_data *realm,
490                      const char *name, struct addrlist *al, int get_masters,
491                      int udpport, int sec_udpport)
492 {
493     krb5_error_code ret;
494
495     ret = krb5_locate_srv_conf_1 (context, realm, name, al,
496                                   get_masters, 0, udpport, sec_udpport, 0);
497     if (ret)
498         return ret;
499     if (al->naddrs == 0)        /* Couldn't resolve any KDC names */
500         return KRB5_REALM_CANT_RESOLVE;
501     return 0;
502 }
503 #endif
504
505 #ifdef KRB5_DNS_LOOKUP
506
507 /*
508  * Lookup a KDC via DNS SRV records
509  */
510
511 struct srv_dns_entry {
512     struct srv_dns_entry *next;
513     int priority;
514     int weight;
515     unsigned short port;
516     char *host;
517 };
518
519 /* Do DNS SRV query, return results in *answers.
520
521    Make best effort to return all the data we can.  On memory or
522    decoding errors, just return what we've got.  Always return 0,
523    currently.  */
524 static krb5_error_code
525 make_srv_query_realm(const krb5_data *realm,
526                      const char *service,
527                      const char *protocol,
528                      struct srv_dns_entry **answers)
529 {
530     union {
531         unsigned char bytes[2048];
532         HEADER hdr;
533     } answer;
534     unsigned char *p=NULL;
535     char host[MAX_DNS_NAMELEN], *h;
536     int type, rrclass;
537     int priority, weight, size, len, numanswers, numqueries, rdlen;
538     unsigned short port;
539     const int hdrsize = sizeof(HEADER);
540
541     struct srv_dns_entry *head = NULL;
542     struct srv_dns_entry *srv = NULL, *entry = NULL;
543
544     /*
545      * First off, build a query of the form:
546      *
547      * service.protocol.realm
548      *
549      * which will most likely be something like:
550      *
551      * _kerberos._udp.REALM
552      *
553      */
554
555     if (memchr(realm->data, 0, realm->length))
556         return 0;
557     if ( strlen(service) + strlen(protocol) + realm->length + 6 
558          > MAX_DNS_NAMELEN )
559         return 0;
560     sprintf(host, "%s.%s.%.*s", service, protocol, (int) realm->length,
561             realm->data);
562
563     /* Realm names don't (normally) end with ".", but if the query
564        doesn't end with "." and doesn't get an answer as is, the
565        resolv code will try appending the local domain.  Since the
566        realm names are absolutes, let's stop that.  
567
568        But only if a name has been specified.  If we are performing
569        a search on the prefix alone then the intention is to allow
570        the local domain or domain search lists to be expanded.  */
571
572     h = host + strlen (host);
573     if ((h[-1] != '.') && ((h - host + 1) < sizeof(host)))
574         strcpy (h, ".");
575
576 #ifdef TEST
577     fprintf (stderr, "sending DNS SRV query for %s\n", host);
578 #endif
579
580     size = res_search(host, C_IN, T_SRV, answer.bytes, sizeof(answer.bytes));
581
582     if ((size < hdrsize) || (size > sizeof(answer.bytes)))
583         goto out;
584
585     /*
586      * We got an answer!  First off, parse the header and figure out how
587      * many answers we got back.
588      */
589
590     p = answer.bytes;
591
592     numqueries = ntohs(answer.hdr.qdcount);
593     numanswers = ntohs(answer.hdr.ancount);
594
595     p += sizeof(HEADER);
596
597     /*
598      * We need to skip over all of the questions, so we have to iterate
599      * over every query record.  dn_expand() is able to tell us the size
600      * of compress DNS names, so we use it.
601      */
602
603 #define INCR_CHECK(x,y) x += y; if (x > size + answer.bytes) goto out
604 #define CHECK(x,y) if (x + y > size + answer.bytes) goto out
605 #define NTOHSP(x,y) x[0] << 8 | x[1]; x += y
606
607     while (numqueries--) {
608         len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
609         if (len < 0)
610             goto out;
611         INCR_CHECK(p, len + 4);
612     }
613
614     /*
615      * We're now pointing at the answer records.  Only process them if
616      * they're actually T_SRV records (they might be CNAME records,
617      * for instance).
618      *
619      * But in a DNS reply, if you get a CNAME you always get the associated
620      * "real" RR for that CNAME.  RFC 1034, 3.6.2:
621      *
622      * CNAME RRs cause special action in DNS software.  When a name server
623      * fails to find a desired RR in the resource set associated with the
624      * domain name, it checks to see if the resource set consists of a CNAME
625      * record with a matching class.  If so, the name server includes the CNAME
626      * record in the response and restarts the query at the domain name
627      * specified in the data field of the CNAME record.  The one exception to
628      * this rule is that queries which match the CNAME type are not restarted.
629      *
630      * In other words, CNAMEs do not need to be expanded by the client.
631      */
632
633     while (numanswers--) {
634
635         /* First is the name; use dn_expand to get the compressed size */
636         len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
637         if (len < 0)
638             goto out;
639         INCR_CHECK(p, len);
640
641         /* Next is the query type */
642         CHECK(p, 2);
643         type = NTOHSP(p,2);
644
645         /* Next is the query class; also skip over 4 byte TTL */
646         CHECK(p, 6);
647         rrclass = NTOHSP(p,6);
648
649         /* Record data length */
650
651         CHECK(p,2);
652         rdlen = NTOHSP(p,2);
653
654         /*
655          * If this is an SRV record, process it.  Record format is:
656          *
657          * Priority
658          * Weight
659          * Port
660          * Server name
661          */
662
663         if (rrclass == C_IN && type == T_SRV) {
664             CHECK(p,2);
665             priority = NTOHSP(p,2);
666             CHECK(p, 2);
667             weight = NTOHSP(p,2);
668             CHECK(p, 2);
669             port = NTOHSP(p,2);
670             len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
671             if (len < 0)
672                 goto out;
673             INCR_CHECK(p, len);
674
675             /*
676              * We got everything!  Insert it into our list, but make sure
677              * it's in the right order.  Right now we don't do anything
678              * with the weight field
679              */
680
681             srv = (struct srv_dns_entry *) malloc(sizeof(struct srv_dns_entry));
682             if (srv == NULL)
683                 goto out;
684         
685             srv->priority = priority;
686             srv->weight = weight;
687             srv->port = port;
688             srv->host = strdup(host);
689             if (srv->host == NULL) {
690                 free(srv);
691                 goto out;
692             }
693
694             if (head == NULL || head->priority > srv->priority) {
695                 srv->next = head;
696                 head = srv;
697             } else
698                 /*
699                  * This is confusing.  Only insert an entry into this
700                  * spot if:
701                  * The next person has a higher priority (lower priorities
702                  * are preferred).
703                  * Or
704                  * There is no next entry (we're at the end)
705                  */
706                 for (entry = head; entry != NULL; entry = entry->next)
707                     if ((entry->next &&
708                          entry->next->priority > srv->priority) ||
709                         entry->next == NULL) {
710                         srv->next = entry->next;
711                         entry->next = srv;
712                         break;
713                     }
714         } else
715             INCR_CHECK(p, rdlen);
716     }
717         
718   out:
719     *answers = head;
720     return 0;
721 }
722
723 static krb5_error_code
724 krb5_locate_srv_dns_1 (const krb5_data *realm,
725                        const char *service,
726                        const char *protocol,
727                        struct addrlist *addrlist,
728                        int family)
729 {
730     struct srv_dns_entry *head = NULL;
731     struct srv_dns_entry *entry = NULL, *next;
732     krb5_error_code code = 0;
733
734     code = make_srv_query_realm(realm, service, protocol, &head);
735     if (code)
736         return 0;
737
738     /*
739      * Okay!  Now we've got a linked list of entries sorted by
740      * priority.  Start looking up A records and returning
741      * addresses.
742      */
743
744     if (head == NULL)
745         return 0;
746
747     /* Check for the "." case indicating no support.  */
748     if (head->next == 0 && head->host[0] == 0) {
749         free(head->host);
750         free(head);
751         return KRB5_ERR_NO_SERVICE;
752     }
753
754 #ifdef TEST
755     fprintf (stderr, "walking answer list:\n");
756 #endif
757     for (entry = head; entry != NULL; entry = next) {
758 #ifdef TEST
759         fprintf (stderr, "\tport=%d host=%s\n", entry->port, entry->host);
760 #endif
761         next = entry->next;
762         code = add_host_to_list (addrlist, entry->host, htons (entry->port), 0,
763                                  (strcmp("_tcp", protocol)
764                                   ? SOCK_DGRAM
765                                   : SOCK_STREAM), family);
766         if (code)
767             break;
768         if (entry == head) {
769             free(entry->host);
770             free(entry);
771             head = next;
772             entry = 0;
773         }
774     }
775 #ifdef TEST
776     fprintf (stderr, "[end]\n");
777 #endif
778
779     for (entry = head; entry != NULL; ) {
780         struct srv_dns_entry *srv;
781         free(entry->host);
782         entry->host = NULL;
783         srv = entry;
784         entry = entry->next;
785         free(srv);
786     }
787
788     return code;
789 }
790 #endif /* KRB5_DNS_LOOKUP */
791
792 /*
793  * Wrapper function for the two backends
794  */
795
796 krb5_error_code
797 krb5int_locate_server (krb5_context context, const krb5_data *realm,
798                        struct addrlist *addrlist,
799                        int get_masters,
800                        const char *profname, const char *dnsname,
801                        int socktype,
802                        /* network order port numbers! */
803                        int dflport1, int dflport2,
804                        int family)
805 {
806     krb5_error_code code;
807     struct addrlist al = ADDRLIST_INIT;
808
809     *addrlist = al;
810
811     /*
812      * We always try the local file first
813      */
814
815     code = krb5_locate_srv_conf_1(context, realm, profname, &al, get_masters,
816                                   socktype, dflport1, dflport2, family);
817
818 #ifdef KRB5_DNS_LOOKUP
819     if (code && dnsname != 0) {
820         int use_dns = _krb5_use_dns_kdc(context);
821         if (use_dns) {
822             code = 0;
823             if (socktype == SOCK_DGRAM || socktype == 0) {
824                 code = krb5_locate_srv_dns_1(realm, dnsname, "_udp",
825                                              &al, family);
826 #ifdef TEST
827                 if (code)
828                     fprintf(stderr, "dns udp lookup returned error %d\n",
829                             code);
830 #endif
831             }
832             if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) {
833                 code = krb5_locate_srv_dns_1(realm, dnsname, "_tcp",
834                                              &al, family);
835 #ifdef TEST
836                 if (code)
837                     fprintf(stderr, "dns tcp lookup returned error %d\n",
838                             code);
839 #endif
840             }
841         }
842     }
843 #endif /* KRB5_DNS_LOOKUP */
844 #ifdef TEST
845     if (code == 0)
846         fprintf (stderr, "krb5int_locate_server found %d addresses\n",
847                  al.naddrs);
848     else
849         fprintf (stderr, "krb5int_locate_server returning error code %d\n",
850                  code);
851 #endif
852     if (code != 0) {
853         if (al.space)
854             free_list (&al);
855         return code;
856     }
857     if (al.naddrs == 0) {       /* No good servers */
858         if (al.space)
859             free_list (&al);
860         return KRB5_REALM_CANT_RESOLVE;
861     }
862     *addrlist = al;
863     return 0;
864 }
865
866 krb5_error_code
867 krb5_locate_kdc(krb5_context context, const krb5_data *realm,
868                 struct addrlist *addrlist,
869                 int get_masters, int socktype, int family)
870 {
871     int udpport, sec_udpport;
872
873     udpport = get_port (KDC_PORTNAME, 0, KRB5_DEFAULT_PORT);
874     if (socktype == SOCK_STREAM)
875         sec_udpport = 0;
876     else {
877         sec_udpport = get_port (KDC_SECONDARY_PORTNAME, 0,
878                                 (udpport == htons (KRB5_DEFAULT_PORT)
879                                  ? KRB5_DEFAULT_SEC_PORT
880                                  : KRB5_DEFAULT_PORT));
881         if (sec_udpport == udpport)
882             sec_udpport = 0;
883     }
884
885     return krb5int_locate_server(context, realm, addrlist, get_masters, "kdc",
886                                  (get_masters
887                                   ? "_kerberos-master"
888                                   : "_kerberos"),
889                                  socktype, udpport, sec_udpport, family);
890 }