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