root/source4/heimdal/lib/krb5/krbhst.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. string_to_proto
  2. srv_find_realm
  3. krbhst_empty
  4. krbhst_get_default_proto
  5. parse_hostspec
  6. _krb5_free_krbhst_info
  7. _krb5_krbhost_info_move
  8. append_host_hostinfo
  9. append_host_string
  10. krb5_krbhst_format_string
  11. make_hints
  12. krb5_krbhst_get_addrinfo
  13. get_next
  14. srv_get_hosts
  15. config_get_hosts
  16. fallback_get_hosts
  17. add_locate
  18. plugin_get_hosts
  19. kdc_get_next
  20. admin_get_next
  21. kpasswd_get_next
  22. krb524_get_next
  23. common_init
  24. krb5_krbhst_init
  25. krb5_krbhst_init_flags
  26. krb5_krbhst_next
  27. krb5_krbhst_next_as_string
  28. krb5_krbhst_reset
  29. krb5_krbhst_free
  30. gethostlist
  31. krb5_get_krb_admin_hst
  32. krb5_get_krb_changepw_hst
  33. krb5_get_krb524hst
  34. krb5_get_krbhst
  35. krb5_free_krbhst

   1 /*
   2  * Copyright (c) 2001 - 2003 Kungliga Tekniska Högskolan
   3  * (Royal Institute of Technology, Stockholm, Sweden).
   4  * All rights reserved.
   5  *
   6  * Redistribution and use in source and binary forms, with or without
   7  * modification, are permitted provided that the following conditions
   8  * are met:
   9  *
  10  * 1. Redistributions of source code must retain the above copyright
  11  *    notice, this list of conditions and the following disclaimer.
  12  *
  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.
  16  *
  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.
  20  *
  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.
  32  */
  33 
  34 #include "krb5_locl.h"
  35 #include <resolve.h>
  36 #include "locate_plugin.h"
  37 
  38 RCSID("$Id$");
  39 
  40 static int
  41 string_to_proto(const char *string)
     /* [<][>][^][v][top][bottom][index][help] */
  42 {
  43     if(strcasecmp(string, "udp") == 0)
  44         return KRB5_KRBHST_UDP;
  45     else if(strcasecmp(string, "tcp") == 0)
  46         return KRB5_KRBHST_TCP;
  47     else if(strcasecmp(string, "http") == 0)
  48         return KRB5_KRBHST_HTTP;
  49     return -1;
  50 }
  51 
  52 /*
  53  * set `res' and `count' to the result of looking up SRV RR in DNS for
  54  * `proto', `proto', `realm' using `dns_type'.
  55  * if `port' != 0, force that port number
  56  */
  57 
  58 static krb5_error_code
  59 srv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count,
     /* [<][>][^][v][top][bottom][index][help] */
  60                const char *realm, const char *dns_type,
  61                const char *proto, const char *service, int port)
  62 {
  63     char domain[1024];
  64     struct dns_reply *r;
  65     struct resource_record *rr;
  66     int num_srv;
  67     int proto_num;
  68     int def_port;
  69 
  70     *res = NULL;
  71     *count = 0;
  72 
  73     proto_num = string_to_proto(proto);
  74     if(proto_num < 0) {
  75         krb5_set_error_message(context, EINVAL,
  76                                N_("unknown protocol `%s' to lookup", ""),
  77                                proto);
  78         return EINVAL;
  79     }
  80 
  81     if(proto_num == KRB5_KRBHST_HTTP)
  82         def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80));
  83     else if(port == 0)
  84         def_port = ntohs(krb5_getportbyname (context, service, proto, 88));
  85     else
  86         def_port = port;
  87 
  88     snprintf(domain, sizeof(domain), "_%s._%s.%s.", service, proto, realm);
  89 
  90     r = dns_lookup(domain, dns_type);
  91     if(r == NULL)
  92         return KRB5_KDC_UNREACH;
  93 
  94     for(num_srv = 0, rr = r->head; rr; rr = rr->next)
  95         if(rr->type == T_SRV)
  96             num_srv++;
  97 
  98     *res = malloc(num_srv * sizeof(**res));
  99     if(*res == NULL) {
 100         dns_free_data(r);
 101         krb5_set_error_message(context, ENOMEM,
 102                                N_("malloc: out of memory", ""));
 103         return ENOMEM;
 104     }
 105 
 106     dns_srv_order(r);
 107 
 108     for(num_srv = 0, rr = r->head; rr; rr = rr->next)
 109         if(rr->type == T_SRV) {
 110             krb5_krbhst_info *hi;
 111             size_t len = strlen(rr->u.srv->target);
 112 
 113             hi = calloc(1, sizeof(*hi) + len);
 114             if(hi == NULL) {
 115                 dns_free_data(r);
 116                 while(--num_srv >= 0)
 117                     free((*res)[num_srv]);
 118                 free(*res);
 119                 *res = NULL;
 120                 return ENOMEM;
 121             }
 122             (*res)[num_srv++] = hi;
 123 
 124             hi->proto = proto_num;
 125         
 126             hi->def_port = def_port;
 127             if (port != 0)
 128                 hi->port = port;
 129             else
 130                 hi->port = rr->u.srv->port;
 131 
 132             strlcpy(hi->hostname, rr->u.srv->target, len + 1);
 133         }
 134 
 135     *count = num_srv;
 136         
 137     dns_free_data(r);
 138     return 0;
 139 }
 140 
 141 
 142 struct krb5_krbhst_data {
 143     char *realm;
 144     unsigned int flags;
 145     int def_port;
 146     int port;                   /* hardwired port number if != 0 */
 147 #define KD_CONFIG                1
 148 #define KD_SRV_UDP               2
 149 #define KD_SRV_TCP               4
 150 #define KD_SRV_HTTP              8
 151 #define KD_FALLBACK             16
 152 #define KD_CONFIG_EXISTS        32
 153 #define KD_LARGE_MSG            64
 154 #define KD_PLUGIN              128
 155     krb5_error_code (*get_next)(krb5_context, struct krb5_krbhst_data *,
 156                                 krb5_krbhst_info**);
 157 
 158     unsigned int fallback_count;
 159 
 160     struct krb5_krbhst_info *hosts, **index, **end;
 161 };
 162 
 163 static krb5_boolean
 164 krbhst_empty(const struct krb5_krbhst_data *kd)
     /* [<][>][^][v][top][bottom][index][help] */
 165 {
 166     return kd->index == &kd->hosts;
 167 }
 168 
 169 /*
 170  * Return the default protocol for the `kd' (either TCP or UDP)
 171  */
 172 
 173 static int
 174 krbhst_get_default_proto(struct krb5_krbhst_data *kd)
     /* [<][>][^][v][top][bottom][index][help] */
 175 {
 176     if (kd->flags & KD_LARGE_MSG)
 177         return KRB5_KRBHST_TCP;
 178     return KRB5_KRBHST_UDP;
 179 }
 180 
 181 
 182 /*
 183  * parse `spec' into a krb5_krbhst_info, defaulting the port to `def_port'
 184  * and forcing it to `port' if port != 0
 185  */
 186 
 187 static struct krb5_krbhst_info*
 188 parse_hostspec(krb5_context context, struct krb5_krbhst_data *kd,
     /* [<][>][^][v][top][bottom][index][help] */
 189                const char *spec, int def_port, int port)
 190 {
 191     const char *p = spec;
 192     struct krb5_krbhst_info *hi;
 193 
 194     hi = calloc(1, sizeof(*hi) + strlen(spec));
 195     if(hi == NULL)
 196         return NULL;
 197 
 198     hi->proto = krbhst_get_default_proto(kd);
 199 
 200     if(strncmp(p, "http://", 7) == 0){
 201         hi->proto = KRB5_KRBHST_HTTP;
 202         p += 7;
 203     } else if(strncmp(p, "http/", 5) == 0) {
 204         hi->proto = KRB5_KRBHST_HTTP;
 205         p += 5;
 206         def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80));
 207     }else if(strncmp(p, "tcp/", 4) == 0){
 208         hi->proto = KRB5_KRBHST_TCP;
 209         p += 4;
 210     } else if(strncmp(p, "udp/", 4) == 0) {
 211         p += 4;
 212     }
 213 
 214     if(strsep_copy(&p, ":", hi->hostname, strlen(spec) + 1) < 0) {
 215         free(hi);
 216         return NULL;
 217     }
 218     /* get rid of trailing /, and convert to lower case */
 219     hi->hostname[strcspn(hi->hostname, "/")] = '\0';
 220     strlwr(hi->hostname);
 221 
 222     hi->port = hi->def_port = def_port;
 223     if(p != NULL) {
 224         char *end;
 225         hi->port = strtol(p, &end, 0);
 226         if(end == p) {
 227             free(hi);
 228             return NULL;
 229         }
 230     }
 231     if (port)
 232         hi->port = port;
 233     return hi;
 234 }
 235 
 236 void
 237 _krb5_free_krbhst_info(krb5_krbhst_info *hi)
     /* [<][>][^][v][top][bottom][index][help] */
 238 {
 239     if (hi->ai != NULL)
 240         freeaddrinfo(hi->ai);
 241     free(hi);
 242 }
 243 
 244 krb5_error_code
 245 _krb5_krbhost_info_move(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 246                         krb5_krbhst_info *from,
 247                         krb5_krbhst_info **to)
 248 {
 249     size_t hostnamelen = strlen(from->hostname);
 250     /* trailing NUL is included in structure */
 251     *to = calloc(1, sizeof(**to) + hostnamelen);
 252     if(*to == NULL) {
 253         krb5_set_error_message(context, ENOMEM,
 254                                N_("malloc: out of memory", ""));
 255         return ENOMEM;
 256     }
 257 
 258     (*to)->proto = from->proto;
 259     (*to)->port = from->port;
 260     (*to)->def_port = from->def_port;
 261     (*to)->ai = from->ai;
 262     from->ai = NULL;
 263     (*to)->next = NULL;
 264     memcpy((*to)->hostname, from->hostname, hostnamelen + 1);
 265     return 0;
 266 }
 267 
 268 
 269 static void
 270 append_host_hostinfo(struct krb5_krbhst_data *kd, struct krb5_krbhst_info *host)
     /* [<][>][^][v][top][bottom][index][help] */
 271 {
 272     struct krb5_krbhst_info *h;
 273 
 274     for(h = kd->hosts; h; h = h->next)
 275         if(h->proto == host->proto &&
 276            h->port == host->port &&
 277            strcmp(h->hostname, host->hostname) == 0) {
 278             _krb5_free_krbhst_info(host);
 279             return;
 280         }
 281     *kd->end = host;
 282     kd->end = &host->next;
 283 }
 284 
 285 static krb5_error_code
 286 append_host_string(krb5_context context, struct krb5_krbhst_data *kd,
     /* [<][>][^][v][top][bottom][index][help] */
 287                    const char *host, int def_port, int port)
 288 {
 289     struct krb5_krbhst_info *hi;
 290 
 291     hi = parse_hostspec(context, kd, host, def_port, port);
 292     if(hi == NULL)
 293         return ENOMEM;
 294 
 295     append_host_hostinfo(kd, hi);
 296     return 0;
 297 }
 298 
 299 /*
 300  * return a readable representation of `host' in `hostname, hostlen'
 301  */
 302 
 303 krb5_error_code KRB5_LIB_FUNCTION
 304 krb5_krbhst_format_string(krb5_context context, const krb5_krbhst_info *host,
     /* [<][>][^][v][top][bottom][index][help] */
 305                           char *hostname, size_t hostlen)
 306 {
 307     const char *proto = "";
 308     char portstr[7] = "";
 309     if(host->proto == KRB5_KRBHST_TCP)
 310         proto = "tcp/";
 311     else if(host->proto == KRB5_KRBHST_HTTP)
 312         proto = "http://";
 313     if(host->port != host->def_port)
 314         snprintf(portstr, sizeof(portstr), ":%d", host->port);
 315     snprintf(hostname, hostlen, "%s%s%s", proto, host->hostname, portstr);
 316     return 0;
 317 }
 318 
 319 /*
 320  * create a getaddrinfo `hints' based on `proto'
 321  */
 322 
 323 static void
 324 make_hints(struct addrinfo *hints, int proto)
     /* [<][>][^][v][top][bottom][index][help] */
 325 {
 326     memset(hints, 0, sizeof(*hints));
 327     hints->ai_family = AF_UNSPEC;
 328     switch(proto) {
 329     case KRB5_KRBHST_UDP :
 330         hints->ai_socktype = SOCK_DGRAM;
 331         break;
 332     case KRB5_KRBHST_HTTP :
 333     case KRB5_KRBHST_TCP :
 334         hints->ai_socktype = SOCK_STREAM;
 335         break;
 336     }
 337 }
 338 
 339 /*
 340  * return an `struct addrinfo *' in `ai' corresponding to the information
 341  * in `host'.  free:ing is handled by krb5_krbhst_free.
 342  */
 343 
 344 krb5_error_code KRB5_LIB_FUNCTION
 345 krb5_krbhst_get_addrinfo(krb5_context context, krb5_krbhst_info *host,
     /* [<][>][^][v][top][bottom][index][help] */
 346                          struct addrinfo **ai)
 347 {
 348     struct addrinfo hints;
 349     char portstr[NI_MAXSERV];
 350     int ret;
 351 
 352     if (host->ai == NULL) {
 353         make_hints(&hints, host->proto);
 354         snprintf (portstr, sizeof(portstr), "%d", host->port);
 355         ret = getaddrinfo(host->hostname, portstr, &hints, &host->ai);
 356         if (ret)
 357             return krb5_eai_to_heim_errno(ret, errno);
 358     }
 359     *ai = host->ai;
 360     return 0;
 361 }
 362 
 363 static krb5_boolean
 364 get_next(struct krb5_krbhst_data *kd, krb5_krbhst_info **host)
     /* [<][>][^][v][top][bottom][index][help] */
 365 {
 366     struct krb5_krbhst_info *hi = *kd->index;
 367     if(hi != NULL) {
 368         *host = hi;
 369         kd->index = &(*kd->index)->next;
 370         return TRUE;
 371     }
 372     return FALSE;
 373 }
 374 
 375 static void
 376 srv_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
     /* [<][>][^][v][top][bottom][index][help] */
 377               const char *proto, const char *service)
 378 {
 379     krb5_krbhst_info **res;
 380     int count, i;
 381 
 382     if (srv_find_realm(context, &res, &count, kd->realm, "SRV", proto, service,
 383                        kd->port))
 384         return;
 385     for(i = 0; i < count; i++)
 386         append_host_hostinfo(kd, res[i]);
 387     free(res);
 388 }
 389 
 390 /*
 391  * read the configuration for `conf_string', defaulting to kd->def_port and
 392  * forcing it to `kd->port' if kd->port != 0
 393  */
 394 
 395 static void
 396 config_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
     /* [<][>][^][v][top][bottom][index][help] */
 397                  const char *conf_string)
 398 {
 399     int i;
 400         
 401     char **hostlist;
 402     hostlist = krb5_config_get_strings(context, NULL,
 403                                        "realms", kd->realm, conf_string, NULL);
 404 
 405     if(hostlist == NULL)
 406         return;
 407     kd->flags |= KD_CONFIG_EXISTS;
 408     for(i = 0; hostlist && hostlist[i] != NULL; i++)
 409         append_host_string(context, kd, hostlist[i], kd->def_port, kd->port);
 410 
 411     krb5_config_free_strings(hostlist);
 412 }
 413 
 414 /*
 415  * as a fallback, look for `serv_string.kd->realm' (typically
 416  * kerberos.REALM, kerberos-1.REALM, ...
 417  * `port' is the default port for the service, and `proto' the
 418  * protocol
 419  */
 420 
 421 static krb5_error_code
 422 fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
     /* [<][>][^][v][top][bottom][index][help] */
 423                    const char *serv_string, int port, int proto)
 424 {
 425     char *host;
 426     int ret;
 427     struct addrinfo *ai;
 428     struct addrinfo hints;
 429     char portstr[NI_MAXSERV];
 430 
 431     /*
 432      * Don't try forever in case the DNS server keep returning us
 433      * entries (like wildcard entries or the .nu TLD)
 434      */
 435     if(kd->fallback_count >= 5) {
 436         kd->flags |= KD_FALLBACK;
 437         return 0;
 438     }
 439 
 440     if(kd->fallback_count == 0)
 441         asprintf(&host, "%s.%s.", serv_string, kd->realm);
 442     else
 443         asprintf(&host, "%s-%d.%s.",
 444                  serv_string, kd->fallback_count, kd->realm);   
 445 
 446     if (host == NULL)
 447         return ENOMEM;
 448 
 449     make_hints(&hints, proto);
 450     snprintf(portstr, sizeof(portstr), "%d", port);
 451     ret = getaddrinfo(host, portstr, &hints, &ai);
 452     if (ret) {
 453         /* no more hosts, so we're done here */
 454         free(host);
 455         kd->flags |= KD_FALLBACK;
 456     } else {
 457         struct krb5_krbhst_info *hi;
 458         size_t hostlen = strlen(host);
 459 
 460         hi = calloc(1, sizeof(*hi) + hostlen);
 461         if(hi == NULL) {
 462             free(host);
 463             return ENOMEM;
 464         }
 465 
 466         hi->proto = proto;
 467         hi->port  = hi->def_port = port;
 468         hi->ai    = ai;
 469         memmove(hi->hostname, host, hostlen);
 470         hi->hostname[hostlen] = '\0';
 471         free(host);
 472         append_host_hostinfo(kd, hi);
 473         kd->fallback_count++;
 474     }
 475     return 0;
 476 }
 477 
 478 /*
 479  * Fetch hosts from plugin
 480  */
 481 
 482 static krb5_error_code
 483 add_locate(void *ctx, int type, struct sockaddr *addr)
     /* [<][>][^][v][top][bottom][index][help] */
 484 {
 485     struct krb5_krbhst_info *hi;
 486     struct krb5_krbhst_data *kd = ctx;
 487     char host[NI_MAXHOST], port[NI_MAXSERV];
 488     struct addrinfo hints, *ai;
 489     socklen_t socklen;
 490     size_t hostlen;
 491     int ret;
 492 
 493     socklen = socket_sockaddr_size(addr);
 494 
 495     ret = getnameinfo(addr, socklen, host, sizeof(host), port, sizeof(port),
 496                       NI_NUMERICHOST|NI_NUMERICSERV);
 497     if (ret != 0)
 498         return 0;
 499 
 500     make_hints(&hints, krbhst_get_default_proto(kd));
 501     ret = getaddrinfo(host, port, &hints, &ai);
 502     if (ret)
 503         return 0;
 504 
 505     hostlen = strlen(host);
 506 
 507     hi = calloc(1, sizeof(*hi) + hostlen);
 508     if(hi == NULL)
 509         return ENOMEM;
 510 
 511     hi->proto = krbhst_get_default_proto(kd);
 512     hi->port  = hi->def_port = socket_get_port(addr);
 513     hi->ai    = ai;
 514     memmove(hi->hostname, host, hostlen);
 515     hi->hostname[hostlen] = '\0';
 516     append_host_hostinfo(kd, hi);
 517 
 518     return 0;
 519 }
 520 
 521 static void
 522 plugin_get_hosts(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 523                  struct krb5_krbhst_data *kd,
 524                  enum locate_service_type type)
 525 {
 526     struct krb5_plugin *list = NULL, *e;
 527     krb5_error_code ret;
 528 
 529     ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA,
 530                             KRB5_PLUGIN_LOCATE, &list);
 531     if(ret != 0 || list == NULL)
 532         return;
 533 
 534     for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
 535         krb5plugin_service_locate_ftable *service;
 536         void *ctx;
 537 
 538         service = _krb5_plugin_get_symbol(e);
 539         if (service->minor_version != 0)
 540             continue;
 541         
 542         (*service->init)(context, &ctx);
 543         ret = (*service->lookup)(ctx, type, kd->realm, 0, 0, add_locate, kd);
 544         (*service->fini)(ctx);
 545         if (ret && ret != KRB5_PLUGIN_NO_HANDLE) {
 546             krb5_set_error_message(context, ret,
 547                                    N_("Locate plugin failed to lookup realm %s: %d", ""),
 548                                    kd->realm, ret);
 549             break;
 550         } else if (ret == 0)
 551             kd->flags |= KD_CONFIG_EXISTS;
 552 
 553     }
 554     _krb5_plugin_free(list);
 555 }
 556 
 557 /*
 558  *
 559  */
 560 
 561 static krb5_error_code
 562 kdc_get_next(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 563              struct krb5_krbhst_data *kd,
 564              krb5_krbhst_info **host)
 565 {
 566     krb5_error_code ret;
 567 
 568     if ((kd->flags & KD_PLUGIN) == 0) {
 569         plugin_get_hosts(context, kd, locate_service_kdc);
 570         kd->flags |= KD_PLUGIN;
 571         if(get_next(kd, host))
 572             return 0;
 573     }
 574 
 575     if((kd->flags & KD_CONFIG) == 0) {
 576         config_get_hosts(context, kd, "kdc");
 577         kd->flags |= KD_CONFIG;
 578         if(get_next(kd, host))
 579             return 0;
 580     }
 581 
 582     if (kd->flags & KD_CONFIG_EXISTS)
 583         return KRB5_KDC_UNREACH; /* XXX */
 584 
 585     if(context->srv_lookup) {
 586         if((kd->flags & KD_SRV_UDP) == 0 && (kd->flags & KD_LARGE_MSG) == 0) {
 587             srv_get_hosts(context, kd, "udp", "kerberos");
 588             kd->flags |= KD_SRV_UDP;
 589             if(get_next(kd, host))
 590                 return 0;
 591         }
 592 
 593         if((kd->flags & KD_SRV_TCP) == 0) {
 594             srv_get_hosts(context, kd, "tcp", "kerberos");
 595             kd->flags |= KD_SRV_TCP;
 596             if(get_next(kd, host))
 597                 return 0;
 598         }
 599         if((kd->flags & KD_SRV_HTTP) == 0) {
 600             srv_get_hosts(context, kd, "http", "kerberos");
 601             kd->flags |= KD_SRV_HTTP;
 602             if(get_next(kd, host))
 603                 return 0;
 604         }
 605     }
 606 
 607     while((kd->flags & KD_FALLBACK) == 0) {
 608         ret = fallback_get_hosts(context, kd, "kerberos",
 609                                  kd->def_port,
 610                                  krbhst_get_default_proto(kd));
 611         if(ret)
 612             return ret;
 613         if(get_next(kd, host))
 614             return 0;
 615     }
 616 
 617     return KRB5_KDC_UNREACH; /* XXX */
 618 }
 619 
 620 static krb5_error_code
 621 admin_get_next(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 622                struct krb5_krbhst_data *kd,
 623                krb5_krbhst_info **host)
 624 {
 625     krb5_error_code ret;
 626 
 627     if ((kd->flags & KD_PLUGIN) == 0) {
 628         plugin_get_hosts(context, kd, locate_service_kadmin);
 629         kd->flags |= KD_PLUGIN;
 630         if(get_next(kd, host))
 631             return 0;
 632     }
 633 
 634     if((kd->flags & KD_CONFIG) == 0) {
 635         config_get_hosts(context, kd, "admin_server");
 636         kd->flags |= KD_CONFIG;
 637         if(get_next(kd, host))
 638             return 0;
 639     }
 640 
 641     if (kd->flags & KD_CONFIG_EXISTS)
 642         return KRB5_KDC_UNREACH; /* XXX */
 643 
 644     if(context->srv_lookup) {
 645         if((kd->flags & KD_SRV_TCP) == 0) {
 646             srv_get_hosts(context, kd, "tcp", "kerberos-adm");
 647             kd->flags |= KD_SRV_TCP;
 648             if(get_next(kd, host))
 649                 return 0;
 650         }
 651     }
 652 
 653     if (krbhst_empty(kd)
 654         && (kd->flags & KD_FALLBACK) == 0) {
 655         ret = fallback_get_hosts(context, kd, "kerberos",
 656                                  kd->def_port,
 657                                  krbhst_get_default_proto(kd));
 658         if(ret)
 659             return ret;
 660         kd->flags |= KD_FALLBACK;
 661         if(get_next(kd, host))
 662             return 0;
 663     }
 664 
 665     return KRB5_KDC_UNREACH;    /* XXX */
 666 }
 667 
 668 static krb5_error_code
 669 kpasswd_get_next(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 670                  struct krb5_krbhst_data *kd,
 671                  krb5_krbhst_info **host)
 672 {
 673     krb5_error_code ret;
 674 
 675     if ((kd->flags & KD_PLUGIN) == 0) {
 676         plugin_get_hosts(context, kd, locate_service_kpasswd);
 677         kd->flags |= KD_PLUGIN;
 678         if(get_next(kd, host))
 679             return 0;
 680     }
 681 
 682     if((kd->flags & KD_CONFIG) == 0) {
 683         config_get_hosts(context, kd, "kpasswd_server");
 684         kd->flags |= KD_CONFIG;
 685         if(get_next(kd, host))
 686             return 0;
 687     }
 688 
 689     if (kd->flags & KD_CONFIG_EXISTS)
 690         return KRB5_KDC_UNREACH; /* XXX */
 691 
 692     if(context->srv_lookup) {
 693         if((kd->flags & KD_SRV_UDP) == 0) {
 694             srv_get_hosts(context, kd, "udp", "kpasswd");
 695             kd->flags |= KD_SRV_UDP;
 696             if(get_next(kd, host))
 697                 return 0;
 698         }
 699         if((kd->flags & KD_SRV_TCP) == 0) {
 700             srv_get_hosts(context, kd, "tcp", "kpasswd");
 701             kd->flags |= KD_SRV_TCP;
 702             if(get_next(kd, host))
 703                 return 0;
 704         }
 705     }
 706 
 707     /* no matches -> try admin */
 708 
 709     if (krbhst_empty(kd)) {
 710         kd->flags = 0;
 711         kd->port  = kd->def_port;
 712         kd->get_next = admin_get_next;
 713         ret = (*kd->get_next)(context, kd, host);
 714         if (ret == 0)
 715             (*host)->proto = krbhst_get_default_proto(kd);
 716         return ret;
 717     }
 718 
 719     return KRB5_KDC_UNREACH; /* XXX */
 720 }
 721 
 722 static krb5_error_code
 723 krb524_get_next(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 724                 struct krb5_krbhst_data *kd,
 725                 krb5_krbhst_info **host)
 726 {
 727     if ((kd->flags & KD_PLUGIN) == 0) {
 728         plugin_get_hosts(context, kd, locate_service_krb524);
 729         kd->flags |= KD_PLUGIN;
 730         if(get_next(kd, host))
 731             return 0;
 732     }
 733 
 734     if((kd->flags & KD_CONFIG) == 0) {
 735         config_get_hosts(context, kd, "krb524_server");
 736         if(get_next(kd, host))
 737             return 0;
 738         kd->flags |= KD_CONFIG;
 739     }
 740 
 741     if (kd->flags & KD_CONFIG_EXISTS)
 742         return KRB5_KDC_UNREACH; /* XXX */
 743 
 744     if(context->srv_lookup) {
 745         if((kd->flags & KD_SRV_UDP) == 0) {
 746             srv_get_hosts(context, kd, "udp", "krb524");
 747             kd->flags |= KD_SRV_UDP;
 748             if(get_next(kd, host))
 749                 return 0;
 750         }
 751 
 752         if((kd->flags & KD_SRV_TCP) == 0) {
 753             srv_get_hosts(context, kd, "tcp", "krb524");
 754             kd->flags |= KD_SRV_TCP;
 755             if(get_next(kd, host))
 756                 return 0;
 757         }
 758     }
 759 
 760     /* no matches -> try kdc */
 761 
 762     if (krbhst_empty(kd)) {
 763         kd->flags = 0;
 764         kd->port  = kd->def_port;
 765         kd->get_next = kdc_get_next;
 766         return (*kd->get_next)(context, kd, host);
 767     }
 768 
 769     return KRB5_KDC_UNREACH; /* XXX */
 770 }
 771 
 772 static struct krb5_krbhst_data*
 773 common_init(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 774             const char *realm,
 775             int flags)
 776 {
 777     struct krb5_krbhst_data *kd;
 778 
 779     if((kd = calloc(1, sizeof(*kd))) == NULL)
 780         return NULL;
 781 
 782     if((kd->realm = strdup(realm)) == NULL) {
 783         free(kd);
 784         return NULL;
 785     }
 786 
 787     /* For 'realms' without a . do not even think of going to DNS */
 788     if (!strchr(realm, '.'))
 789         kd->flags |= KD_CONFIG_EXISTS;
 790 
 791     if (flags & KRB5_KRBHST_FLAGS_LARGE_MSG)
 792         kd->flags |= KD_LARGE_MSG;
 793     kd->end = kd->index = &kd->hosts;
 794     return kd;
 795 }
 796 
 797 /*
 798  * initialize `handle' to look for hosts of type `type' in realm `realm'
 799  */
 800 
 801 krb5_error_code KRB5_LIB_FUNCTION
 802 krb5_krbhst_init(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 803                  const char *realm,
 804                  unsigned int type,
 805                  krb5_krbhst_handle *handle)
 806 {
 807     return krb5_krbhst_init_flags(context, realm, type, 0, handle);
 808 }
 809 
 810 krb5_error_code KRB5_LIB_FUNCTION
 811 krb5_krbhst_init_flags(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 812                        const char *realm,
 813                        unsigned int type,
 814                        int flags,
 815                        krb5_krbhst_handle *handle)
 816 {
 817     struct krb5_krbhst_data *kd;
 818     krb5_error_code (*next)(krb5_context, struct krb5_krbhst_data *,
 819                             krb5_krbhst_info **);
 820     int def_port;
 821 
 822     switch(type) {
 823     case KRB5_KRBHST_KDC:
 824         next = kdc_get_next;
 825         def_port = ntohs(krb5_getportbyname (context, "kerberos", "udp", 88));
 826         break;
 827     case KRB5_KRBHST_ADMIN:
 828         next = admin_get_next;
 829         def_port = ntohs(krb5_getportbyname (context, "kerberos-adm",
 830                                              "tcp", 749));
 831         break;
 832     case KRB5_KRBHST_CHANGEPW:
 833         next = kpasswd_get_next;
 834         def_port = ntohs(krb5_getportbyname (context, "kpasswd", "udp",
 835                                              KPASSWD_PORT));
 836         break;
 837     case KRB5_KRBHST_KRB524:
 838         next = krb524_get_next;
 839         def_port = ntohs(krb5_getportbyname (context, "krb524", "udp", 4444));
 840         break;
 841     default:
 842         krb5_set_error_message(context, ENOTTY,
 843                                N_("unknown krbhst type (%u)", ""), type);
 844         return ENOTTY;
 845     }
 846     if((kd = common_init(context, realm, flags)) == NULL)
 847         return ENOMEM;
 848     kd->get_next = next;
 849     kd->def_port = def_port;
 850     *handle = kd;
 851     return 0;
 852 }
 853 
 854 /*
 855  * return the next host information from `handle' in `host'
 856  */
 857 
 858 krb5_error_code KRB5_LIB_FUNCTION
 859 krb5_krbhst_next(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 860                  krb5_krbhst_handle handle,
 861                  krb5_krbhst_info **host)
 862 {
 863     if(get_next(handle, host))
 864         return 0;
 865 
 866     return (*handle->get_next)(context, handle, host);
 867 }
 868 
 869 /*
 870  * return the next host information from `handle' as a host name
 871  * in `hostname' (or length `hostlen)
 872  */
 873 
 874 krb5_error_code KRB5_LIB_FUNCTION
 875 krb5_krbhst_next_as_string(krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 876                            krb5_krbhst_handle handle,
 877                            char *hostname,
 878                            size_t hostlen)
 879 {
 880     krb5_error_code ret;
 881     krb5_krbhst_info *host;
 882     ret = krb5_krbhst_next(context, handle, &host);
 883     if(ret)
 884         return ret;
 885     return krb5_krbhst_format_string(context, host, hostname, hostlen);
 886 }
 887 
 888 
 889 void KRB5_LIB_FUNCTION
 890 krb5_krbhst_reset(krb5_context context, krb5_krbhst_handle handle)
     /* [<][>][^][v][top][bottom][index][help] */
 891 {
 892     handle->index = &handle->hosts;
 893 }
 894 
 895 void KRB5_LIB_FUNCTION
 896 krb5_krbhst_free(krb5_context context, krb5_krbhst_handle handle)
     /* [<][>][^][v][top][bottom][index][help] */
 897 {
 898     krb5_krbhst_info *h, *next;
 899 
 900     if (handle == NULL)
 901         return;
 902 
 903     for (h = handle->hosts; h != NULL; h = next) {
 904         next = h->next;
 905         _krb5_free_krbhst_info(h);
 906     }
 907 
 908     free(handle->realm);
 909     free(handle);
 910 }
 911 
 912 /* backwards compatibility ahead */
 913 
 914 static krb5_error_code
 915 gethostlist(krb5_context context, const char *realm,
     /* [<][>][^][v][top][bottom][index][help] */
 916             unsigned int type, char ***hostlist)
 917 {
 918     krb5_error_code ret;
 919     int nhost = 0;
 920     krb5_krbhst_handle handle;
 921     char host[MAXHOSTNAMELEN];
 922     krb5_krbhst_info *hostinfo;
 923 
 924     ret = krb5_krbhst_init(context, realm, type, &handle);
 925     if (ret)
 926         return ret;
 927 
 928     while(krb5_krbhst_next(context, handle, &hostinfo) == 0)
 929         nhost++;
 930     if(nhost == 0) {
 931         krb5_set_error_message(context, KRB5_KDC_UNREACH,
 932                                N_("No KDC found for realm %s", ""), realm);
 933         return KRB5_KDC_UNREACH;
 934     }
 935     *hostlist = calloc(nhost + 1, sizeof(**hostlist));
 936     if(*hostlist == NULL) {
 937         krb5_krbhst_free(context, handle);
 938         return ENOMEM;
 939     }
 940 
 941     krb5_krbhst_reset(context, handle);
 942     nhost = 0;
 943     while(krb5_krbhst_next_as_string(context, handle,
 944                                      host, sizeof(host)) == 0) {
 945         if(((*hostlist)[nhost++] = strdup(host)) == NULL) {
 946             krb5_free_krbhst(context, *hostlist);
 947             krb5_krbhst_free(context, handle);
 948             return ENOMEM;
 949         }
 950     }
 951     (*hostlist)[nhost++] = NULL;
 952     krb5_krbhst_free(context, handle);
 953     return 0;
 954 }
 955 
 956 /*
 957  * return an malloced list of kadmin-hosts for `realm' in `hostlist'
 958  */
 959 
 960 krb5_error_code KRB5_LIB_FUNCTION
 961 krb5_get_krb_admin_hst (krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 962                         const krb5_realm *realm,
 963                         char ***hostlist)
 964 {
 965     return gethostlist(context, *realm, KRB5_KRBHST_ADMIN, hostlist);
 966 }
 967 
 968 /*
 969  * return an malloced list of changepw-hosts for `realm' in `hostlist'
 970  */
 971 
 972 krb5_error_code KRB5_LIB_FUNCTION
 973 krb5_get_krb_changepw_hst (krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 974                            const krb5_realm *realm,
 975                            char ***hostlist)
 976 {
 977     return gethostlist(context, *realm, KRB5_KRBHST_CHANGEPW, hostlist);
 978 }
 979 
 980 /*
 981  * return an malloced list of 524-hosts for `realm' in `hostlist'
 982  */
 983 
 984 krb5_error_code KRB5_LIB_FUNCTION
 985 krb5_get_krb524hst (krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 986                     const krb5_realm *realm,
 987                     char ***hostlist)
 988 {
 989     return gethostlist(context, *realm, KRB5_KRBHST_KRB524, hostlist);
 990 }
 991 
 992 
 993 /*
 994  * return an malloced list of KDC's for `realm' in `hostlist'
 995  */
 996 
 997 krb5_error_code KRB5_LIB_FUNCTION
 998 krb5_get_krbhst (krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
 999                  const krb5_realm *realm,
1000                  char ***hostlist)
1001 {
1002     return gethostlist(context, *realm, KRB5_KRBHST_KDC, hostlist);
1003 }
1004 
1005 /*
1006  * free all the memory allocated in `hostlist'
1007  */
1008 
1009 krb5_error_code KRB5_LIB_FUNCTION
1010 krb5_free_krbhst (krb5_context context,
     /* [<][>][^][v][top][bottom][index][help] */
1011                   char **hostlist)
1012 {
1013     char **p;
1014 
1015     for (p = hostlist; *p; ++p)
1016         free (*p);
1017     free (hostlist);
1018     return 0;
1019 }

/* [<][>][^][v][top][bottom][index][help] */