155682Smarkm/*
2233294Sstas * Copyright (c) 2001 - 2003 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
555682Smarkm *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
955682Smarkm *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
2055682Smarkm *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "krb5_locl.h"
3555682Smarkm#include <resolve.h>
36178825Sdfr#include "locate_plugin.h"
3755682Smarkm
3890926Snectarstatic int
3990926Snectarstring_to_proto(const char *string)
4090926Snectar{
4190926Snectar    if(strcasecmp(string, "udp") == 0)
4290926Snectar	return KRB5_KRBHST_UDP;
43233294Sstas    else if(strcasecmp(string, "tcp") == 0)
4490926Snectar	return KRB5_KRBHST_TCP;
45233294Sstas    else if(strcasecmp(string, "http") == 0)
4690926Snectar	return KRB5_KRBHST_HTTP;
4790926Snectar    return -1;
4890926Snectar}
4990926Snectar
5055682Smarkm/*
5190926Snectar * set `res' and `count' to the result of looking up SRV RR in DNS for
5290926Snectar * `proto', `proto', `realm' using `dns_type'.
5390926Snectar * if `port' != 0, force that port number
5455682Smarkm */
5555682Smarkm
5690926Snectarstatic krb5_error_code
57233294Sstassrv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count,
5890926Snectar	       const char *realm, const char *dns_type,
5990926Snectar	       const char *proto, const char *service, int port)
6055682Smarkm{
6190926Snectar    char domain[1024];
62233294Sstas    struct rk_dns_reply *r;
63233294Sstas    struct rk_resource_record *rr;
6490926Snectar    int num_srv;
6590926Snectar    int proto_num;
6690926Snectar    int def_port;
6755682Smarkm
68178825Sdfr    *res = NULL;
69178825Sdfr    *count = 0;
70178825Sdfr
7190926Snectar    proto_num = string_to_proto(proto);
7290926Snectar    if(proto_num < 0) {
73233294Sstas	krb5_set_error_message(context, EINVAL,
74233294Sstas			       N_("unknown protocol `%s' to lookup", ""),
75233294Sstas			       proto);
7690926Snectar	return EINVAL;
7790926Snectar    }
7890926Snectar
7990926Snectar    if(proto_num == KRB5_KRBHST_HTTP)
8090926Snectar	def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80));
8190926Snectar    else if(port == 0)
8290926Snectar	def_port = ntohs(krb5_getportbyname (context, service, proto, 88));
8390926Snectar    else
8490926Snectar	def_port = port;
8590926Snectar
8690926Snectar    snprintf(domain, sizeof(domain), "_%s._%s.%s.", service, proto, realm);
8790926Snectar
88233294Sstas    r = rk_dns_lookup(domain, dns_type);
89233294Sstas    if(r == NULL) {
90233294Sstas	_krb5_debug(context, 0,
91233294Sstas		    "DNS lookup failed domain: %s", domain);
9290926Snectar	return KRB5_KDC_UNREACH;
93233294Sstas    }
9490926Snectar
95233294Sstas    for(num_srv = 0, rr = r->head; rr; rr = rr->next)
96233294Sstas	if(rr->type == rk_ns_t_srv)
9790926Snectar	    num_srv++;
9890926Snectar
9990926Snectar    *res = malloc(num_srv * sizeof(**res));
10090926Snectar    if(*res == NULL) {
101233294Sstas	rk_dns_free_data(r);
102233294Sstas	krb5_set_error_message(context, ENOMEM,
103233294Sstas			       N_("malloc: out of memory", ""));
10455682Smarkm	return ENOMEM;
10578527Sassar    }
10690926Snectar
107233294Sstas    rk_dns_srv_order(r);
10890926Snectar
109233294Sstas    for(num_srv = 0, rr = r->head; rr; rr = rr->next)
110233294Sstas	if(rr->type == rk_ns_t_srv) {
11190926Snectar	    krb5_krbhst_info *hi;
112120945Snectar	    size_t len = strlen(rr->u.srv->target);
113120945Snectar
114120945Snectar	    hi = calloc(1, sizeof(*hi) + len);
11590926Snectar	    if(hi == NULL) {
116233294Sstas		rk_dns_free_data(r);
11790926Snectar		while(--num_srv >= 0)
11890926Snectar		    free((*res)[num_srv]);
11990926Snectar		free(*res);
120178825Sdfr		*res = NULL;
12190926Snectar		return ENOMEM;
12290926Snectar	    }
12390926Snectar	    (*res)[num_srv++] = hi;
12490926Snectar
12590926Snectar	    hi->proto = proto_num;
126233294Sstas
12790926Snectar	    hi->def_port = def_port;
12890926Snectar	    if (port != 0)
12990926Snectar		hi->port = port;
13090926Snectar	    else
13190926Snectar		hi->port = rr->u.srv->port;
13290926Snectar
133120945Snectar	    strlcpy(hi->hostname, rr->u.srv->target, len + 1);
13478527Sassar	}
13590926Snectar
13690926Snectar    *count = num_srv;
137233294Sstas
138233294Sstas    rk_dns_free_data(r);
13955682Smarkm    return 0;
14055682Smarkm}
14155682Smarkm
14290926Snectar
14390926Snectarstruct krb5_krbhst_data {
14490926Snectar    char *realm;
14590926Snectar    unsigned int flags;
14690926Snectar    int def_port;
14790926Snectar    int port;			/* hardwired port number if != 0 */
148178825Sdfr#define KD_CONFIG		 1
149178825Sdfr#define KD_SRV_UDP		 2
150178825Sdfr#define KD_SRV_TCP		 4
151178825Sdfr#define KD_SRV_HTTP		 8
152178825Sdfr#define KD_FALLBACK		16
153178825Sdfr#define KD_CONFIG_EXISTS	32
154178825Sdfr#define KD_LARGE_MSG		64
155178825Sdfr#define KD_PLUGIN	       128
156233294Sstas    krb5_error_code (*get_next)(krb5_context, struct krb5_krbhst_data *,
15790926Snectar				krb5_krbhst_info**);
15890926Snectar
15990926Snectar    unsigned int fallback_count;
16090926Snectar
16190926Snectar    struct krb5_krbhst_info *hosts, **index, **end;
16290926Snectar};
16390926Snectar
16490926Snectarstatic krb5_boolean
16590926Snectarkrbhst_empty(const struct krb5_krbhst_data *kd)
16690926Snectar{
16790926Snectar    return kd->index == &kd->hosts;
16890926Snectar}
16990926Snectar
17072445Sassar/*
171178825Sdfr * Return the default protocol for the `kd' (either TCP or UDP)
172178825Sdfr */
173178825Sdfr
174178825Sdfrstatic int
175178825Sdfrkrbhst_get_default_proto(struct krb5_krbhst_data *kd)
176178825Sdfr{
177178825Sdfr    if (kd->flags & KD_LARGE_MSG)
178178825Sdfr	return KRB5_KRBHST_TCP;
179178825Sdfr    return KRB5_KRBHST_UDP;
180178825Sdfr}
181178825Sdfr
182233294Sstas/*
183233294Sstas *
184233294Sstas */
185178825Sdfr
186233294Sstasconst char *
187233294Sstas_krb5_krbhst_get_realm(krb5_krbhst_handle handle)
188233294Sstas{
189233294Sstas    return handle->realm;
190233294Sstas}
191233294Sstas
192178825Sdfr/*
19390926Snectar * parse `spec' into a krb5_krbhst_info, defaulting the port to `def_port'
19490926Snectar * and forcing it to `port' if port != 0
19572445Sassar */
19672445Sassar
19790926Snectarstatic struct krb5_krbhst_info*
198178825Sdfrparse_hostspec(krb5_context context, struct krb5_krbhst_data *kd,
199178825Sdfr	       const char *spec, int def_port, int port)
20090926Snectar{
201233294Sstas    const char *p = spec, *q;
20290926Snectar    struct krb5_krbhst_info *hi;
203233294Sstas
20490926Snectar    hi = calloc(1, sizeof(*hi) + strlen(spec));
20590926Snectar    if(hi == NULL)
20690926Snectar	return NULL;
207233294Sstas
208178825Sdfr    hi->proto = krbhst_get_default_proto(kd);
20990926Snectar
21090926Snectar    if(strncmp(p, "http://", 7) == 0){
21190926Snectar	hi->proto = KRB5_KRBHST_HTTP;
21290926Snectar	p += 7;
21390926Snectar    } else if(strncmp(p, "http/", 5) == 0) {
21490926Snectar	hi->proto = KRB5_KRBHST_HTTP;
21590926Snectar	p += 5;
21690926Snectar	def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80));
21790926Snectar    }else if(strncmp(p, "tcp/", 4) == 0){
21890926Snectar	hi->proto = KRB5_KRBHST_TCP;
21990926Snectar	p += 4;
22090926Snectar    } else if(strncmp(p, "udp/", 4) == 0) {
22190926Snectar	p += 4;
22290926Snectar    }
22390926Snectar
224233294Sstas    if (p[0] == '[' && (q = strchr(p, ']')) != NULL) {
225233294Sstas	/* if address looks like [foo:bar] or [foo:bar]: its a ipv6
226233294Sstas	   adress, strip of [] */
227233294Sstas	memcpy(hi->hostname, &p[1], q - p - 1);
228233294Sstas	hi->hostname[q - p - 1] = '\0';
229233294Sstas	p = q + 1;
230233294Sstas	/* get trailing : */
231233294Sstas	if (p[0] == ':')
232233294Sstas	    p++;
233233294Sstas    } else if(strsep_copy(&p, ":", hi->hostname, strlen(spec) + 1) < 0) {
234233294Sstas	/* copy everything before : */
23590926Snectar	free(hi);
23690926Snectar	return NULL;
23790926Snectar    }
23890926Snectar    /* get rid of trailing /, and convert to lower case */
23990926Snectar    hi->hostname[strcspn(hi->hostname, "/")] = '\0';
24090926Snectar    strlwr(hi->hostname);
24190926Snectar
24290926Snectar    hi->port = hi->def_port = def_port;
243233294Sstas    if(p != NULL && p[0]) {
24490926Snectar	char *end;
24590926Snectar	hi->port = strtol(p, &end, 0);
24690926Snectar	if(end == p) {
24790926Snectar	    free(hi);
24890926Snectar	    return NULL;
24990926Snectar	}
25090926Snectar    }
25190926Snectar    if (port)
25290926Snectar	hi->port = port;
25390926Snectar    return hi;
25490926Snectar}
25590926Snectar
256178825Sdfrvoid
257178825Sdfr_krb5_free_krbhst_info(krb5_krbhst_info *hi)
25890926Snectar{
25990926Snectar    if (hi->ai != NULL)
26090926Snectar	freeaddrinfo(hi->ai);
26190926Snectar    free(hi);
26290926Snectar}
26390926Snectar
264178825Sdfrkrb5_error_code
265178825Sdfr_krb5_krbhost_info_move(krb5_context context,
266178825Sdfr			krb5_krbhst_info *from,
267178825Sdfr			krb5_krbhst_info **to)
268178825Sdfr{
269178825Sdfr    size_t hostnamelen = strlen(from->hostname);
270178825Sdfr    /* trailing NUL is included in structure */
271233294Sstas    *to = calloc(1, sizeof(**to) + hostnamelen);
272178825Sdfr    if(*to == NULL) {
273233294Sstas	krb5_set_error_message(context, ENOMEM,
274233294Sstas			       N_("malloc: out of memory", ""));
275178825Sdfr	return ENOMEM;
276178825Sdfr    }
277178825Sdfr
278178825Sdfr    (*to)->proto = from->proto;
279178825Sdfr    (*to)->port = from->port;
280178825Sdfr    (*to)->def_port = from->def_port;
281178825Sdfr    (*to)->ai = from->ai;
282178825Sdfr    from->ai = NULL;
283178825Sdfr    (*to)->next = NULL;
284178825Sdfr    memcpy((*to)->hostname, from->hostname, hostnamelen + 1);
285178825Sdfr    return 0;
286178825Sdfr}
287178825Sdfr
288178825Sdfr
28990926Snectarstatic void
29090926Snectarappend_host_hostinfo(struct krb5_krbhst_data *kd, struct krb5_krbhst_info *host)
29190926Snectar{
29290926Snectar    struct krb5_krbhst_info *h;
29390926Snectar
29490926Snectar    for(h = kd->hosts; h; h = h->next)
295233294Sstas	if(h->proto == host->proto &&
296233294Sstas	   h->port == host->port &&
29790926Snectar	   strcmp(h->hostname, host->hostname) == 0) {
298178825Sdfr	    _krb5_free_krbhst_info(host);
29990926Snectar	    return;
30090926Snectar	}
30190926Snectar    *kd->end = host;
30290926Snectar    kd->end = &host->next;
30390926Snectar}
30490926Snectar
30555682Smarkmstatic krb5_error_code
30690926Snectarappend_host_string(krb5_context context, struct krb5_krbhst_data *kd,
30790926Snectar		   const char *host, int def_port, int port)
30855682Smarkm{
30990926Snectar    struct krb5_krbhst_info *hi;
31055682Smarkm
311178825Sdfr    hi = parse_hostspec(context, kd, host, def_port, port);
31290926Snectar    if(hi == NULL)
31390926Snectar	return ENOMEM;
314233294Sstas
31590926Snectar    append_host_hostinfo(kd, hi);
31690926Snectar    return 0;
31790926Snectar}
31890926Snectar
31990926Snectar/*
32090926Snectar * return a readable representation of `host' in `hostname, hostlen'
32190926Snectar */
32290926Snectar
323233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
324233294Sstaskrb5_krbhst_format_string(krb5_context context, const krb5_krbhst_info *host,
32590926Snectar			  char *hostname, size_t hostlen)
32690926Snectar{
32790926Snectar    const char *proto = "";
32890926Snectar    char portstr[7] = "";
32990926Snectar    if(host->proto == KRB5_KRBHST_TCP)
33090926Snectar	proto = "tcp/";
33190926Snectar    else if(host->proto == KRB5_KRBHST_HTTP)
33290926Snectar	proto = "http://";
33390926Snectar    if(host->port != host->def_port)
33490926Snectar	snprintf(portstr, sizeof(portstr), ":%d", host->port);
33590926Snectar    snprintf(hostname, hostlen, "%s%s%s", proto, host->hostname, portstr);
33690926Snectar    return 0;
33790926Snectar}
33890926Snectar
33990926Snectar/*
34090926Snectar * create a getaddrinfo `hints' based on `proto'
34190926Snectar */
34290926Snectar
34390926Snectarstatic void
34490926Snectarmake_hints(struct addrinfo *hints, int proto)
34590926Snectar{
34690926Snectar    memset(hints, 0, sizeof(*hints));
34790926Snectar    hints->ai_family = AF_UNSPEC;
34890926Snectar    switch(proto) {
34990926Snectar    case KRB5_KRBHST_UDP :
35090926Snectar	hints->ai_socktype = SOCK_DGRAM;
35190926Snectar	break;
35290926Snectar    case KRB5_KRBHST_HTTP :
35390926Snectar    case KRB5_KRBHST_TCP :
35490926Snectar	hints->ai_socktype = SOCK_STREAM;
35590926Snectar	break;
35655682Smarkm    }
35790926Snectar}
35855682Smarkm
359233294Sstas/**
360233294Sstas * Return an `struct addrinfo *' for a KDC host.
361233294Sstas *
362233294Sstas * Returns an the struct addrinfo in in that corresponds to the
363233294Sstas * information in `host'.  free:ing is handled by krb5_krbhst_free, so
364233294Sstas * the returned ai must not be released.
365233294Sstas *
366233294Sstas * @ingroup krb5
36790926Snectar */
36855682Smarkm
369233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
37090926Snectarkrb5_krbhst_get_addrinfo(krb5_context context, krb5_krbhst_info *host,
37190926Snectar			 struct addrinfo **ai)
37290926Snectar{
373233294Sstas    int ret = 0;
37490926Snectar
37590926Snectar    if (host->ai == NULL) {
376233294Sstas	struct addrinfo hints;
377233294Sstas	char portstr[NI_MAXSERV];
378233294Sstas	char *hostname = host->hostname;
379233294Sstas
380233294Sstas	snprintf (portstr, sizeof(portstr), "%d", host->port);
38190926Snectar	make_hints(&hints, host->proto);
382233294Sstas
383233294Sstas	/**
384233294Sstas	 * First try this as an IP address, this allows us to add a
385233294Sstas	 * dot at the end to stop using the search domains.
386233294Sstas	 */
387233294Sstas
388233294Sstas	hints.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV;
389233294Sstas
39090926Snectar	ret = getaddrinfo(host->hostname, portstr, &hints, &host->ai);
391233294Sstas	if (ret == 0)
392233294Sstas	    goto out;
393233294Sstas
394233294Sstas	/**
395233294Sstas	 * If the hostname contains a dot, assumes it's a FQDN and
396233294Sstas	 * don't use search domains since that might be painfully slow
397233294Sstas	 * when machine is disconnected from that network.
398233294Sstas	 */
399233294Sstas
400233294Sstas	hints.ai_flags &= ~(AI_NUMERICHOST);
401233294Sstas
402233294Sstas	if (strchr(hostname, '.') && hostname[strlen(hostname) - 1] != '.') {
403233294Sstas	    ret = asprintf(&hostname, "%s.", host->hostname);
404233294Sstas	    if (ret < 0 || hostname == NULL)
405233294Sstas		return ENOMEM;
406233294Sstas	}
407233294Sstas
408233294Sstas	ret = getaddrinfo(hostname, portstr, &hints, &host->ai);
409233294Sstas	if (hostname != host->hostname)
410233294Sstas	    free(hostname);
411233294Sstas	if (ret) {
412233294Sstas	    ret = krb5_eai_to_heim_errno(ret, errno);
413233294Sstas	    goto out;
414233294Sstas	}
41555682Smarkm    }
416233294Sstas out:
41790926Snectar    *ai = host->ai;
418233294Sstas    return ret;
41955682Smarkm}
42055682Smarkm
42190926Snectarstatic krb5_boolean
42290926Snectarget_next(struct krb5_krbhst_data *kd, krb5_krbhst_info **host)
42390926Snectar{
42490926Snectar    struct krb5_krbhst_info *hi = *kd->index;
42590926Snectar    if(hi != NULL) {
42690926Snectar	*host = hi;
42790926Snectar	kd->index = &(*kd->index)->next;
42890926Snectar	return TRUE;
42990926Snectar    }
43090926Snectar    return FALSE;
43190926Snectar}
43290926Snectar
43390926Snectarstatic void
434233294Sstassrv_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
435178825Sdfr	      const char *proto, const char *service)
43690926Snectar{
437233294Sstas    krb5_error_code ret;
43890926Snectar    krb5_krbhst_info **res;
43990926Snectar    int count, i;
44090926Snectar
441233294Sstas    ret = srv_find_realm(context, &res, &count, kd->realm, "SRV", proto, service,
442233294Sstas			 kd->port);
443233294Sstas    _krb5_debug(context, 2, "searching DNS for realm %s %s.%s -> %d",
444233294Sstas		kd->realm, proto, service, ret);
445233294Sstas    if (ret)
446178825Sdfr	return;
44790926Snectar    for(i = 0; i < count; i++)
44890926Snectar	append_host_hostinfo(kd, res[i]);
44990926Snectar    free(res);
45090926Snectar}
45190926Snectar
45255682Smarkm/*
45390926Snectar * read the configuration for `conf_string', defaulting to kd->def_port and
45490926Snectar * forcing it to `kd->port' if kd->port != 0
45555682Smarkm */
45655682Smarkm
45790926Snectarstatic void
458233294Sstasconfig_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
45990926Snectar		 const char *conf_string)
46090926Snectar{
46190926Snectar    int i;
46290926Snectar    char **hostlist;
463233294Sstas    hostlist = krb5_config_get_strings(context, NULL,
46490926Snectar				       "realms", kd->realm, conf_string, NULL);
46590926Snectar
466233294Sstas    _krb5_debug(context, 2, "configuration file for realm %s%s found",
467233294Sstas		kd->realm, hostlist ? "" : " not");
468233294Sstas
46990926Snectar    if(hostlist == NULL)
47090926Snectar	return;
47190926Snectar    kd->flags |= KD_CONFIG_EXISTS;
47290926Snectar    for(i = 0; hostlist && hostlist[i] != NULL; i++)
47390926Snectar	append_host_string(context, kd, hostlist[i], kd->def_port, kd->port);
47490926Snectar
47590926Snectar    krb5_config_free_strings(hostlist);
47690926Snectar}
47790926Snectar
47890926Snectar/*
47990926Snectar * as a fallback, look for `serv_string.kd->realm' (typically
48090926Snectar * kerberos.REALM, kerberos-1.REALM, ...
481233294Sstas * `port' is the default port for the service, and `proto' the
48290926Snectar * protocol
48390926Snectar */
48490926Snectar
48555682Smarkmstatic krb5_error_code
486233294Sstasfallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
48790926Snectar		   const char *serv_string, int port, int proto)
48855682Smarkm{
489233294Sstas    char *host = NULL;
49090926Snectar    int ret;
49190926Snectar    struct addrinfo *ai;
49290926Snectar    struct addrinfo hints;
49390926Snectar    char portstr[NI_MAXSERV];
49490926Snectar
495233294Sstas    _krb5_debug(context, 2, "fallback lookup %d for realm %s (service %s)",
496233294Sstas		kd->fallback_count, kd->realm, serv_string);
497233294Sstas
498233294Sstas    /*
499178825Sdfr     * Don't try forever in case the DNS server keep returning us
500178825Sdfr     * entries (like wildcard entries or the .nu TLD)
501178825Sdfr     */
502178825Sdfr    if(kd->fallback_count >= 5) {
503178825Sdfr	kd->flags |= KD_FALLBACK;
504178825Sdfr	return 0;
505178825Sdfr    }
506178825Sdfr
50790926Snectar    if(kd->fallback_count == 0)
508233294Sstas	ret = asprintf(&host, "%s.%s.", serv_string, kd->realm);
50990926Snectar    else
510233294Sstas	ret = asprintf(&host, "%s-%d.%s.",
511233294Sstas		       serv_string, kd->fallback_count, kd->realm);
51290926Snectar
513233294Sstas    if (ret < 0 || host == NULL)
51490926Snectar	return ENOMEM;
515233294Sstas
51690926Snectar    make_hints(&hints, proto);
51790926Snectar    snprintf(portstr, sizeof(portstr), "%d", port);
51890926Snectar    ret = getaddrinfo(host, portstr, &hints, &ai);
51990926Snectar    if (ret) {
52090926Snectar	/* no more hosts, so we're done here */
52190926Snectar	free(host);
52290926Snectar	kd->flags |= KD_FALLBACK;
52390926Snectar    } else {
52490926Snectar	struct krb5_krbhst_info *hi;
52590926Snectar	size_t hostlen = strlen(host);
52690926Snectar
52790926Snectar	hi = calloc(1, sizeof(*hi) + hostlen);
52890926Snectar	if(hi == NULL) {
52990926Snectar	    free(host);
53090926Snectar	    return ENOMEM;
53190926Snectar	}
53290926Snectar
53390926Snectar	hi->proto = proto;
53490926Snectar	hi->port  = hi->def_port = port;
53590926Snectar	hi->ai    = ai;
536178825Sdfr	memmove(hi->hostname, host, hostlen);
537178825Sdfr	hi->hostname[hostlen] = '\0';
53890926Snectar	free(host);
53990926Snectar	append_host_hostinfo(kd, hi);
54090926Snectar	kd->fallback_count++;
54190926Snectar    }
54290926Snectar    return 0;
54390926Snectar}
54490926Snectar
545178825Sdfr/*
546178825Sdfr * Fetch hosts from plugin
547178825Sdfr */
548178825Sdfr
549233294Sstasstatic krb5_error_code
550178825Sdfradd_locate(void *ctx, int type, struct sockaddr *addr)
551178825Sdfr{
552178825Sdfr    struct krb5_krbhst_info *hi;
553178825Sdfr    struct krb5_krbhst_data *kd = ctx;
554178825Sdfr    char host[NI_MAXHOST], port[NI_MAXSERV];
555178825Sdfr    struct addrinfo hints, *ai;
556178825Sdfr    socklen_t socklen;
557178825Sdfr    size_t hostlen;
558178825Sdfr    int ret;
559178825Sdfr
560178825Sdfr    socklen = socket_sockaddr_size(addr);
561178825Sdfr
562178825Sdfr    ret = getnameinfo(addr, socklen, host, sizeof(host), port, sizeof(port),
563178825Sdfr		      NI_NUMERICHOST|NI_NUMERICSERV);
564178825Sdfr    if (ret != 0)
565178825Sdfr	return 0;
566178825Sdfr
567178825Sdfr    make_hints(&hints, krbhst_get_default_proto(kd));
568178825Sdfr    ret = getaddrinfo(host, port, &hints, &ai);
569178825Sdfr    if (ret)
570178825Sdfr	return 0;
571178825Sdfr
572178825Sdfr    hostlen = strlen(host);
573178825Sdfr
574178825Sdfr    hi = calloc(1, sizeof(*hi) + hostlen);
575178825Sdfr    if(hi == NULL)
576178825Sdfr	return ENOMEM;
577233294Sstas
578178825Sdfr    hi->proto = krbhst_get_default_proto(kd);
579178825Sdfr    hi->port  = hi->def_port = socket_get_port(addr);
580178825Sdfr    hi->ai    = ai;
581178825Sdfr    memmove(hi->hostname, host, hostlen);
582178825Sdfr    hi->hostname[hostlen] = '\0';
583178825Sdfr    append_host_hostinfo(kd, hi);
584178825Sdfr
585178825Sdfr    return 0;
586178825Sdfr}
587178825Sdfr
588178825Sdfrstatic void
589178825Sdfrplugin_get_hosts(krb5_context context,
590178825Sdfr		 struct krb5_krbhst_data *kd,
591178825Sdfr		 enum locate_service_type type)
592178825Sdfr{
593178825Sdfr    struct krb5_plugin *list = NULL, *e;
594178825Sdfr    krb5_error_code ret;
595178825Sdfr
596233294Sstas    ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA,
597233294Sstas			    KRB5_PLUGIN_LOCATE, &list);
598178825Sdfr    if(ret != 0 || list == NULL)
599178825Sdfr	return;
600178825Sdfr
601178825Sdfr    for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
602178825Sdfr	krb5plugin_service_locate_ftable *service;
603178825Sdfr	void *ctx;
604178825Sdfr
605178825Sdfr	service = _krb5_plugin_get_symbol(e);
606178825Sdfr	if (service->minor_version != 0)
607178825Sdfr	    continue;
608233294Sstas
609178825Sdfr	(*service->init)(context, &ctx);
610178825Sdfr	ret = (*service->lookup)(ctx, type, kd->realm, 0, 0, add_locate, kd);
611178825Sdfr	(*service->fini)(ctx);
612233294Sstas	if (ret && ret != KRB5_PLUGIN_NO_HANDLE) {
613233294Sstas	    krb5_set_error_message(context, ret,
614233294Sstas				   N_("Locate plugin failed to lookup realm %s: %d", ""),
615233294Sstas				   kd->realm, ret);
616178825Sdfr	    break;
617233294Sstas	} else if (ret == 0) {
618233294Sstas	    _krb5_debug(context, 2, "plugin found result for realm %s", kd->realm);
619233294Sstas	    kd->flags |= KD_CONFIG_EXISTS;
620178825Sdfr	}
621233294Sstas
622178825Sdfr    }
623178825Sdfr    _krb5_plugin_free(list);
624178825Sdfr}
625178825Sdfr
626178825Sdfr/*
627178825Sdfr *
628178825Sdfr */
629178825Sdfr
63090926Snectarstatic krb5_error_code
63190926Snectarkdc_get_next(krb5_context context,
63290926Snectar	     struct krb5_krbhst_data *kd,
63390926Snectar	     krb5_krbhst_info **host)
63490926Snectar{
63555682Smarkm    krb5_error_code ret;
63655682Smarkm
637178825Sdfr    if ((kd->flags & KD_PLUGIN) == 0) {
638178825Sdfr	plugin_get_hosts(context, kd, locate_service_kdc);
639178825Sdfr	kd->flags |= KD_PLUGIN;
640178825Sdfr	if(get_next(kd, host))
641178825Sdfr	    return 0;
642178825Sdfr    }
643178825Sdfr
64490926Snectar    if((kd->flags & KD_CONFIG) == 0) {
64590926Snectar	config_get_hosts(context, kd, "kdc");
64690926Snectar	kd->flags |= KD_CONFIG;
64790926Snectar	if(get_next(kd, host))
64890926Snectar	    return 0;
64990926Snectar    }
65055682Smarkm
651233294Sstas    if (kd->flags & KD_CONFIG_EXISTS) {
652233294Sstas	_krb5_debug(context, 1,
653233294Sstas		    "Configuration exists for realm %s, wont go to DNS",
654233294Sstas		    kd->realm);
655233294Sstas	return KRB5_KDC_UNREACH;
656233294Sstas    }
65790926Snectar
65890926Snectar    if(context->srv_lookup) {
659178825Sdfr	if((kd->flags & KD_SRV_UDP) == 0 && (kd->flags & KD_LARGE_MSG) == 0) {
66090926Snectar	    srv_get_hosts(context, kd, "udp", "kerberos");
66190926Snectar	    kd->flags |= KD_SRV_UDP;
66290926Snectar	    if(get_next(kd, host))
66390926Snectar		return 0;
66455682Smarkm	}
66590926Snectar
66690926Snectar	if((kd->flags & KD_SRV_TCP) == 0) {
66790926Snectar	    srv_get_hosts(context, kd, "tcp", "kerberos");
66890926Snectar	    kd->flags |= KD_SRV_TCP;
66990926Snectar	    if(get_next(kd, host))
67090926Snectar		return 0;
67190926Snectar	}
67290926Snectar	if((kd->flags & KD_SRV_HTTP) == 0) {
67390926Snectar	    srv_get_hosts(context, kd, "http", "kerberos");
67490926Snectar	    kd->flags |= KD_SRV_HTTP;
67590926Snectar	    if(get_next(kd, host))
67690926Snectar		return 0;
67790926Snectar	}
67855682Smarkm    }
67955682Smarkm
68090926Snectar    while((kd->flags & KD_FALLBACK) == 0) {
68190926Snectar	ret = fallback_get_hosts(context, kd, "kerberos",
682233294Sstas				 kd->def_port,
683178825Sdfr				 krbhst_get_default_proto(kd));
68490926Snectar	if(ret)
68555682Smarkm	    return ret;
68690926Snectar	if(get_next(kd, host))
68790926Snectar	    return 0;
68890926Snectar    }
68990926Snectar
690233294Sstas    _krb5_debug(context, 0, "No KDC entries found for %s", kd->realm);
691233294Sstas
69290926Snectar    return KRB5_KDC_UNREACH; /* XXX */
69390926Snectar}
69490926Snectar
69590926Snectarstatic krb5_error_code
69690926Snectaradmin_get_next(krb5_context context,
69790926Snectar	       struct krb5_krbhst_data *kd,
69890926Snectar	       krb5_krbhst_info **host)
69990926Snectar{
70090926Snectar    krb5_error_code ret;
70190926Snectar
702178825Sdfr    if ((kd->flags & KD_PLUGIN) == 0) {
703178825Sdfr	plugin_get_hosts(context, kd, locate_service_kadmin);
704178825Sdfr	kd->flags |= KD_PLUGIN;
705178825Sdfr	if(get_next(kd, host))
706178825Sdfr	    return 0;
707178825Sdfr    }
708178825Sdfr
70990926Snectar    if((kd->flags & KD_CONFIG) == 0) {
71090926Snectar	config_get_hosts(context, kd, "admin_server");
71190926Snectar	kd->flags |= KD_CONFIG;
71290926Snectar	if(get_next(kd, host))
71390926Snectar	    return 0;
71490926Snectar    }
71590926Snectar
716233294Sstas    if (kd->flags & KD_CONFIG_EXISTS) {
717233294Sstas	_krb5_debug(context, 1,
718233294Sstas		    "Configuration exists for realm %s, wont go to DNS",
719233294Sstas		    kd->realm);
720233294Sstas	return KRB5_KDC_UNREACH;
721233294Sstas    }
72290926Snectar
72390926Snectar    if(context->srv_lookup) {
72490926Snectar	if((kd->flags & KD_SRV_TCP) == 0) {
72590926Snectar	    srv_get_hosts(context, kd, "tcp", "kerberos-adm");
72690926Snectar	    kd->flags |= KD_SRV_TCP;
72790926Snectar	    if(get_next(kd, host))
72890926Snectar		return 0;
72955682Smarkm	}
73055682Smarkm    }
73190926Snectar
73290926Snectar    if (krbhst_empty(kd)
73390926Snectar	&& (kd->flags & KD_FALLBACK) == 0) {
73490926Snectar	ret = fallback_get_hosts(context, kd, "kerberos",
735178825Sdfr				 kd->def_port,
736178825Sdfr				 krbhst_get_default_proto(kd));
73790926Snectar	if(ret)
73890926Snectar	    return ret;
73990926Snectar	kd->flags |= KD_FALLBACK;
74090926Snectar	if(get_next(kd, host))
74190926Snectar	    return 0;
74290926Snectar    }
74390926Snectar
744233294Sstas    _krb5_debug(context, 0, "No admin entries found for realm %s", kd->realm);
745233294Sstas
74690926Snectar    return KRB5_KDC_UNREACH;	/* XXX */
74790926Snectar}
74890926Snectar
74990926Snectarstatic krb5_error_code
75090926Snectarkpasswd_get_next(krb5_context context,
75190926Snectar		 struct krb5_krbhst_data *kd,
75290926Snectar		 krb5_krbhst_info **host)
75390926Snectar{
754102644Snectar    krb5_error_code ret;
755102644Snectar
756178825Sdfr    if ((kd->flags & KD_PLUGIN) == 0) {
757178825Sdfr	plugin_get_hosts(context, kd, locate_service_kpasswd);
758178825Sdfr	kd->flags |= KD_PLUGIN;
759178825Sdfr	if(get_next(kd, host))
760178825Sdfr	    return 0;
761178825Sdfr    }
762178825Sdfr
76390926Snectar    if((kd->flags & KD_CONFIG) == 0) {
76490926Snectar	config_get_hosts(context, kd, "kpasswd_server");
765178825Sdfr	kd->flags |= KD_CONFIG;
76690926Snectar	if(get_next(kd, host))
76790926Snectar	    return 0;
76890926Snectar    }
76990926Snectar
770233294Sstas    if (kd->flags & KD_CONFIG_EXISTS) {
771233294Sstas	_krb5_debug(context, 1,
772233294Sstas		    "Configuration exists for realm %s, wont go to DNS",
773233294Sstas		    kd->realm);
774233294Sstas	return KRB5_KDC_UNREACH;
775233294Sstas    }
77690926Snectar
77790926Snectar    if(context->srv_lookup) {
77890926Snectar	if((kd->flags & KD_SRV_UDP) == 0) {
77990926Snectar	    srv_get_hosts(context, kd, "udp", "kpasswd");
78090926Snectar	    kd->flags |= KD_SRV_UDP;
78190926Snectar	    if(get_next(kd, host))
78290926Snectar		return 0;
78390926Snectar	}
784178825Sdfr	if((kd->flags & KD_SRV_TCP) == 0) {
785178825Sdfr	    srv_get_hosts(context, kd, "tcp", "kpasswd");
786178825Sdfr	    kd->flags |= KD_SRV_TCP;
787178825Sdfr	    if(get_next(kd, host))
788178825Sdfr		return 0;
789178825Sdfr	}
79090926Snectar    }
79190926Snectar
79290926Snectar    /* no matches -> try admin */
79390926Snectar
79490926Snectar    if (krbhst_empty(kd)) {
79590926Snectar	kd->flags = 0;
79690926Snectar	kd->port  = kd->def_port;
79790926Snectar	kd->get_next = admin_get_next;
798102644Snectar	ret = (*kd->get_next)(context, kd, host);
799102644Snectar	if (ret == 0)
800178825Sdfr	    (*host)->proto = krbhst_get_default_proto(kd);
801102644Snectar	return ret;
80290926Snectar    }
80390926Snectar
804233294Sstas    _krb5_debug(context, 0, "No kpasswd entries found for realm %s", kd->realm);
805233294Sstas
806233294Sstas    return KRB5_KDC_UNREACH;
80790926Snectar}
80890926Snectar
80990926Snectarstatic krb5_error_code
81090926Snectarkrb524_get_next(krb5_context context,
81190926Snectar		struct krb5_krbhst_data *kd,
81290926Snectar		krb5_krbhst_info **host)
81390926Snectar{
814178825Sdfr    if ((kd->flags & KD_PLUGIN) == 0) {
815178825Sdfr	plugin_get_hosts(context, kd, locate_service_krb524);
816178825Sdfr	kd->flags |= KD_PLUGIN;
817178825Sdfr	if(get_next(kd, host))
818178825Sdfr	    return 0;
819178825Sdfr    }
820178825Sdfr
82190926Snectar    if((kd->flags & KD_CONFIG) == 0) {
82290926Snectar	config_get_hosts(context, kd, "krb524_server");
82390926Snectar	if(get_next(kd, host))
82490926Snectar	    return 0;
82590926Snectar	kd->flags |= KD_CONFIG;
82690926Snectar    }
82790926Snectar
828233294Sstas    if (kd->flags & KD_CONFIG_EXISTS) {
829233294Sstas	_krb5_debug(context, 1,
830233294Sstas		    "Configuration exists for realm %s, wont go to DNS",
831233294Sstas		    kd->realm);
832233294Sstas	return KRB5_KDC_UNREACH;
833233294Sstas    }
83490926Snectar
83590926Snectar    if(context->srv_lookup) {
83690926Snectar	if((kd->flags & KD_SRV_UDP) == 0) {
83790926Snectar	    srv_get_hosts(context, kd, "udp", "krb524");
83890926Snectar	    kd->flags |= KD_SRV_UDP;
83990926Snectar	    if(get_next(kd, host))
84090926Snectar		return 0;
84190926Snectar	}
84290926Snectar
84390926Snectar	if((kd->flags & KD_SRV_TCP) == 0) {
84490926Snectar	    srv_get_hosts(context, kd, "tcp", "krb524");
84590926Snectar	    kd->flags |= KD_SRV_TCP;
84690926Snectar	    if(get_next(kd, host))
84790926Snectar		return 0;
84890926Snectar	}
84990926Snectar    }
85090926Snectar
85190926Snectar    /* no matches -> try kdc */
85290926Snectar
85390926Snectar    if (krbhst_empty(kd)) {
85490926Snectar	kd->flags = 0;
85590926Snectar	kd->port  = kd->def_port;
85690926Snectar	kd->get_next = kdc_get_next;
85790926Snectar	return (*kd->get_next)(context, kd, host);
85890926Snectar    }
85990926Snectar
860233294Sstas    _krb5_debug(context, 0, "No kpasswd entries found for realm %s", kd->realm);
861233294Sstas
862233294Sstas    return KRB5_KDC_UNREACH;
86390926Snectar}
86490926Snectar
86590926Snectarstatic struct krb5_krbhst_data*
86690926Snectarcommon_init(krb5_context context,
867233294Sstas	    const char *service,
868178825Sdfr	    const char *realm,
869178825Sdfr	    int flags)
87090926Snectar{
87190926Snectar    struct krb5_krbhst_data *kd;
87290926Snectar
87390926Snectar    if((kd = calloc(1, sizeof(*kd))) == NULL)
87490926Snectar	return NULL;
87590926Snectar
87690926Snectar    if((kd->realm = strdup(realm)) == NULL) {
87790926Snectar	free(kd);
87890926Snectar	return NULL;
87990926Snectar    }
88090926Snectar
881233294Sstas    _krb5_debug(context, 2, "Trying to find service %s for realm %s flags %x",
882233294Sstas		service, realm, flags);
883233294Sstas
884178825Sdfr    /* For 'realms' without a . do not even think of going to DNS */
885178825Sdfr    if (!strchr(realm, '.'))
886178825Sdfr	kd->flags |= KD_CONFIG_EXISTS;
887178825Sdfr
888178825Sdfr    if (flags & KRB5_KRBHST_FLAGS_LARGE_MSG)
889178825Sdfr	kd->flags |= KD_LARGE_MSG;
89090926Snectar    kd->end = kd->index = &kd->hosts;
89190926Snectar    return kd;
89290926Snectar}
89390926Snectar
89490926Snectar/*
89590926Snectar * initialize `handle' to look for hosts of type `type' in realm `realm'
89690926Snectar */
89790926Snectar
898233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
89990926Snectarkrb5_krbhst_init(krb5_context context,
90090926Snectar		 const char *realm,
90190926Snectar		 unsigned int type,
90290926Snectar		 krb5_krbhst_handle *handle)
90390926Snectar{
904178825Sdfr    return krb5_krbhst_init_flags(context, realm, type, 0, handle);
905178825Sdfr}
906178825Sdfr
907233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
908178825Sdfrkrb5_krbhst_init_flags(krb5_context context,
909178825Sdfr		       const char *realm,
910178825Sdfr		       unsigned int type,
911178825Sdfr		       int flags,
912178825Sdfr		       krb5_krbhst_handle *handle)
913178825Sdfr{
91490926Snectar    struct krb5_krbhst_data *kd;
915233294Sstas    krb5_error_code (*next)(krb5_context, struct krb5_krbhst_data *,
916178825Sdfr			    krb5_krbhst_info **);
91790926Snectar    int def_port;
918233294Sstas    const char *service;
91990926Snectar
92090926Snectar    switch(type) {
92190926Snectar    case KRB5_KRBHST_KDC:
922178825Sdfr	next = kdc_get_next;
92390926Snectar	def_port = ntohs(krb5_getportbyname (context, "kerberos", "udp", 88));
924233294Sstas	service = "kdc";
92590926Snectar	break;
92690926Snectar    case KRB5_KRBHST_ADMIN:
927178825Sdfr	next = admin_get_next;
92890926Snectar	def_port = ntohs(krb5_getportbyname (context, "kerberos-adm",
92990926Snectar					     "tcp", 749));
930233294Sstas	service = "admin";
93190926Snectar	break;
93290926Snectar    case KRB5_KRBHST_CHANGEPW:
933178825Sdfr	next = kpasswd_get_next;
93490926Snectar	def_port = ntohs(krb5_getportbyname (context, "kpasswd", "udp",
93590926Snectar					     KPASSWD_PORT));
936233294Sstas	service = "change_password";
93790926Snectar	break;
93890926Snectar    case KRB5_KRBHST_KRB524:
939178825Sdfr	next = krb524_get_next;
94090926Snectar	def_port = ntohs(krb5_getportbyname (context, "krb524", "udp", 4444));
941233294Sstas	service = "524";
94290926Snectar	break;
94390926Snectar    default:
944233294Sstas	krb5_set_error_message(context, ENOTTY,
945233294Sstas			       N_("unknown krbhst type (%u)", ""), type);
94690926Snectar	return ENOTTY;
94790926Snectar    }
948233294Sstas    if((kd = common_init(context, service, realm, flags)) == NULL)
94990926Snectar	return ENOMEM;
950178825Sdfr    kd->get_next = next;
95190926Snectar    kd->def_port = def_port;
95290926Snectar    *handle = kd;
95355682Smarkm    return 0;
95455682Smarkm}
95555682Smarkm
95672445Sassar/*
95790926Snectar * return the next host information from `handle' in `host'
95872445Sassar */
95972445Sassar
960233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
96190926Snectarkrb5_krbhst_next(krb5_context context,
96290926Snectar		 krb5_krbhst_handle handle,
96390926Snectar		 krb5_krbhst_info **host)
96490926Snectar{
96590926Snectar    if(get_next(handle, host))
96690926Snectar	return 0;
96790926Snectar
96890926Snectar    return (*handle->get_next)(context, handle, host);
96990926Snectar}
97090926Snectar
97190926Snectar/*
97290926Snectar * return the next host information from `handle' as a host name
97390926Snectar * in `hostname' (or length `hostlen)
97490926Snectar */
97590926Snectar
976233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
97790926Snectarkrb5_krbhst_next_as_string(krb5_context context,
97890926Snectar			   krb5_krbhst_handle handle,
97990926Snectar			   char *hostname,
98090926Snectar			   size_t hostlen)
98190926Snectar{
98290926Snectar    krb5_error_code ret;
98390926Snectar    krb5_krbhst_info *host;
98490926Snectar    ret = krb5_krbhst_next(context, handle, &host);
98590926Snectar    if(ret)
98690926Snectar	return ret;
98790926Snectar    return krb5_krbhst_format_string(context, host, hostname, hostlen);
98890926Snectar}
98990926Snectar
99090926Snectar
991233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL
99290926Snectarkrb5_krbhst_reset(krb5_context context, krb5_krbhst_handle handle)
99390926Snectar{
99490926Snectar    handle->index = &handle->hosts;
99590926Snectar}
99690926Snectar
997233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL
99890926Snectarkrb5_krbhst_free(krb5_context context, krb5_krbhst_handle handle)
99990926Snectar{
100090926Snectar    krb5_krbhst_info *h, *next;
100190926Snectar
100290926Snectar    if (handle == NULL)
100390926Snectar	return;
100490926Snectar
100590926Snectar    for (h = handle->hosts; h != NULL; h = next) {
100690926Snectar	next = h->next;
1007178825Sdfr	_krb5_free_krbhst_info(h);
100890926Snectar    }
100990926Snectar
101090926Snectar    free(handle->realm);
101190926Snectar    free(handle);
101290926Snectar}
101390926Snectar
101490926Snectar/* backwards compatibility ahead */
101590926Snectar
101690926Snectarstatic krb5_error_code
1017233294Sstasgethostlist(krb5_context context, const char *realm,
101890926Snectar	    unsigned int type, char ***hostlist)
101990926Snectar{
102090926Snectar    krb5_error_code ret;
102190926Snectar    int nhost = 0;
102290926Snectar    krb5_krbhst_handle handle;
102390926Snectar    char host[MAXHOSTNAMELEN];
102490926Snectar    krb5_krbhst_info *hostinfo;
102590926Snectar
102690926Snectar    ret = krb5_krbhst_init(context, realm, type, &handle);
102790926Snectar    if (ret)
102890926Snectar	return ret;
102990926Snectar
103090926Snectar    while(krb5_krbhst_next(context, handle, &hostinfo) == 0)
103190926Snectar	nhost++;
1032178825Sdfr    if(nhost == 0) {
1033233294Sstas	krb5_set_error_message(context, KRB5_KDC_UNREACH,
1034233294Sstas			       N_("No KDC found for realm %s", ""), realm);
103590926Snectar	return KRB5_KDC_UNREACH;
1036178825Sdfr    }
103790926Snectar    *hostlist = calloc(nhost + 1, sizeof(**hostlist));
103890926Snectar    if(*hostlist == NULL) {
103990926Snectar	krb5_krbhst_free(context, handle);
104090926Snectar	return ENOMEM;
104190926Snectar    }
104290926Snectar
104390926Snectar    krb5_krbhst_reset(context, handle);
104490926Snectar    nhost = 0;
1045233294Sstas    while(krb5_krbhst_next_as_string(context, handle,
104690926Snectar				     host, sizeof(host)) == 0) {
104790926Snectar	if(((*hostlist)[nhost++] = strdup(host)) == NULL) {
104890926Snectar	    krb5_free_krbhst(context, *hostlist);
104990926Snectar	    krb5_krbhst_free(context, handle);
105090926Snectar	    return ENOMEM;
105190926Snectar	}
105290926Snectar    }
1053233294Sstas    (*hostlist)[nhost] = NULL;
105490926Snectar    krb5_krbhst_free(context, handle);
105590926Snectar    return 0;
105690926Snectar}
105790926Snectar
105890926Snectar/*
105990926Snectar * return an malloced list of kadmin-hosts for `realm' in `hostlist'
106090926Snectar */
106190926Snectar
1062233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
106355682Smarkmkrb5_get_krb_admin_hst (krb5_context context,
106455682Smarkm			const krb5_realm *realm,
106555682Smarkm			char ***hostlist)
106655682Smarkm{
106790926Snectar    return gethostlist(context, *realm, KRB5_KRBHST_ADMIN, hostlist);
106855682Smarkm}
106955682Smarkm
107072445Sassar/*
107190926Snectar * return an malloced list of changepw-hosts for `realm' in `hostlist'
107272445Sassar */
107372445Sassar
1074233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
107555682Smarkmkrb5_get_krb_changepw_hst (krb5_context context,
107655682Smarkm			   const krb5_realm *realm,
107755682Smarkm			   char ***hostlist)
107855682Smarkm{
107990926Snectar    return gethostlist(context, *realm, KRB5_KRBHST_CHANGEPW, hostlist);
108090926Snectar}
108172445Sassar
108290926Snectar/*
108390926Snectar * return an malloced list of 524-hosts for `realm' in `hostlist'
108490926Snectar */
108590926Snectar
1086233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
108790926Snectarkrb5_get_krb524hst (krb5_context context,
108890926Snectar		    const krb5_realm *realm,
108990926Snectar		    char ***hostlist)
109090926Snectar{
109190926Snectar    return gethostlist(context, *realm, KRB5_KRBHST_KRB524, hostlist);
109255682Smarkm}
109355682Smarkm
109490926Snectar
109572445Sassar/*
109690926Snectar * return an malloced list of KDC's for `realm' in `hostlist'
109772445Sassar */
109872445Sassar
1099233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
110055682Smarkmkrb5_get_krbhst (krb5_context context,
110155682Smarkm		 const krb5_realm *realm,
110255682Smarkm		 char ***hostlist)
110355682Smarkm{
110490926Snectar    return gethostlist(context, *realm, KRB5_KRBHST_KDC, hostlist);
110555682Smarkm}
110655682Smarkm
110772445Sassar/*
110890926Snectar * free all the memory allocated in `hostlist'
110972445Sassar */
111072445Sassar
1111233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
111255682Smarkmkrb5_free_krbhst (krb5_context context,
111355682Smarkm		  char **hostlist)
111455682Smarkm{
111555682Smarkm    char **p;
111655682Smarkm
111755682Smarkm    for (p = hostlist; *p; ++p)
111855682Smarkm	free (*p);
111955682Smarkm    free (hostlist);
112055682Smarkm    return 0;
112155682Smarkm}
1122