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