r19604: This is a massive commit, and I appologise in advance for it's size.
[Samba.git] / source / heimdal / lib / krb5 / krbhst.c
blobe7b2579229a63ba7d59ae53a1c5a83ff7edc2f37
1 /*
2 * Copyright (c) 2001 - 2003 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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
31 * SUCH DAMAGE.
34 #include "krb5_locl.h"
35 #include <resolve.h>
37 RCSID("$Id: krbhst.c,v 1.57 2006/10/06 17:11:02 lha Exp $");
39 static int
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;
48 return -1;
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)
62 char domain[1024];
63 struct dns_reply *r;
64 struct resource_record *rr;
65 int num_srv;
66 int proto_num;
67 int def_port;
69 *res = NULL;
70 *count = 0;
72 proto_num = string_to_proto(proto);
73 if(proto_num < 0) {
74 krb5_set_error_string(context, "unknown protocol `%s'", proto);
75 return EINVAL;
78 if(proto_num == KRB5_KRBHST_HTTP)
79 def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80));
80 else if(port == 0)
81 def_port = ntohs(krb5_getportbyname (context, service, proto, 88));
82 else
83 def_port = port;
85 snprintf(domain, sizeof(domain), "_%s._%s.%s.", service, proto, realm);
87 r = dns_lookup(domain, dns_type);
88 if(r == NULL)
89 return KRB5_KDC_UNREACH;
91 for(num_srv = 0, rr = r->head; rr; rr = rr->next)
92 if(rr->type == T_SRV)
93 num_srv++;
95 *res = malloc(num_srv * sizeof(**res));
96 if(*res == NULL) {
97 dns_free_data(r);
98 krb5_set_error_string(context, "malloc: out of memory");
99 return ENOMEM;
102 dns_srv_order(r);
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);
110 if(hi == NULL) {
111 dns_free_data(r);
112 while(--num_srv >= 0)
113 free((*res)[num_srv]);
114 free(*res);
115 *res = NULL;
116 return ENOMEM;
118 (*res)[num_srv++] = hi;
120 hi->proto = proto_num;
122 hi->def_port = def_port;
123 if (port != 0)
124 hi->port = port;
125 else
126 hi->port = rr->u.srv->port;
128 strlcpy(hi->hostname, rr->u.srv->target, len + 1);
131 *count = num_srv;
133 dns_free_data(r);
134 return 0;
138 struct krb5_krbhst_data {
139 char *realm;
140 unsigned int flags;
141 int def_port;
142 int port; /* hardwired port number if != 0 */
143 #define KD_CONFIG 1
144 #define KD_SRV_UDP 2
145 #define KD_SRV_TCP 4
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 *,
151 krb5_krbhst_info**);
153 unsigned int fallback_count;
155 struct krb5_krbhst_info *hosts, **index, **end;
158 static krb5_boolean
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)
168 static int
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));
190 if(hi == NULL)
191 return NULL;
193 hi->proto = krbhst_get_default_proto(kd);
195 if(strncmp(p, "http://", 7) == 0){
196 hi->proto = KRB5_KRBHST_HTTP;
197 p += 7;
198 } else if(strncmp(p, "http/", 5) == 0) {
199 hi->proto = KRB5_KRBHST_HTTP;
200 p += 5;
201 def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80));
202 }else if(strncmp(p, "tcp/", 4) == 0){
203 hi->proto = KRB5_KRBHST_TCP;
204 p += 4;
205 } else if(strncmp(p, "udp/", 4) == 0) {
206 p += 4;
209 if(strsep_copy(&p, ":", hi->hostname, strlen(spec) + 1) < 0) {
210 free(hi);
211 return NULL;
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;
218 if(p != NULL) {
219 char *end;
220 hi->port = strtol(p, &end, 0);
221 if(end == p) {
222 free(hi);
223 return NULL;
226 if (port)
227 hi->port = port;
228 return hi;
231 void
232 _krb5_free_krbhst_info(krb5_krbhst_info *hi)
234 if (hi->ai != NULL)
235 freeaddrinfo(hi->ai);
236 free(hi);
239 krb5_error_code
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);
247 if(*to == NULL) {
248 krb5_set_error_string(context, "malloc - out of memory");
249 return ENOMEM;
252 (*to)->proto = from->proto;
253 (*to)->port = from->port;
254 (*to)->def_port = from->def_port;
255 (*to)->ai = from->ai;
256 from->ai = NULL;
257 (*to)->next = NULL;
258 memcpy((*to)->hostname, from->hostname, hostnamelen + 1);
259 return 0;
263 static void
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);
273 return;
275 *kd->end = 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);
286 if(hi == NULL)
287 return ENOMEM;
289 append_host_hostinfo(kd, hi);
290 return 0;
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)
304 proto = "tcp/";
305 else if(host->proto == KRB5_KRBHST_HTTP)
306 proto = "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);
310 return 0;
314 * create a getaddrinfo `hints' based on `proto'
317 static void
318 make_hints(struct addrinfo *hints, int proto)
320 memset(hints, 0, sizeof(*hints));
321 hints->ai_family = AF_UNSPEC;
322 switch(proto) {
323 case KRB5_KRBHST_UDP :
324 hints->ai_socktype = SOCK_DGRAM;
325 break;
326 case KRB5_KRBHST_HTTP :
327 case KRB5_KRBHST_TCP :
328 hints->ai_socktype = SOCK_STREAM;
329 break;
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];
344 int ret;
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);
350 if (ret)
351 return krb5_eai_to_heim_errno(ret, errno);
353 *ai = host->ai;
354 return 0;
357 static krb5_boolean
358 get_next(struct krb5_krbhst_data *kd, krb5_krbhst_info **host)
360 struct krb5_krbhst_info *hi = *kd->index;
361 if(hi != NULL) {
362 *host = hi;
363 kd->index = &(*kd->index)->next;
364 return TRUE;
366 return FALSE;
369 static void
370 srv_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
371 const char *proto, const char *service)
373 krb5_krbhst_info **res;
374 int count, i;
376 if (srv_find_realm(context, &res, &count, kd->realm, "SRV", proto, service,
377 kd->port))
378 return;
379 for(i = 0; i < count; i++)
380 append_host_hostinfo(kd, res[i]);
381 free(res);
385 * read the configuration for `conf_string', defaulting to kd->def_port and
386 * forcing it to `kd->port' if kd->port != 0
389 static void
390 config_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
391 const char *conf_string)
393 int i;
395 char **hostlist;
396 hostlist = krb5_config_get_strings(context, NULL,
397 "realms", kd->realm, conf_string, NULL);
399 if(hostlist == NULL)
400 return;
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
412 * protocol
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)
419 char *host;
420 int ret;
421 struct addrinfo *ai;
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;
431 return 0;
434 if(kd->fallback_count == 0)
435 asprintf(&host, "%s.%s.", serv_string, kd->realm);
436 else
437 asprintf(&host, "%s-%d.%s.",
438 serv_string, kd->fallback_count, kd->realm);
440 if (host == NULL)
441 return ENOMEM;
443 make_hints(&hints, proto);
444 snprintf(portstr, sizeof(portstr), "%d", port);
445 ret = getaddrinfo(host, portstr, &hints, &ai);
446 if (ret) {
447 /* no more hosts, so we're done here */
448 free(host);
449 kd->flags |= KD_FALLBACK;
450 } else {
451 struct krb5_krbhst_info *hi;
452 size_t hostlen = strlen(host);
454 hi = calloc(1, sizeof(*hi) + hostlen);
455 if(hi == NULL) {
456 free(host);
457 return ENOMEM;
460 hi->proto = proto;
461 hi->port = hi->def_port = port;
462 hi->ai = ai;
463 memmove(hi->hostname, host, hostlen - 1);
464 hi->hostname[hostlen - 1] = '\0';
465 free(host);
466 append_host_hostinfo(kd, hi);
467 kd->fallback_count++;
469 return 0;
472 static krb5_error_code
473 kdc_get_next(krb5_context context,
474 struct krb5_krbhst_data *kd,
475 krb5_krbhst_info **host)
477 krb5_error_code ret;
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))
483 return 0;
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))
494 return 0;
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))
501 return 0;
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))
507 return 0;
511 while((kd->flags & KD_FALLBACK) == 0) {
512 ret = fallback_get_hosts(context, kd, "kerberos",
513 kd->def_port,
514 krbhst_get_default_proto(kd));
515 if(ret)
516 return ret;
517 if(get_next(kd, host))
518 return 0;
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)
529 krb5_error_code ret;
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))
535 return 0;
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))
546 return 0;
550 if (krbhst_empty(kd)
551 && (kd->flags & KD_FALLBACK) == 0) {
552 ret = fallback_get_hosts(context, kd, "kerberos",
553 kd->def_port,
554 krbhst_get_default_proto(kd));
555 if(ret)
556 return ret;
557 kd->flags |= KD_FALLBACK;
558 if(get_next(kd, host))
559 return 0;
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)
570 krb5_error_code ret;
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))
576 return 0;
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))
587 return 0;
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))
593 return 0;
597 /* no matches -> try admin */
599 if (krbhst_empty(kd)) {
600 kd->flags = 0;
601 kd->port = kd->def_port;
602 kd->get_next = admin_get_next;
603 ret = (*kd->get_next)(context, kd, host);
604 if (ret == 0)
605 (*host)->proto = krbhst_get_default_proto(kd);
606 return ret;
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))
620 return 0;
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))
632 return 0;
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))
639 return 0;
643 /* no matches -> try kdc */
645 if (krbhst_empty(kd)) {
646 kd->flags = 0;
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,
657 const char *realm,
658 int flags)
660 struct krb5_krbhst_data *kd;
662 if((kd = calloc(1, sizeof(*kd))) == NULL)
663 return NULL;
665 if((kd->realm = strdup(realm)) == NULL) {
666 free(kd);
667 return 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;
677 return kd;
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,
686 const char *realm,
687 unsigned int type,
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,
695 const char *realm,
696 unsigned int type,
697 int flags,
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 **);
703 int def_port;
705 switch(type) {
706 case KRB5_KRBHST_KDC:
707 next = kdc_get_next;
708 def_port = ntohs(krb5_getportbyname (context, "kerberos", "udp", 88));
709 break;
710 case KRB5_KRBHST_ADMIN:
711 next = admin_get_next;
712 def_port = ntohs(krb5_getportbyname (context, "kerberos-adm",
713 "tcp", 749));
714 break;
715 case KRB5_KRBHST_CHANGEPW:
716 next = kpasswd_get_next;
717 def_port = ntohs(krb5_getportbyname (context, "kpasswd", "udp",
718 KPASSWD_PORT));
719 break;
720 case KRB5_KRBHST_KRB524:
721 next = krb524_get_next;
722 def_port = ntohs(krb5_getportbyname (context, "krb524", "udp", 4444));
723 break;
724 default:
725 krb5_set_error_string(context, "unknown krbhst type (%u)", type);
726 return ENOTTY;
728 if((kd = common_init(context, realm, flags)) == NULL)
729 return ENOMEM;
730 kd->get_next = next;
731 kd->def_port = def_port;
732 *handle = kd;
733 return 0;
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))
746 return 0;
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,
759 char *hostname,
760 size_t hostlen)
762 krb5_error_code ret;
763 krb5_krbhst_info *host;
764 ret = krb5_krbhst_next(context, handle, &host);
765 if(ret)
766 return ret;
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;
782 if (handle == NULL)
783 return;
785 for (h = handle->hosts; h != NULL; h = next) {
786 next = h->next;
787 _krb5_free_krbhst_info(h);
790 free(handle->realm);
791 free(handle);
794 /* backwards compatibility ahead */
796 static krb5_error_code
797 gethostlist(krb5_context context, const char *realm,
798 unsigned int type, char ***hostlist)
800 krb5_error_code ret;
801 int nhost = 0;
802 krb5_krbhst_handle handle;
803 char host[MAXHOSTNAMELEN];
804 krb5_krbhst_info *hostinfo;
806 ret = krb5_krbhst_init(context, realm, type, &handle);
807 if (ret)
808 return ret;
810 while(krb5_krbhst_next(context, handle, &hostinfo) == 0)
811 nhost++;
812 if(nhost == 0)
813 return KRB5_KDC_UNREACH;
814 *hostlist = calloc(nhost + 1, sizeof(**hostlist));
815 if(*hostlist == NULL) {
816 krb5_krbhst_free(context, handle);
817 return ENOMEM;
820 krb5_krbhst_reset(context, handle);
821 nhost = 0;
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);
827 return ENOMEM;
830 (*hostlist)[nhost++] = NULL;
831 krb5_krbhst_free(context, handle);
832 return 0;
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,
842 char ***hostlist)
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,
854 char ***hostlist)
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,
866 char ***hostlist)
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,
879 char ***hostlist)
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,
890 char **hostlist)
892 char **p;
894 for (p = hostlist; *p; ++p)
895 free (*p);
896 free (hostlist);
897 return 0;