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