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