2 * Copyright (c) 2001 - 2003 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include "krb5_locl.h"
37 RCSID("$Id: krbhst.c,v 1.57 2006/10/06 17:11:02 lha Exp $");
40 string_to_proto(const char *string
)
42 if(strcasecmp(string
, "udp") == 0)
43 return KRB5_KRBHST_UDP
;
44 else if(strcasecmp(string
, "tcp") == 0)
45 return KRB5_KRBHST_TCP
;
46 else if(strcasecmp(string
, "http") == 0)
47 return KRB5_KRBHST_HTTP
;
52 * set `res' and `count' to the result of looking up SRV RR in DNS for
53 * `proto', `proto', `realm' using `dns_type'.
54 * if `port' != 0, force that port number
57 static krb5_error_code
58 srv_find_realm(krb5_context context
, krb5_krbhst_info
***res
, int *count
,
59 const char *realm
, const char *dns_type
,
60 const char *proto
, const char *service
, int port
)
64 struct resource_record
*rr
;
72 proto_num
= string_to_proto(proto
);
74 krb5_set_error_string(context
, "unknown protocol `%s'", proto
);
78 if(proto_num
== KRB5_KRBHST_HTTP
)
79 def_port
= ntohs(krb5_getportbyname (context
, "http", "tcp", 80));
81 def_port
= ntohs(krb5_getportbyname (context
, service
, proto
, 88));
85 snprintf(domain
, sizeof(domain
), "_%s._%s.%s.", service
, proto
, realm
);
87 r
= dns_lookup(domain
, dns_type
);
89 return KRB5_KDC_UNREACH
;
91 for(num_srv
= 0, rr
= r
->head
; rr
; rr
= rr
->next
)
95 *res
= malloc(num_srv
* sizeof(**res
));
98 krb5_set_error_string(context
, "malloc: out of memory");
104 for(num_srv
= 0, rr
= r
->head
; rr
; rr
= rr
->next
)
105 if(rr
->type
== T_SRV
) {
106 krb5_krbhst_info
*hi
;
107 size_t len
= strlen(rr
->u
.srv
->target
);
109 hi
= calloc(1, sizeof(*hi
) + len
);
112 while(--num_srv
>= 0)
113 free((*res
)[num_srv
]);
118 (*res
)[num_srv
++] = hi
;
120 hi
->proto
= proto_num
;
122 hi
->def_port
= def_port
;
126 hi
->port
= rr
->u
.srv
->port
;
128 strlcpy(hi
->hostname
, rr
->u
.srv
->target
, len
+ 1);
138 struct krb5_krbhst_data
{
142 int port
; /* hardwired port number if != 0 */
146 #define KD_SRV_HTTP 8
147 #define KD_FALLBACK 16
148 #define KD_CONFIG_EXISTS 32
149 #define KD_LARGE_MSG 64
150 krb5_error_code (*get_next
)(krb5_context
, struct krb5_krbhst_data
*,
153 unsigned int fallback_count
;
155 struct krb5_krbhst_info
*hosts
, **index
, **end
;
159 krbhst_empty(const struct krb5_krbhst_data
*kd
)
161 return kd
->index
== &kd
->hosts
;
165 * Return the default protocol for the `kd' (either TCP or UDP)
169 krbhst_get_default_proto(struct krb5_krbhst_data
*kd
)
171 if (kd
->flags
& KD_LARGE_MSG
)
172 return KRB5_KRBHST_TCP
;
173 return KRB5_KRBHST_UDP
;
178 * parse `spec' into a krb5_krbhst_info, defaulting the port to `def_port'
179 * and forcing it to `port' if port != 0
182 static struct krb5_krbhst_info
*
183 parse_hostspec(krb5_context context
, struct krb5_krbhst_data
*kd
,
184 const char *spec
, int def_port
, int port
)
186 const char *p
= spec
;
187 struct krb5_krbhst_info
*hi
;
189 hi
= calloc(1, sizeof(*hi
) + strlen(spec
));
193 hi
->proto
= krbhst_get_default_proto(kd
);
195 if(strncmp(p
, "http://", 7) == 0){
196 hi
->proto
= KRB5_KRBHST_HTTP
;
198 } else if(strncmp(p
, "http/", 5) == 0) {
199 hi
->proto
= KRB5_KRBHST_HTTP
;
201 def_port
= ntohs(krb5_getportbyname (context
, "http", "tcp", 80));
202 }else if(strncmp(p
, "tcp/", 4) == 0){
203 hi
->proto
= KRB5_KRBHST_TCP
;
205 } else if(strncmp(p
, "udp/", 4) == 0) {
209 if(strsep_copy(&p
, ":", hi
->hostname
, strlen(spec
) + 1) < 0) {
213 /* get rid of trailing /, and convert to lower case */
214 hi
->hostname
[strcspn(hi
->hostname
, "/")] = '\0';
215 strlwr(hi
->hostname
);
217 hi
->port
= hi
->def_port
= def_port
;
220 hi
->port
= strtol(p
, &end
, 0);
232 _krb5_free_krbhst_info(krb5_krbhst_info
*hi
)
235 freeaddrinfo(hi
->ai
);
240 _krb5_krbhost_info_move(krb5_context context
,
241 krb5_krbhst_info
*from
,
242 krb5_krbhst_info
**to
)
244 size_t hostnamelen
= strlen(from
->hostname
);
245 /* trailing NUL is included in structure */
246 *to
= calloc(1, sizeof(**to
) + hostnamelen
);
248 krb5_set_error_string(context
, "malloc - out of memory");
252 (*to
)->proto
= from
->proto
;
253 (*to
)->port
= from
->port
;
254 (*to
)->def_port
= from
->def_port
;
255 (*to
)->ai
= from
->ai
;
258 memcpy((*to
)->hostname
, from
->hostname
, hostnamelen
+ 1);
264 append_host_hostinfo(struct krb5_krbhst_data
*kd
, struct krb5_krbhst_info
*host
)
266 struct krb5_krbhst_info
*h
;
268 for(h
= kd
->hosts
; h
; h
= h
->next
)
269 if(h
->proto
== host
->proto
&&
270 h
->port
== host
->port
&&
271 strcmp(h
->hostname
, host
->hostname
) == 0) {
272 _krb5_free_krbhst_info(host
);
276 kd
->end
= &host
->next
;
279 static krb5_error_code
280 append_host_string(krb5_context context
, struct krb5_krbhst_data
*kd
,
281 const char *host
, int def_port
, int port
)
283 struct krb5_krbhst_info
*hi
;
285 hi
= parse_hostspec(context
, kd
, host
, def_port
, port
);
289 append_host_hostinfo(kd
, hi
);
294 * return a readable representation of `host' in `hostname, hostlen'
297 krb5_error_code KRB5_LIB_FUNCTION
298 krb5_krbhst_format_string(krb5_context context
, const krb5_krbhst_info
*host
,
299 char *hostname
, size_t hostlen
)
301 const char *proto
= "";
302 char portstr
[7] = "";
303 if(host
->proto
== KRB5_KRBHST_TCP
)
305 else if(host
->proto
== KRB5_KRBHST_HTTP
)
307 if(host
->port
!= host
->def_port
)
308 snprintf(portstr
, sizeof(portstr
), ":%d", host
->port
);
309 snprintf(hostname
, hostlen
, "%s%s%s", proto
, host
->hostname
, portstr
);
314 * create a getaddrinfo `hints' based on `proto'
318 make_hints(struct addrinfo
*hints
, int proto
)
320 memset(hints
, 0, sizeof(*hints
));
321 hints
->ai_family
= AF_UNSPEC
;
323 case KRB5_KRBHST_UDP
:
324 hints
->ai_socktype
= SOCK_DGRAM
;
326 case KRB5_KRBHST_HTTP
:
327 case KRB5_KRBHST_TCP
:
328 hints
->ai_socktype
= SOCK_STREAM
;
334 * return an `struct addrinfo *' in `ai' corresponding to the information
335 * in `host'. free:ing is handled by krb5_krbhst_free.
338 krb5_error_code KRB5_LIB_FUNCTION
339 krb5_krbhst_get_addrinfo(krb5_context context
, krb5_krbhst_info
*host
,
340 struct addrinfo
**ai
)
342 struct addrinfo hints
;
343 char portstr
[NI_MAXSERV
];
346 if (host
->ai
== NULL
) {
347 make_hints(&hints
, host
->proto
);
348 snprintf (portstr
, sizeof(portstr
), "%d", host
->port
);
349 ret
= getaddrinfo(host
->hostname
, portstr
, &hints
, &host
->ai
);
351 return krb5_eai_to_heim_errno(ret
, errno
);
358 get_next(struct krb5_krbhst_data
*kd
, krb5_krbhst_info
**host
)
360 struct krb5_krbhst_info
*hi
= *kd
->index
;
363 kd
->index
= &(*kd
->index
)->next
;
370 srv_get_hosts(krb5_context context
, struct krb5_krbhst_data
*kd
,
371 const char *proto
, const char *service
)
373 krb5_krbhst_info
**res
;
376 if (srv_find_realm(context
, &res
, &count
, kd
->realm
, "SRV", proto
, service
,
379 for(i
= 0; i
< count
; i
++)
380 append_host_hostinfo(kd
, res
[i
]);
385 * read the configuration for `conf_string', defaulting to kd->def_port and
386 * forcing it to `kd->port' if kd->port != 0
390 config_get_hosts(krb5_context context
, struct krb5_krbhst_data
*kd
,
391 const char *conf_string
)
396 hostlist
= krb5_config_get_strings(context
, NULL
,
397 "realms", kd
->realm
, conf_string
, NULL
);
401 kd
->flags
|= KD_CONFIG_EXISTS
;
402 for(i
= 0; hostlist
&& hostlist
[i
] != NULL
; i
++)
403 append_host_string(context
, kd
, hostlist
[i
], kd
->def_port
, kd
->port
);
405 krb5_config_free_strings(hostlist
);
409 * as a fallback, look for `serv_string.kd->realm' (typically
410 * kerberos.REALM, kerberos-1.REALM, ...
411 * `port' is the default port for the service, and `proto' the
415 static krb5_error_code
416 fallback_get_hosts(krb5_context context
, struct krb5_krbhst_data
*kd
,
417 const char *serv_string
, int port
, int proto
)
422 struct addrinfo hints
;
423 char portstr
[NI_MAXSERV
];
426 * Don't try forever in case the DNS server keep returning us
427 * entries (like wildcard entries or the .nu TLD)
429 if(kd
->fallback_count
>= 5) {
430 kd
->flags
|= KD_FALLBACK
;
434 if(kd
->fallback_count
== 0)
435 asprintf(&host
, "%s.%s.", serv_string
, kd
->realm
);
437 asprintf(&host
, "%s-%d.%s.",
438 serv_string
, kd
->fallback_count
, kd
->realm
);
443 make_hints(&hints
, proto
);
444 snprintf(portstr
, sizeof(portstr
), "%d", port
);
445 ret
= getaddrinfo(host
, portstr
, &hints
, &ai
);
447 /* no more hosts, so we're done here */
449 kd
->flags
|= KD_FALLBACK
;
451 struct krb5_krbhst_info
*hi
;
452 size_t hostlen
= strlen(host
);
454 hi
= calloc(1, sizeof(*hi
) + hostlen
);
461 hi
->port
= hi
->def_port
= port
;
463 memmove(hi
->hostname
, host
, hostlen
- 1);
464 hi
->hostname
[hostlen
- 1] = '\0';
466 append_host_hostinfo(kd
, hi
);
467 kd
->fallback_count
++;
472 static krb5_error_code
473 kdc_get_next(krb5_context context
,
474 struct krb5_krbhst_data
*kd
,
475 krb5_krbhst_info
**host
)
479 if((kd
->flags
& KD_CONFIG
) == 0) {
480 config_get_hosts(context
, kd
, "kdc");
481 kd
->flags
|= KD_CONFIG
;
482 if(get_next(kd
, host
))
486 if (kd
->flags
& KD_CONFIG_EXISTS
)
487 return KRB5_KDC_UNREACH
; /* XXX */
489 if(context
->srv_lookup
) {
490 if((kd
->flags
& KD_SRV_UDP
) == 0 && (kd
->flags
& KD_LARGE_MSG
) == 0) {
491 srv_get_hosts(context
, kd
, "udp", "kerberos");
492 kd
->flags
|= KD_SRV_UDP
;
493 if(get_next(kd
, host
))
497 if((kd
->flags
& KD_SRV_TCP
) == 0) {
498 srv_get_hosts(context
, kd
, "tcp", "kerberos");
499 kd
->flags
|= KD_SRV_TCP
;
500 if(get_next(kd
, host
))
503 if((kd
->flags
& KD_SRV_HTTP
) == 0) {
504 srv_get_hosts(context
, kd
, "http", "kerberos");
505 kd
->flags
|= KD_SRV_HTTP
;
506 if(get_next(kd
, host
))
511 while((kd
->flags
& KD_FALLBACK
) == 0) {
512 ret
= fallback_get_hosts(context
, kd
, "kerberos",
514 krbhst_get_default_proto(kd
));
517 if(get_next(kd
, host
))
521 return KRB5_KDC_UNREACH
; /* XXX */
524 static krb5_error_code
525 admin_get_next(krb5_context context
,
526 struct krb5_krbhst_data
*kd
,
527 krb5_krbhst_info
**host
)
531 if((kd
->flags
& KD_CONFIG
) == 0) {
532 config_get_hosts(context
, kd
, "admin_server");
533 kd
->flags
|= KD_CONFIG
;
534 if(get_next(kd
, host
))
538 if (kd
->flags
& KD_CONFIG_EXISTS
)
539 return KRB5_KDC_UNREACH
; /* XXX */
541 if(context
->srv_lookup
) {
542 if((kd
->flags
& KD_SRV_TCP
) == 0) {
543 srv_get_hosts(context
, kd
, "tcp", "kerberos-adm");
544 kd
->flags
|= KD_SRV_TCP
;
545 if(get_next(kd
, host
))
551 && (kd
->flags
& KD_FALLBACK
) == 0) {
552 ret
= fallback_get_hosts(context
, kd
, "kerberos",
554 krbhst_get_default_proto(kd
));
557 kd
->flags
|= KD_FALLBACK
;
558 if(get_next(kd
, host
))
562 return KRB5_KDC_UNREACH
; /* XXX */
565 static krb5_error_code
566 kpasswd_get_next(krb5_context context
,
567 struct krb5_krbhst_data
*kd
,
568 krb5_krbhst_info
**host
)
572 if((kd
->flags
& KD_CONFIG
) == 0) {
573 config_get_hosts(context
, kd
, "kpasswd_server");
574 kd
->flags
|= KD_CONFIG
;
575 if(get_next(kd
, host
))
579 if (kd
->flags
& KD_CONFIG_EXISTS
)
580 return KRB5_KDC_UNREACH
; /* XXX */
582 if(context
->srv_lookup
) {
583 if((kd
->flags
& KD_SRV_UDP
) == 0) {
584 srv_get_hosts(context
, kd
, "udp", "kpasswd");
585 kd
->flags
|= KD_SRV_UDP
;
586 if(get_next(kd
, host
))
589 if((kd
->flags
& KD_SRV_TCP
) == 0) {
590 srv_get_hosts(context
, kd
, "tcp", "kpasswd");
591 kd
->flags
|= KD_SRV_TCP
;
592 if(get_next(kd
, host
))
597 /* no matches -> try admin */
599 if (krbhst_empty(kd
)) {
601 kd
->port
= kd
->def_port
;
602 kd
->get_next
= admin_get_next
;
603 ret
= (*kd
->get_next
)(context
, kd
, host
);
605 (*host
)->proto
= krbhst_get_default_proto(kd
);
609 return KRB5_KDC_UNREACH
; /* XXX */
612 static krb5_error_code
613 krb524_get_next(krb5_context context
,
614 struct krb5_krbhst_data
*kd
,
615 krb5_krbhst_info
**host
)
617 if((kd
->flags
& KD_CONFIG
) == 0) {
618 config_get_hosts(context
, kd
, "krb524_server");
619 if(get_next(kd
, host
))
621 kd
->flags
|= KD_CONFIG
;
624 if (kd
->flags
& KD_CONFIG_EXISTS
)
625 return KRB5_KDC_UNREACH
; /* XXX */
627 if(context
->srv_lookup
) {
628 if((kd
->flags
& KD_SRV_UDP
) == 0) {
629 srv_get_hosts(context
, kd
, "udp", "krb524");
630 kd
->flags
|= KD_SRV_UDP
;
631 if(get_next(kd
, host
))
635 if((kd
->flags
& KD_SRV_TCP
) == 0) {
636 srv_get_hosts(context
, kd
, "tcp", "krb524");
637 kd
->flags
|= KD_SRV_TCP
;
638 if(get_next(kd
, host
))
643 /* no matches -> try kdc */
645 if (krbhst_empty(kd
)) {
647 kd
->port
= kd
->def_port
;
648 kd
->get_next
= kdc_get_next
;
649 return (*kd
->get_next
)(context
, kd
, host
);
652 return KRB5_KDC_UNREACH
; /* XXX */
655 static struct krb5_krbhst_data
*
656 common_init(krb5_context context
,
660 struct krb5_krbhst_data
*kd
;
662 if((kd
= calloc(1, sizeof(*kd
))) == NULL
)
665 if((kd
->realm
= strdup(realm
)) == NULL
) {
670 /* For 'realms' without a . do not even think of going to DNS */
671 if (!strchr(realm
, '.'))
672 kd
->flags
|= KD_CONFIG_EXISTS
;
674 if (flags
& KRB5_KRBHST_FLAGS_LARGE_MSG
)
675 kd
->flags
|= KD_LARGE_MSG
;
676 kd
->end
= kd
->index
= &kd
->hosts
;
681 * initialize `handle' to look for hosts of type `type' in realm `realm'
684 krb5_error_code KRB5_LIB_FUNCTION
685 krb5_krbhst_init(krb5_context context
,
688 krb5_krbhst_handle
*handle
)
690 return krb5_krbhst_init_flags(context
, realm
, type
, 0, handle
);
693 krb5_error_code KRB5_LIB_FUNCTION
694 krb5_krbhst_init_flags(krb5_context context
,
698 krb5_krbhst_handle
*handle
)
700 struct krb5_krbhst_data
*kd
;
701 krb5_error_code (*next
)(krb5_context
, struct krb5_krbhst_data
*,
702 krb5_krbhst_info
**);
706 case KRB5_KRBHST_KDC
:
708 def_port
= ntohs(krb5_getportbyname (context
, "kerberos", "udp", 88));
710 case KRB5_KRBHST_ADMIN
:
711 next
= admin_get_next
;
712 def_port
= ntohs(krb5_getportbyname (context
, "kerberos-adm",
715 case KRB5_KRBHST_CHANGEPW
:
716 next
= kpasswd_get_next
;
717 def_port
= ntohs(krb5_getportbyname (context
, "kpasswd", "udp",
720 case KRB5_KRBHST_KRB524
:
721 next
= krb524_get_next
;
722 def_port
= ntohs(krb5_getportbyname (context
, "krb524", "udp", 4444));
725 krb5_set_error_string(context
, "unknown krbhst type (%u)", type
);
728 if((kd
= common_init(context
, realm
, flags
)) == NULL
)
731 kd
->def_port
= def_port
;
737 * return the next host information from `handle' in `host'
740 krb5_error_code KRB5_LIB_FUNCTION
741 krb5_krbhst_next(krb5_context context
,
742 krb5_krbhst_handle handle
,
743 krb5_krbhst_info
**host
)
745 if(get_next(handle
, host
))
748 return (*handle
->get_next
)(context
, handle
, host
);
752 * return the next host information from `handle' as a host name
753 * in `hostname' (or length `hostlen)
756 krb5_error_code KRB5_LIB_FUNCTION
757 krb5_krbhst_next_as_string(krb5_context context
,
758 krb5_krbhst_handle handle
,
763 krb5_krbhst_info
*host
;
764 ret
= krb5_krbhst_next(context
, handle
, &host
);
767 return krb5_krbhst_format_string(context
, host
, hostname
, hostlen
);
771 void KRB5_LIB_FUNCTION
772 krb5_krbhst_reset(krb5_context context
, krb5_krbhst_handle handle
)
774 handle
->index
= &handle
->hosts
;
777 void KRB5_LIB_FUNCTION
778 krb5_krbhst_free(krb5_context context
, krb5_krbhst_handle handle
)
780 krb5_krbhst_info
*h
, *next
;
785 for (h
= handle
->hosts
; h
!= NULL
; h
= next
) {
787 _krb5_free_krbhst_info(h
);
794 /* backwards compatibility ahead */
796 static krb5_error_code
797 gethostlist(krb5_context context
, const char *realm
,
798 unsigned int type
, char ***hostlist
)
802 krb5_krbhst_handle handle
;
803 char host
[MAXHOSTNAMELEN
];
804 krb5_krbhst_info
*hostinfo
;
806 ret
= krb5_krbhst_init(context
, realm
, type
, &handle
);
810 while(krb5_krbhst_next(context
, handle
, &hostinfo
) == 0)
813 return KRB5_KDC_UNREACH
;
814 *hostlist
= calloc(nhost
+ 1, sizeof(**hostlist
));
815 if(*hostlist
== NULL
) {
816 krb5_krbhst_free(context
, handle
);
820 krb5_krbhst_reset(context
, handle
);
822 while(krb5_krbhst_next_as_string(context
, handle
,
823 host
, sizeof(host
)) == 0) {
824 if(((*hostlist
)[nhost
++] = strdup(host
)) == NULL
) {
825 krb5_free_krbhst(context
, *hostlist
);
826 krb5_krbhst_free(context
, handle
);
830 (*hostlist
)[nhost
++] = NULL
;
831 krb5_krbhst_free(context
, handle
);
836 * return an malloced list of kadmin-hosts for `realm' in `hostlist'
839 krb5_error_code KRB5_LIB_FUNCTION
840 krb5_get_krb_admin_hst (krb5_context context
,
841 const krb5_realm
*realm
,
844 return gethostlist(context
, *realm
, KRB5_KRBHST_ADMIN
, hostlist
);
848 * return an malloced list of changepw-hosts for `realm' in `hostlist'
851 krb5_error_code KRB5_LIB_FUNCTION
852 krb5_get_krb_changepw_hst (krb5_context context
,
853 const krb5_realm
*realm
,
856 return gethostlist(context
, *realm
, KRB5_KRBHST_CHANGEPW
, hostlist
);
860 * return an malloced list of 524-hosts for `realm' in `hostlist'
863 krb5_error_code KRB5_LIB_FUNCTION
864 krb5_get_krb524hst (krb5_context context
,
865 const krb5_realm
*realm
,
868 return gethostlist(context
, *realm
, KRB5_KRBHST_KRB524
, hostlist
);
873 * return an malloced list of KDC's for `realm' in `hostlist'
876 krb5_error_code KRB5_LIB_FUNCTION
877 krb5_get_krbhst (krb5_context context
,
878 const krb5_realm
*realm
,
881 return gethostlist(context
, *realm
, KRB5_KRBHST_KDC
, hostlist
);
885 * free all the memory allocated in `hostlist'
888 krb5_error_code KRB5_LIB_FUNCTION
889 krb5_free_krbhst (krb5_context context
,
894 for (p
= hostlist
; *p
; ++p
)