heimdal Fix DNS name qualification to not mangle IP addresses
[samba.git] / source4 / heimdal / lib / krb5 / krbhst.c
index 69b52dd808c296f7b6423a60130d99093df7a95b..ec0c8b738e68fd03627aa8cbb2ae97335e19c37b 100644 (file)
@@ -1,50 +1,48 @@
 /*
- * Copyright (c) 2001 - 2003 Kungliga Tekniska Högskolan
- * (Royal Institute of Technology, Stockholm, Sweden). 
- * All rights reserved. 
+ * Copyright (c) 2001 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
  *
- * Redistribution and use in source and binary forms, with or without 
- * modification, are permitted provided that the following conditions 
- * are met: 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * 1. Redistributions of source code must retain the above copyright 
- *    notice, this list of conditions and the following disclaimer. 
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
  *
- * 2. Redistributions in binary form must reproduce the above copyright 
- *    notice, this list of conditions and the following disclaimer in the 
- *    documentation and/or other materials provided with the distribution. 
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
  *
- * 3. Neither the name of the Institute nor the names of its contributors 
- *    may be used to endorse or promote products derived from this software 
- *    without specific prior written permission. 
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
  *
- * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
- * SUCH DAMAGE. 
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 
 #include "krb5_locl.h"
 #include <resolve.h>
 #include "locate_plugin.h"
 
-RCSID("$Id: krbhst.c 21131 2007-06-18 20:48:09Z lha $");
-
 static int
 string_to_proto(const char *string)
 {
     if(strcasecmp(string, "udp") == 0)
        return KRB5_KRBHST_UDP;
-    else if(strcasecmp(string, "tcp") == 0) 
+    else if(strcasecmp(string, "tcp") == 0)
        return KRB5_KRBHST_TCP;
-    else if(strcasecmp(string, "http") == 0) 
+    else if(strcasecmp(string, "http") == 0)
        return KRB5_KRBHST_HTTP;
     return -1;
 }
@@ -56,13 +54,13 @@ string_to_proto(const char *string)
  */
 
 static krb5_error_code
-srv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count, 
+srv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count,
               const char *realm, const char *dns_type,
               const char *proto, const char *service, int port)
 {
     char domain[1024];
-    struct dns_reply *r;
-    struct resource_record *rr;
+    struct rk_dns_reply *r;
+    struct rk_resource_record *rr;
     int num_srv;
     int proto_num;
     int def_port;
@@ -72,7 +70,9 @@ srv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count,
 
     proto_num = string_to_proto(proto);
     if(proto_num < 0) {
-       krb5_set_error_string(context, "unknown protocol `%s'", proto);
+       krb5_set_error_message(context, EINVAL,
+                              N_("unknown protocol `%s' to lookup", ""),
+                              proto);
        return EINVAL;
     }
 
@@ -85,31 +85,35 @@ srv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count,
 
     snprintf(domain, sizeof(domain), "_%s._%s.%s.", service, proto, realm);
 
-    r = dns_lookup(domain, dns_type);
-    if(r == NULL)
+    r = rk_dns_lookup(domain, dns_type);
+    if(r == NULL) {
+       _krb5_debug(context, 0,
+                   "DNS lookup failed domain: %s", domain);
        return KRB5_KDC_UNREACH;
+    }
 
-    for(num_srv = 0, rr = r->head; rr; rr = rr->next) 
-       if(rr->type == T_SRV)
+    for(num_srv = 0, rr = r->head; rr; rr = rr->next)
+       if(rr->type == rk_ns_t_srv)
            num_srv++;
 
     *res = malloc(num_srv * sizeof(**res));
     if(*res == NULL) {
-       dns_free_data(r);
-       krb5_set_error_string(context, "malloc: out of memory");
+       rk_dns_free_data(r);
+       krb5_set_error_message(context, ENOMEM,
+                              N_("malloc: out of memory", ""));
        return ENOMEM;
     }
 
-    dns_srv_order(r);
+    rk_dns_srv_order(r);
 
-    for(num_srv = 0, rr = r->head; rr; rr = rr->next) 
-       if(rr->type == T_SRV) {
+    for(num_srv = 0, rr = r->head; rr; rr = rr->next)
+       if(rr->type == rk_ns_t_srv) {
            krb5_krbhst_info *hi;
            size_t len = strlen(rr->u.srv->target);
 
            hi = calloc(1, sizeof(*hi) + len);
            if(hi == NULL) {
-               dns_free_data(r);
+               rk_dns_free_data(r);
                while(--num_srv >= 0)
                    free((*res)[num_srv]);
                free(*res);
@@ -119,7 +123,7 @@ srv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count,
            (*res)[num_srv++] = hi;
 
            hi->proto = proto_num;
-           
+       
            hi->def_port = def_port;
            if (port != 0)
                hi->port = port;
@@ -130,8 +134,8 @@ srv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count,
        }
 
     *count = num_srv;
-           
-    dns_free_data(r);
+       
+    rk_dns_free_data(r);
     return 0;
 }
 
@@ -149,7 +153,7 @@ struct krb5_krbhst_data {
 #define KD_CONFIG_EXISTS       32
 #define KD_LARGE_MSG           64
 #define KD_PLUGIN             128
-    krb5_error_code (*get_next)(krb5_context, struct krb5_krbhst_data *, 
+    krb5_error_code (*get_next)(krb5_context, struct krb5_krbhst_data *,
                                krb5_krbhst_info**);
 
     unsigned int fallback_count;
@@ -175,6 +179,15 @@ krbhst_get_default_proto(struct krb5_krbhst_data *kd)
     return KRB5_KRBHST_UDP;
 }
 
+/*
+ *
+ */
+
+const char *
+_krb5_krbhst_get_realm(krb5_krbhst_handle handle)
+{
+    return handle->realm;
+}
 
 /*
  * parse `spec' into a krb5_krbhst_info, defaulting the port to `def_port'
@@ -185,13 +198,13 @@ static struct krb5_krbhst_info*
 parse_hostspec(krb5_context context, struct krb5_krbhst_data *kd,
               const char *spec, int def_port, int port)
 {
-    const char *p = spec;
+    const char *p = spec, *q;
     struct krb5_krbhst_info *hi;
-    
+
     hi = calloc(1, sizeof(*hi) + strlen(spec));
     if(hi == NULL)
        return NULL;
-       
+
     hi->proto = krbhst_get_default_proto(kd);
 
     if(strncmp(p, "http://", 7) == 0){
@@ -208,7 +221,17 @@ parse_hostspec(krb5_context context, struct krb5_krbhst_data *kd,
        p += 4;
     }
 
-    if(strsep_copy(&p, ":", hi->hostname, strlen(spec) + 1) < 0) {
+    if (p[0] == '[' && (q = strchr(p, ']')) != NULL) {
+       /* if address looks like [foo:bar] or [foo:bar]: its a ipv6
+          adress, strip of [] */
+       memcpy(hi->hostname, &p[1], q - p - 1);
+       hi->hostname[q - p - 1] = '\0';
+       p = q + 1;
+       /* get trailing : */
+       if (p[0] == ':')
+           p++;
+    } else if(strsep_copy(&p, ":", hi->hostname, strlen(spec) + 1) < 0) {
+       /* copy everything before : */
        free(hi);
        return NULL;
     }
@@ -217,7 +240,7 @@ parse_hostspec(krb5_context context, struct krb5_krbhst_data *kd,
     strlwr(hi->hostname);
 
     hi->port = hi->def_port = def_port;
-    if(p != NULL) {
+    if(p != NULL && p[0]) {
        char *end;
        hi->port = strtol(p, &end, 0);
        if(end == p) {
@@ -245,9 +268,10 @@ _krb5_krbhost_info_move(krb5_context context,
 {
     size_t hostnamelen = strlen(from->hostname);
     /* trailing NUL is included in structure */
-    *to = calloc(1, sizeof(**to) + hostnamelen); 
+    *to = calloc(1, sizeof(**to) + hostnamelen);
     if(*to == NULL) {
-       krb5_set_error_string(context, "malloc - out of memory");
+       krb5_set_error_message(context, ENOMEM,
+                              N_("malloc: out of memory", ""));
        return ENOMEM;
     }
 
@@ -268,8 +292,8 @@ append_host_hostinfo(struct krb5_krbhst_data *kd, struct krb5_krbhst_info *host)
     struct krb5_krbhst_info *h;
 
     for(h = kd->hosts; h; h = h->next)
-       if(h->proto == host->proto && 
-          h->port == host->port && 
+       if(h->proto == host->proto &&
+          h->port == host->port &&
           strcmp(h->hostname, host->hostname) == 0) {
            _krb5_free_krbhst_info(host);
            return;
@@ -287,7 +311,7 @@ append_host_string(krb5_context context, struct krb5_krbhst_data *kd,
     hi = parse_hostspec(context, kd, host, def_port, port);
     if(hi == NULL)
        return ENOMEM;
-    
+
     append_host_hostinfo(kd, hi);
     return 0;
 }
@@ -296,8 +320,8 @@ append_host_string(krb5_context context, struct krb5_krbhst_data *kd,
  * return a readable representation of `host' in `hostname, hostlen'
  */
 
-krb5_error_code KRB5_LIB_FUNCTION
-krb5_krbhst_format_string(krb5_context context, const krb5_krbhst_info *host, 
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_krbhst_format_string(krb5_context context, const krb5_krbhst_info *host,
                          char *hostname, size_t hostlen)
 {
     const char *proto = "";
@@ -337,7 +361,7 @@ make_hints(struct addrinfo *hints, int proto)
  * in `host'.  free:ing is handled by krb5_krbhst_free.
  */
 
-krb5_error_code KRB5_LIB_FUNCTION
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
 krb5_krbhst_get_addrinfo(krb5_context context, krb5_krbhst_info *host,
                         struct addrinfo **ai)
 {
@@ -347,8 +371,41 @@ krb5_krbhst_get_addrinfo(krb5_context context, krb5_krbhst_info *host,
 
     if (host->ai == NULL) {
        make_hints(&hints, host->proto);
+       hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
        snprintf (portstr, sizeof(portstr), "%d", host->port);
+
+       /* First try this as an IP address - the flags we have set
+        * will prevent it from looking up a name */
        ret = getaddrinfo(host->hostname, portstr, &hints, &host->ai);
+       if (ret == 0) {
+               *ai = host->ai;
+               return 0;
+       }
+
+       hints.ai_flags &= ~AI_NUMERICHOST;
+
+       /* Now that we know it's not an IP, we can manipulate
+          it as a dotted-name, to add a final . if we think
+          it's a fully qualified DNS name */
+       if (strchr(host->hostname, '.') &&
+           host->hostname[strlen(host->hostname)-1] != '.') {
+               char *hostname_dot = NULL;
+
+               /* avoid expansion of search domains from resolv.conf
+                  - these can be very slow if the DNS server is not up
+                  for the searched domain */
+               hostname_dot = malloc(strlen(host->hostname)+2);
+               if (hostname_dot) {
+                       strcpy(hostname_dot, host->hostname);
+                       hostname_dot[strlen(host->hostname)] = '.';
+                       hostname_dot[strlen(host->hostname)+1] = 0;
+               }
+               ret = getaddrinfo(hostname_dot?hostname_dot:host->hostname, portstr, &hints, &host->ai);
+               if (hostname_dot)
+                       free(hostname_dot);
+       } else {
+               ret = getaddrinfo(host->hostname, portstr, &hints, &host->ai);
+       }
        if (ret)
            return krb5_eai_to_heim_errno(ret, errno);
     }
@@ -369,14 +426,18 @@ get_next(struct krb5_krbhst_data *kd, krb5_krbhst_info **host)
 }
 
 static void
-srv_get_hosts(krb5_context context, struct krb5_krbhst_data *kd, 
+srv_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
              const char *proto, const char *service)
 {
+    krb5_error_code ret;
     krb5_krbhst_info **res;
     int count, i;
 
-    if (srv_find_realm(context, &res, &count, kd->realm, "SRV", proto, service,
-                      kd->port))
+    ret = srv_find_realm(context, &res, &count, kd->realm, "SRV", proto, service,
+                        kd->port);
+    _krb5_debug(context, 2, "searching DNS for realm %s %s.%s -> %d",
+               kd->realm, proto, service, ret);
+    if (ret)
        return;
     for(i = 0; i < count; i++)
        append_host_hostinfo(kd, res[i]);
@@ -389,15 +450,17 @@ srv_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
  */
 
 static void
-config_get_hosts(krb5_context context, struct krb5_krbhst_data *kd, 
+config_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
                 const char *conf_string)
 {
     int i;
-       
     char **hostlist;
-    hostlist = krb5_config_get_strings(context, NULL, 
+    hostlist = krb5_config_get_strings(context, NULL,
                                       "realms", kd->realm, conf_string, NULL);
 
+    _krb5_debug(context, 2, "configuration file for realm %s%s found",
+               kd->realm, hostlist ? "" : " not");
+
     if(hostlist == NULL)
        return;
     kd->flags |= KD_CONFIG_EXISTS;
@@ -410,12 +473,12 @@ config_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
 /*
  * as a fallback, look for `serv_string.kd->realm' (typically
  * kerberos.REALM, kerberos-1.REALM, ...
- * `port' is the default port for the service, and `proto' the 
+ * `port' is the default port for the service, and `proto' the
  * protocol
  */
 
 static krb5_error_code
-fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd, 
+fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
                   const char *serv_string, int port, int proto)
 {
     char *host;
@@ -424,7 +487,10 @@ fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
     struct addrinfo hints;
     char portstr[NI_MAXSERV];
 
-    /* 
+    _krb5_debug(context, 2, "fallback lookup %d for realm %s (service %s)",
+               kd->fallback_count, kd->realm, serv_string);
+
+    /*
      * Don't try forever in case the DNS server keep returning us
      * entries (like wildcard entries or the .nu TLD)
      */
@@ -436,12 +502,12 @@ fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
     if(kd->fallback_count == 0)
        asprintf(&host, "%s.%s.", serv_string, kd->realm);
     else
-       asprintf(&host, "%s-%d.%s.", 
-                serv_string, kd->fallback_count, kd->realm);       
+       asprintf(&host, "%s-%d.%s.",
+                serv_string, kd->fallback_count, kd->realm);   
 
     if (host == NULL)
        return ENOMEM;
-    
+
     make_hints(&hints, proto);
     snprintf(portstr, sizeof(portstr), "%d", port);
     ret = getaddrinfo(host, portstr, &hints, &ai);
@@ -475,7 +541,7 @@ fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
  * Fetch hosts from plugin
  */
 
-static krb5_error_code 
+static krb5_error_code
 add_locate(void *ctx, int type, struct sockaddr *addr)
 {
     struct krb5_krbhst_info *hi;
@@ -503,7 +569,7 @@ add_locate(void *ctx, int type, struct sockaddr *addr)
     hi = calloc(1, sizeof(*hi) + hostlen);
     if(hi == NULL)
        return ENOMEM;
-    
+
     hi->proto = krbhst_get_default_proto(kd);
     hi->port  = hi->def_port = socket_get_port(addr);
     hi->ai    = ai;
@@ -522,12 +588,11 @@ plugin_get_hosts(krb5_context context,
     struct krb5_plugin *list = NULL, *e;
     krb5_error_code ret;
 
-    ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, "resolve", &list);
+    ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA,
+                           KRB5_PLUGIN_LOCATE, &list);
     if(ret != 0 || list == NULL)
        return;
 
-    kd->flags |= KD_CONFIG_EXISTS;
-
     for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
        krb5plugin_service_locate_ftable *service;
        void *ctx;
@@ -539,10 +604,16 @@ plugin_get_hosts(krb5_context context,
        (*service->init)(context, &ctx);
        ret = (*service->lookup)(ctx, type, kd->realm, 0, 0, add_locate, kd);
        (*service->fini)(ctx);
-       if (ret) {
-           krb5_set_error_string(context, "Plugin failed to lookup");
+       if (ret && ret != KRB5_PLUGIN_NO_HANDLE) {
+           krb5_set_error_message(context, ret,
+                                  N_("Locate plugin failed to lookup realm %s: %d", ""),
+                                  kd->realm, ret);
            break;
+       } else if (ret == 0) {
+           _krb5_debug(context, 2, "plugin found result for realm %s", kd->realm);
+           kd->flags |= KD_CONFIG_EXISTS;
        }
+
     }
     _krb5_plugin_free(list);
 }
@@ -572,8 +643,12 @@ kdc_get_next(krb5_context context,
            return 0;
     }
 
-    if (kd->flags & KD_CONFIG_EXISTS)
-       return KRB5_KDC_UNREACH; /* XXX */
+    if (kd->flags & KD_CONFIG_EXISTS) {
+       _krb5_debug(context, 1,
+                   "Configuration exists for realm %s, wont go to DNS",
+                   kd->realm);
+       return KRB5_KDC_UNREACH;
+    }
 
     if(context->srv_lookup) {
        if((kd->flags & KD_SRV_UDP) == 0 && (kd->flags & KD_LARGE_MSG) == 0) {
@@ -599,7 +674,7 @@ kdc_get_next(krb5_context context,
 
     while((kd->flags & KD_FALLBACK) == 0) {
        ret = fallback_get_hosts(context, kd, "kerberos",
-                                kd->def_port, 
+                                kd->def_port,
                                 krbhst_get_default_proto(kd));
        if(ret)
            return ret;
@@ -607,6 +682,8 @@ kdc_get_next(krb5_context context,
            return 0;
     }
 
+    _krb5_debug(context, 0, "No KDC entries found for %s", kd->realm);
+
     return KRB5_KDC_UNREACH; /* XXX */
 }
 
@@ -631,8 +708,12 @@ admin_get_next(krb5_context context,
            return 0;
     }
 
-    if (kd->flags & KD_CONFIG_EXISTS)
-       return KRB5_KDC_UNREACH; /* XXX */
+    if (kd->flags & KD_CONFIG_EXISTS) {
+       _krb5_debug(context, 1,
+                   "Configuration exists for realm %s, wont go to DNS",
+                   kd->realm);
+       return KRB5_KDC_UNREACH;
+    }
 
     if(context->srv_lookup) {
        if((kd->flags & KD_SRV_TCP) == 0) {
@@ -655,6 +736,8 @@ admin_get_next(krb5_context context,
            return 0;
     }
 
+    _krb5_debug(context, 0, "No admin entries found for realm %s", kd->realm);
+
     return KRB5_KDC_UNREACH;   /* XXX */
 }
 
@@ -679,8 +762,12 @@ kpasswd_get_next(krb5_context context,
            return 0;
     }
 
-    if (kd->flags & KD_CONFIG_EXISTS)
-       return KRB5_KDC_UNREACH; /* XXX */
+    if (kd->flags & KD_CONFIG_EXISTS) {
+       _krb5_debug(context, 1,
+                   "Configuration exists for realm %s, wont go to DNS",
+                   kd->realm);
+       return KRB5_KDC_UNREACH;
+    }
 
     if(context->srv_lookup) {
        if((kd->flags & KD_SRV_UDP) == 0) {
@@ -709,7 +796,9 @@ kpasswd_get_next(krb5_context context,
        return ret;
     }
 
-    return KRB5_KDC_UNREACH; /* XXX */
+    _krb5_debug(context, 0, "No kpasswd entries found for realm %s", kd->realm);
+
+    return KRB5_KDC_UNREACH;
 }
 
 static krb5_error_code
@@ -731,8 +820,12 @@ krb524_get_next(krb5_context context,
        kd->flags |= KD_CONFIG;
     }
 
-    if (kd->flags & KD_CONFIG_EXISTS)
-       return KRB5_KDC_UNREACH; /* XXX */
+    if (kd->flags & KD_CONFIG_EXISTS) {
+       _krb5_debug(context, 1,
+                   "Configuration exists for realm %s, wont go to DNS",
+                   kd->realm);
+       return KRB5_KDC_UNREACH;
+    }
 
     if(context->srv_lookup) {
        if((kd->flags & KD_SRV_UDP) == 0) {
@@ -759,11 +852,14 @@ krb524_get_next(krb5_context context,
        return (*kd->get_next)(context, kd, host);
     }
 
-    return KRB5_KDC_UNREACH; /* XXX */
+    _krb5_debug(context, 0, "No kpasswd entries found for realm %s", kd->realm);
+
+    return KRB5_KDC_UNREACH;
 }
 
 static struct krb5_krbhst_data*
 common_init(krb5_context context,
+           const char *service,
            const char *realm,
            int flags)
 {
@@ -777,6 +873,9 @@ common_init(krb5_context context,
        return NULL;
     }
 
+    _krb5_debug(context, 2, "Trying to find service %s for realm %s flags %x",
+               service, realm, flags);
+
     /* For 'realms' without a . do not even think of going to DNS */
     if (!strchr(realm, '.'))
        kd->flags |= KD_CONFIG_EXISTS;
@@ -791,7 +890,7 @@ common_init(krb5_context context,
  * initialize `handle' to look for hosts of type `type' in realm `realm'
  */
 
-krb5_error_code KRB5_LIB_FUNCTION
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
 krb5_krbhst_init(krb5_context context,
                 const char *realm,
                 unsigned int type,
@@ -800,7 +899,7 @@ krb5_krbhst_init(krb5_context context,
     return krb5_krbhst_init_flags(context, realm, type, 0, handle);
 }
 
-krb5_error_code KRB5_LIB_FUNCTION
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
 krb5_krbhst_init_flags(krb5_context context,
                       const char *realm,
                       unsigned int type,
@@ -808,34 +907,40 @@ krb5_krbhst_init_flags(krb5_context context,
                       krb5_krbhst_handle *handle)
 {
     struct krb5_krbhst_data *kd;
-    krb5_error_code (*next)(krb5_context, struct krb5_krbhst_data *, 
+    krb5_error_code (*next)(krb5_context, struct krb5_krbhst_data *,
                            krb5_krbhst_info **);
     int def_port;
+    const char *service;
 
     switch(type) {
     case KRB5_KRBHST_KDC:
        next = kdc_get_next;
        def_port = ntohs(krb5_getportbyname (context, "kerberos", "udp", 88));
+       service = "kdc";
        break;
     case KRB5_KRBHST_ADMIN:
        next = admin_get_next;
        def_port = ntohs(krb5_getportbyname (context, "kerberos-adm",
                                             "tcp", 749));
+       service = "admin";
        break;
     case KRB5_KRBHST_CHANGEPW:
        next = kpasswd_get_next;
        def_port = ntohs(krb5_getportbyname (context, "kpasswd", "udp",
                                             KPASSWD_PORT));
+       service = "change_password";
        break;
     case KRB5_KRBHST_KRB524:
        next = krb524_get_next;
        def_port = ntohs(krb5_getportbyname (context, "krb524", "udp", 4444));
+       service = "524";
        break;
     default:
-       krb5_set_error_string(context, "unknown krbhst type (%u)", type);
+       krb5_set_error_message(context, ENOTTY,
+                              N_("unknown krbhst type (%u)", ""), type);
        return ENOTTY;
     }
-    if((kd = common_init(context, realm, flags)) == NULL)
+    if((kd = common_init(context, service, realm, flags)) == NULL)
        return ENOMEM;
     kd->get_next = next;
     kd->def_port = def_port;
@@ -847,7 +952,7 @@ krb5_krbhst_init_flags(krb5_context context,
  * return the next host information from `handle' in `host'
  */
 
-krb5_error_code KRB5_LIB_FUNCTION
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
 krb5_krbhst_next(krb5_context context,
                 krb5_krbhst_handle handle,
                 krb5_krbhst_info **host)
@@ -863,7 +968,7 @@ krb5_krbhst_next(krb5_context context,
  * in `hostname' (or length `hostlen)
  */
 
-krb5_error_code KRB5_LIB_FUNCTION
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
 krb5_krbhst_next_as_string(krb5_context context,
                           krb5_krbhst_handle handle,
                           char *hostname,
@@ -878,13 +983,13 @@ krb5_krbhst_next_as_string(krb5_context context,
 }
 
 
-void KRB5_LIB_FUNCTION
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
 krb5_krbhst_reset(krb5_context context, krb5_krbhst_handle handle)
 {
     handle->index = &handle->hosts;
 }
 
-void KRB5_LIB_FUNCTION
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
 krb5_krbhst_free(krb5_context context, krb5_krbhst_handle handle)
 {
     krb5_krbhst_info *h, *next;
@@ -904,7 +1009,7 @@ krb5_krbhst_free(krb5_context context, krb5_krbhst_handle handle)
 /* backwards compatibility ahead */
 
 static krb5_error_code
-gethostlist(krb5_context context, const char *realm, 
+gethostlist(krb5_context context, const char *realm,
            unsigned int type, char ***hostlist)
 {
     krb5_error_code ret;
@@ -919,8 +1024,11 @@ gethostlist(krb5_context context, const char *realm,
 
     while(krb5_krbhst_next(context, handle, &hostinfo) == 0)
        nhost++;
-    if(nhost == 0)
+    if(nhost == 0) {
+       krb5_set_error_message(context, KRB5_KDC_UNREACH,
+                              N_("No KDC found for realm %s", ""), realm);
        return KRB5_KDC_UNREACH;
+    }
     *hostlist = calloc(nhost + 1, sizeof(**hostlist));
     if(*hostlist == NULL) {
        krb5_krbhst_free(context, handle);
@@ -929,7 +1037,7 @@ gethostlist(krb5_context context, const char *realm,
 
     krb5_krbhst_reset(context, handle);
     nhost = 0;
-    while(krb5_krbhst_next_as_string(context, handle, 
+    while(krb5_krbhst_next_as_string(context, handle,
                                     host, sizeof(host)) == 0) {
        if(((*hostlist)[nhost++] = strdup(host)) == NULL) {
            krb5_free_krbhst(context, *hostlist);
@@ -937,7 +1045,7 @@ gethostlist(krb5_context context, const char *realm,
            return ENOMEM;
        }
     }
-    (*hostlist)[nhost++] = NULL;
+    (*hostlist)[nhost] = NULL;
     krb5_krbhst_free(context, handle);
     return 0;
 }
@@ -946,7 +1054,7 @@ gethostlist(krb5_context context, const char *realm,
  * return an malloced list of kadmin-hosts for `realm' in `hostlist'
  */
 
-krb5_error_code KRB5_LIB_FUNCTION
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
 krb5_get_krb_admin_hst (krb5_context context,
                        const krb5_realm *realm,
                        char ***hostlist)
@@ -958,7 +1066,7 @@ krb5_get_krb_admin_hst (krb5_context context,
  * return an malloced list of changepw-hosts for `realm' in `hostlist'
  */
 
-krb5_error_code KRB5_LIB_FUNCTION
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
 krb5_get_krb_changepw_hst (krb5_context context,
                           const krb5_realm *realm,
                           char ***hostlist)
@@ -970,7 +1078,7 @@ krb5_get_krb_changepw_hst (krb5_context context,
  * return an malloced list of 524-hosts for `realm' in `hostlist'
  */
 
-krb5_error_code KRB5_LIB_FUNCTION
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
 krb5_get_krb524hst (krb5_context context,
                    const krb5_realm *realm,
                    char ***hostlist)
@@ -983,7 +1091,7 @@ krb5_get_krb524hst (krb5_context context,
  * return an malloced list of KDC's for `realm' in `hostlist'
  */
 
-krb5_error_code KRB5_LIB_FUNCTION
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
 krb5_get_krbhst (krb5_context context,
                 const krb5_realm *realm,
                 char ***hostlist)
@@ -995,7 +1103,7 @@ krb5_get_krbhst (krb5_context context,
  * free all the memory allocated in `hostlist'
  */
 
-krb5_error_code KRB5_LIB_FUNCTION
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
 krb5_free_krbhst (krb5_context context,
                  char **hostlist)
 {