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