svn rev #24635: trunk/src/ include/ lib/krb5/ lib/krb5/os/

ghudson@MIT.EDU ghudson at MIT.EDU
Sun Feb 13 16:14:01 EST 2011


http://src.mit.edu/fisheye/changelog/krb5/?cs=24635
Commit By: ghudson
Log Message:
ticket: 6868
subject: Defer hostname lookups in krb5_sendto_kdc

Restructure the locate_kdc and sendto_kdc code to defer getaddrinfo
calls until we need the answer.  This requires many changes:

* struct addrlist is now called struct serverlist, and is declared in
  os-proto.h instead of k5-int.h.  It contains an array of struct
  server_entry structures which can hold either a name or an address.
  (Address entries are used for locate_kdc module results.)

* The connection state list is now a linked list, and holds address
  information directly instead of using a struct addrinfo (this
  simplifies memory management).  Each connection entry contains a
  callback buffer (previously stored in a separate array) and an index
  into the server list.

* The {addrstate} trace formatting primitive is no longer needed, and
  has been replaced by {connstate}.  There is also a new tracing event
  for resolving hostnames.

* locate_server, locate_kdc, free_serverlist, and sendto get their
  prefixes changed from krb5int_ to k5_ as their prototypes were being
  adjusted anyway.  The family argument is gone from the locate
  functions as it was never productively used.  k5_sendto now receives
  the socket types of interest.

* krb5_sendto_kdc will now pass a 0 socktype to k5_locate_kdc if both
  socket types are wanted.  There were some allowances for this in
  locate but this was never previously done.  In order to be
  conservative when invoking locate modules, we always pass an
  explicit socktype, thus calling lookup twice (as we did before,
  albeit with a separate init/fini cycle) in the common case.  When
  creating hostname entries in serverlist from profile configuration,
  we preserve the 0 value of socktype, and later create both TCP and
  UDP addresses from the getaddrinfo results when the host is
  resolved.

* Some accessor functions previously used by libkrb4 have been removed
  as they impinged upon this work.



Changed Files:
U   trunk/src/include/cm.h
U   trunk/src/include/k5-int.h
U   trunk/src/include/k5-trace.h
U   trunk/src/lib/krb5/libkrb5.exports
U   trunk/src/lib/krb5/os/accessor.c
U   trunk/src/lib/krb5/os/changepw.c
U   trunk/src/lib/krb5/os/hst_realm.c
U   trunk/src/lib/krb5/os/locate_kdc.c
U   trunk/src/lib/krb5/os/os-proto.h
U   trunk/src/lib/krb5/os/sendto_kdc.c
U   trunk/src/lib/krb5/os/t_locate_kdc.c
U   trunk/src/lib/krb5/os/t_std_conf.c
U   trunk/src/lib/krb5/os/trace.c
Modified: trunk/src/include/cm.h
===================================================================
--- trunk/src/include/cm.h	2011-02-13 19:12:36 UTC (rev 24634)
+++ trunk/src/include/cm.h	2011-02-13 21:14:00 UTC (rev 24635)
@@ -62,7 +62,10 @@
     unsigned int is_udp : 1;
     int (*service)(krb5_context context, struct conn_state *,
                    struct select_state *, int);
-    struct addrinfo *addr;
+    int socktype;
+    int family;
+    size_t addrlen;
+    struct sockaddr_storage addr;
     struct {
         struct {
             sg_buf sgbuf[2];
@@ -72,6 +75,9 @@
         } out;
         struct incoming_krb5_message in;
     } x;
+    krb5_data callback_buffer;
+    size_t server_index;
+    struct conn_state *next;
 };
 
 struct sendto_callback_info {

Modified: trunk/src/include/k5-int.h
===================================================================
--- trunk/src/include/k5-int.h	2011-02-13 19:12:36 UTC (rev 24634)
+++ trunk/src/include/k5-int.h	2011-02-13 21:14:00 UTC (rev 24635)
@@ -590,7 +590,6 @@
 
 #include "k5-gmt_mktime.h"
 
-struct addrlist;
 struct sendto_callback_info;
 
 /* libos.spec */
@@ -621,33 +620,6 @@
 krb5_error_code
 krb5int_get_domain_realm_mapping(krb5_context , const char *, char ***);
 
-/* N.B.: You need to include fake-addrinfo.h *before* k5-int.h if you're
-   going to use this structure.  */
-struct addrlist {
-    struct {
-#ifdef FAI_DEFINED
-        struct addrinfo *ai;
-#else
-        struct undefined_addrinfo *ai;
-#endif
-        void (*freefn)(void *);
-        void *data;
-    } *addrs;
-    size_t naddrs;
-    size_t space;
-};
-#define ADDRLIST_INIT { 0, 0, 0 }
-extern void krb5int_free_addrlist(struct addrlist *);
-extern int krb5int_grow_addrlist(struct addrlist *, int);
-extern int krb5int_add_host_to_list(struct addrlist *, const char *,
-                                    int, int, int, int);
-
-#include <krb5/locate_plugin.h>
-krb5_error_code
-krb5int_locate_server(krb5_context, const krb5_data *realm,
-                      struct addrlist *, enum locate_service_type svc,
-                      int sockettype, int family);
-
 struct derived_key {
     krb5_data constant;
     krb5_key dkey;
@@ -2289,7 +2261,7 @@
 /* To keep happy libraries which are (for now) accessing internal stuff */
 
 /* Make sure to increment by one when changing the struct */
-#define KRB5INT_ACCESS_STRUCT_VERSION 16
+#define KRB5INT_ACCESS_STRUCT_VERSION 17
 
 #ifndef ANAME_SZ
 struct ktext;                   /* from krb.h, for krb524 support */
@@ -2305,29 +2277,7 @@
     krb5_error_code (*auth_con_get_subkey_enctype)(krb5_context,
                                                    krb5_auth_context,
                                                    krb5_enctype *);
-    /* service location and communication */
-    krb5_error_code (*sendto_udp)(krb5_context, const krb5_data *msg,
-                                  const struct addrlist *,
-                                  struct sendto_callback_info *,
-                                  krb5_data *reply, struct sockaddr *,
-                                  socklen_t *, struct sockaddr *,
-                                  socklen_t *, int *,
-                                  int (*msg_handler)(krb5_context,
-                                                     const krb5_data *,
-                                                     void *),
-                                  void *msg_handler_data);
-    krb5_error_code (*add_host_to_list)(struct addrlist *lp,
-                                        const char *hostname,
-                                        int port, int secport,
-                                        int socktype, int family);
-    void (*free_addrlist)(struct addrlist *);
 
-    krb5_error_code (*make_srv_query_realm)(const krb5_data *realm,
-                                            const char *service,
-                                            const char *protocol,
-                                            struct srv_dns_entry **answers);
-    void (*free_srv_dns_data)(struct srv_dns_entry *);
-    int (*use_dns_kdc)(krb5_context);
     krb5_error_code (*clean_hostname)(krb5_context, const char *, char *,
                                       size_t);
 

Modified: trunk/src/include/k5-trace.h
===================================================================
--- trunk/src/include/k5-trace.h	2011-02-13 19:12:36 UTC (rev 24634)
+++ trunk/src/include/k5-trace.h	2011-02-13 21:14:00 UTC (rev 24635)
@@ -60,7 +60,7 @@
  *   {lenstr}      size_t and const char *, as a counted string
  *   {hexlenstr}   size_t and const char *, as hex bytes
  *   {hashlenstr}  size_t and const char *, as four-character hex hash
- *   {addrinfo}    struct addrinfo *, show socket type, address, port
+ *   {connstate}   struct conn_state *, show socket type, address, port
  *   {data}        krb5_data *, display as counted string
  *   {hexdata}     krb5_data *, display as hex bytes
  *   {errno}       int, display as number/errorstring
@@ -264,32 +264,34 @@
               rlm, (master) ? " (master)" : "", (tcp) ? " (tcp only)" : ""))
 #define TRACE_SENDTO_KDC_MASTER(c, master) \
     TRACE(c, (c, "Response was{str} from master KDC", (master) ? "" : " not"))
-#define TRACE_SENDTO_KDC_RESPONSE(c, addr) \
-    TRACE(c, (c, "Received answer from {addrinfo}", addr))
-#define TRACE_SENDTO_KDC_TCP_CONNECT(c, addr) \
-    TRACE(c, (c, "Initiating TCP connection to {addrinfo}", addr))
-#define TRACE_SENDTO_KDC_TCP_DISCONNECT(c, addr) \
-    TRACE(c, (c, "Terminating TCP connection to {addrinfo}", addr))
-#define TRACE_SENDTO_KDC_TCP_ERROR_CONNECT(c, addr, err) \
-    TRACE(c, (c, "TCP error connecting to {addrinfo}: {errno}", addr, err))
-#define TRACE_SENDTO_KDC_TCP_ERROR_RECV(c, addr, err) \
-    TRACE(c, (c, "TCP error receiving from {addrinfo}: {errno}", addr, err))
-#define TRACE_SENDTO_KDC_TCP_ERROR_RECV_LEN(c, addr, err) \
-    TRACE(c, (c, "TCP error receiving from {addrinfo}: {errno}", addr, err))
-#define TRACE_SENDTO_KDC_TCP_ERROR_SEND(c, addr, err) \
-    TRACE(c, (c, "TCP error sending to {addrinfo}: {errno}", addr, err))
-#define TRACE_SENDTO_KDC_TCP_SEND(c, addr) \
-    TRACE(c, (c, "Sending TCP request to {addrinfo}", addr))
-#define TRACE_SENDTO_KDC_UDP_ERROR_RECV(c, addr, err) \
-    TRACE(c, (c, "UDP error receiving from {addrinfo}: {errno}", addr, err))
-#define TRACE_SENDTO_KDC_UDP_ERROR_SEND_INITIAL(c, addr, err) \
-    TRACE(c, (c, "UDP error sending to {addrinfo}: {errno}", addr, err))
-#define TRACE_SENDTO_KDC_UDP_ERROR_SEND_RETRY(c, addr, err) \
-    TRACE(c, (c, "UDP error sending to {addrinfo}: {errno}", addr, err))
-#define TRACE_SENDTO_KDC_UDP_SEND_INITIAL(c, addr) \
-    TRACE(c, (c, "Sending initial UDP request to {addrinfo}", addr))
-#define TRACE_SENDTO_KDC_UDP_SEND_RETRY(c, addr) \
-    TRACE(c, (c, "Sending retry UDP request to {addrinfo}", addr))
+#define TRACE_SENDTO_KDC_RESOLVING(c, hostname) \
+    TRACE(c, (c, "Resolving hostname {str}", hostname))
+#define TRACE_SENDTO_KDC_RESPONSE(c, conn) \
+    TRACE(c, (c, "Received answer from {connstate}", conn))
+#define TRACE_SENDTO_KDC_TCP_CONNECT(c, conn) \
+    TRACE(c, (c, "Initiating TCP connection to {connstate}", conn))
+#define TRACE_SENDTO_KDC_TCP_DISCONNECT(c, conn) \
+    TRACE(c, (c, "Terminating TCP connection to {connstate}", conn))
+#define TRACE_SENDTO_KDC_TCP_ERROR_CONNECT(c, conn, err) \
+    TRACE(c, (c, "TCP error connecting to {connstate}: {errno}", conn, err))
+#define TRACE_SENDTO_KDC_TCP_ERROR_RECV(c, conn, err) \
+    TRACE(c, (c, "TCP error receiving from {connstate}: {errno}", conn, err))
+#define TRACE_SENDTO_KDC_TCP_ERROR_RECV_LEN(c, conn, err) \
+    TRACE(c, (c, "TCP error receiving from {connstate}: {errno}", conn, err))
+#define TRACE_SENDTO_KDC_TCP_ERROR_SEND(c, conn, err) \
+    TRACE(c, (c, "TCP error sending to {connstate}: {errno}", conn, err))
+#define TRACE_SENDTO_KDC_TCP_SEND(c, conn) \
+    TRACE(c, (c, "Sending TCP request to {connstate}", conn))
+#define TRACE_SENDTO_KDC_UDP_ERROR_RECV(c, conn, err) \
+    TRACE(c, (c, "UDP error receiving from {connstate}: {errno}", conn, err))
+#define TRACE_SENDTO_KDC_UDP_ERROR_SEND_INITIAL(c, conn, err) \
+    TRACE(c, (c, "UDP error sending to {connstate}: {errno}", conn, err))
+#define TRACE_SENDTO_KDC_UDP_ERROR_SEND_RETRY(c, conn, err) \
+    TRACE(c, (c, "UDP error sending to {connstate}: {errno}", conn, err))
+#define TRACE_SENDTO_KDC_UDP_SEND_INITIAL(c, conn) \
+    TRACE(c, (c, "Sending initial UDP request to {connstate}", conn))
+#define TRACE_SENDTO_KDC_UDP_SEND_RETRY(c, conn) \
+    TRACE(c, (c, "Sending retry UDP request to {connstate}", conn))
 
 #define TRACE_SEND_TGS_ETYPES(c, etypes) \
     TRACE(c, (c, "etypes requested in TGS request: {etypes}", etypes))

Modified: trunk/src/lib/krb5/libkrb5.exports
===================================================================
--- trunk/src/lib/krb5/libkrb5.exports	2011-02-13 19:12:36 UTC (rev 24634)
+++ trunk/src/lib/krb5/libkrb5.exports	2011-02-13 21:14:00 UTC (rev 24635)
@@ -107,6 +107,8 @@
 initialize_k5e1_error_table
 initialize_kv5m_error_table
 initialize_prof_error_table
+k5_free_serverlist
+k5_locate_kdc
 k5_plugin_free_modules
 k5_plugin_load
 k5_plugin_load_all
@@ -419,7 +421,6 @@
 krb5_ktf_writable_ops
 krb5_kts_ops
 krb5_kuserok
-krb5_locate_kdc
 krb5_lock_file
 krb5_make_authdata_kdc_issued
 krb5_make_full_ipaddr
@@ -597,7 +598,6 @@
 krb5int_find_authdata
 krb5int_find_pa_data
 krb5int_foreach_localaddr
-krb5int_free_addrlist
 krb5int_free_data_list
 krb5int_get_authdata_containee_types
 krb5int_init_context_kdc

Modified: trunk/src/lib/krb5/os/accessor.c
===================================================================
--- trunk/src/lib/krb5/os/accessor.c	2011-02-13 19:12:36 UTC (rev 24634)
+++ trunk/src/lib/krb5/os/accessor.c	2011-02-13 21:14:00 UTC (rev 24635)
@@ -53,20 +53,8 @@
             krb5int_access internals_temp;
 #endif
             S (arcfour_gsscrypt, krb5int_arcfour_gsscrypt),
-            S (free_addrlist, krb5int_free_addrlist),
             S (auth_con_get_subkey_enctype, krb5_auth_con_get_subkey_enctype),
-            S (sendto_udp, &krb5int_sendto),
-            S (add_host_to_list, krb5int_add_host_to_list),
 
-#ifdef KRB5_DNS_LOOKUP
-#define SC(FIELD, VAL)  S(FIELD, VAL)
-#else /* disable */
-#define SC(FIELD, VAL)  S(FIELD, 0)
-#endif
-            SC (make_srv_query_realm, krb5int_make_srv_query_realm),
-            SC (free_srv_dns_data, krb5int_free_srv_dns_data),
-            SC (use_dns_kdc, _krb5_use_dns_kdc),
-#undef SC
             S (clean_hostname, krb5int_clean_hostname),
 
             S (mandatory_cksumtype, krb5int_c_mandatory_cksumtype),

Modified: trunk/src/lib/krb5/os/changepw.c
===================================================================
--- trunk/src/lib/krb5/os/changepw.c	2011-02-13 19:12:36 UTC (rev 24634)
+++ trunk/src/lib/krb5/os/changepw.c	2011-02-13 21:14:00 UTC (rev 24635)
@@ -59,31 +59,31 @@
 
 static krb5_error_code
 locate_kpasswd(krb5_context context, const krb5_data *realm,
-               struct addrlist *addrlist, krb5_boolean useTcp)
+               struct serverlist *serverlist, int socktype)
 {
     krb5_error_code code;
-    int sockType = (useTcp ? SOCK_STREAM : SOCK_DGRAM);
 
-    code = krb5int_locate_server (context, realm, addrlist,
-                                  locate_service_kpasswd, sockType, AF_UNSPEC);
+    code = k5_locate_server(context, realm, serverlist, locate_service_kpasswd,
+                            socktype);
 
     if (code == KRB5_REALM_CANT_RESOLVE || code == KRB5_REALM_UNKNOWN) {
-        code = krb5int_locate_server (context, realm, addrlist,
-                                      locate_service_kadmin, SOCK_STREAM,
-                                      AF_UNSPEC);
+        code = k5_locate_server(context, realm, serverlist,
+                                locate_service_kadmin, SOCK_STREAM);
         if (!code) {
             /* Success with admin_server but now we need to change the
                port number to use DEFAULT_KPASSWD_PORT and the socktype.  */
             size_t i;
-            for (i=0; i<addrlist->naddrs; i++) {
-                struct addrinfo *a = addrlist->addrs[i].ai;
+            for (i = 0; i < serverlist->nservers; i++) {
+                struct server_entry *s = &serverlist->servers[i];
                 krb5_ui_2 kpasswd_port = htons(DEFAULT_KPASSWD_PORT);
-                if (a->ai_family == AF_INET)
-                    sa2sin (a->ai_addr)->sin_port = kpasswd_port;
-                if (a->ai_family == AF_INET6)
-                    sa2sin6 (a->ai_addr)->sin6_port = kpasswd_port;
-                if (sockType != SOCK_STREAM)
-                    a->ai_socktype = sockType;
+                if (socktype != SOCK_STREAM)
+                    s->socktype = socktype;
+                if (s->hostname != NULL)
+                    s->port = kpasswd_port;
+                else if (s->family == AF_INET)
+                    ss2sin(&s->addr)->sin_port = kpasswd_port;
+                else if (s->family == AF_INET6)
+                    ss2sin6(&s->addr)->sin6_port = kpasswd_port;
             }
         }
     }
@@ -222,7 +222,7 @@
 {
     krb5_data                   chpw_rep;
     krb5_address                remote_kaddr;
-    krb5_boolean                useTcp = 0;
+    krb5_boolean                use_tcp = 0;
     GETSOCKNAME_ARG3_TYPE       addrlen;
     krb5_error_code             code = 0;
     char                        *code_string;
@@ -231,7 +231,7 @@
     struct sendto_callback_context  callback_ctx;
     struct sendto_callback_info callback_info;
     struct sockaddr_storage     remote_addr;
-    struct addrlist             al = ADDRLIST_INIT;
+    struct serverlist           sl = SERVERLIST_INIT;
 
     memset(&chpw_rep, 0, sizeof(krb5_data));
     memset( &callback_ctx, 0, sizeof(struct sendto_callback_context));
@@ -255,10 +255,11 @@
     callback_ctx.local_seq_num = callback_ctx.auth_context->local_seq_number;
 
     do {
+        int socktype = (use_tcp ? SOCK_STREAM : SOCK_DGRAM);
         if ((code = locate_kpasswd(callback_ctx.context,
                                    krb5_princ_realm(callback_ctx.context,
                                                     creds->server),
-                                   &al, useTcp)))
+                                   &sl, socktype)))
             break;
 
         addrlen = sizeof(remote_addr);
@@ -268,20 +269,10 @@
         callback_info.pfn_cleanup = kpasswd_sendto_msg_cleanup;
         krb5_free_data_contents(callback_ctx.context, &chpw_rep);
 
-        if ((code = krb5int_sendto(callback_ctx.context,
-                                   NULL,
-                                   &al,
-                                   &callback_info,
-                                   &chpw_rep,
-                                   NULL,
-                                   NULL,
-                                   ss2sa(&remote_addr),
-                                   &addrlen,
-                                   NULL,
-                                   NULL,
-                                   NULL
-             ))) {
-
+        code = k5_sendto(callback_ctx.context, NULL, &sl, socktype, 0,
+                         &callback_info, &chpw_rep, ss2sa(&remote_addr),
+                         &addrlen, NULL, NULL, NULL);
+        if (code) {
             /*
              * Here we may want to switch to TCP on some errors.
              * right?
@@ -323,9 +314,9 @@
                                        result_string);
 
         if (code) {
-            if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !useTcp ) {
-                krb5int_free_addrlist (&al);
-                useTcp = 1;
+            if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !use_tcp) {
+                k5_free_serverlist(&sl);
+                use_tcp = 1;
                 continue;
             }
 
@@ -356,9 +347,9 @@
             strncpy(result_code_string->data, code_string, result_code_string->length);
         }
 
-        if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !useTcp ) {
-            krb5int_free_addrlist (&al);
-            useTcp = 1;
+        if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !use_tcp) {
+            k5_free_serverlist(&sl);
+            use_tcp = 1;
         } else {
             break;
         }
@@ -368,7 +359,7 @@
     if (callback_ctx.auth_context != NULL)
         krb5_auth_con_free(callback_ctx.context, callback_ctx.auth_context);
 
-    krb5int_free_addrlist (&al);
+    k5_free_serverlist(&sl);
     krb5_free_data_contents(callback_ctx.context, &callback_ctx.ap_req);
     krb5_free_data_contents(callback_ctx.context, &chpw_rep);
 

Modified: trunk/src/lib/krb5/os/hst_realm.c
===================================================================
--- trunk/src/lib/krb5/os/hst_realm.c	2011-02-13 19:12:36 UTC (rev 24634)
+++ trunk/src/lib/krb5/os/hst_realm.c	2011-02-13 21:14:00 UTC (rev 24635)
@@ -450,7 +450,7 @@
                  char **realm, int limit)
 {
     krb5_error_code retval = 0, r;
-    struct addrlist alist;
+    struct serverlist slist;
     krb5_data drealm;
     char *cp = NULL, *fqdn, *dot;
 
@@ -479,9 +479,9 @@
         drealm.data = cp;
 
         /* Find a kdc based on this part of the domain name. */
-        r = krb5_locate_kdc(context, &drealm, &alist, 0, SOCK_DGRAM, 0);
+        r = k5_locate_kdc(context, &drealm, &slist, FALSE, SOCK_DGRAM);
         if (!r) { /* Found a KDC! */
-            krb5int_free_addrlist(&alist);
+            k5_free_serverlist(&slist);
             *realm = strdup(cp);
             if (!*realm) {
                 retval = ENOMEM;

Modified: trunk/src/lib/krb5/os/locate_kdc.c
===================================================================
--- trunk/src/lib/krb5/os/locate_kdc.c	2011-02-13 19:12:36 UTC (rev 24634)
+++ trunk/src/lib/krb5/os/locate_kdc.c	2011-02-13 21:14:00 UTC (rev 24635)
@@ -25,7 +25,7 @@
  * or implied warranty.
  *
  *
- * get socket addresses for KDC.
+ * Get server hostnames or addresses for KDC.
  */
 
 #include "fake-addrinfo.h"
@@ -101,85 +101,18 @@
 
 #endif /* KRB5_DNS_LOOKUP */
 
-int
-krb5int_grow_addrlist (struct addrlist *lp, int nmore)
-{
-    size_t i;
-    size_t newspace = lp->space + nmore;
-    size_t newsize = newspace * sizeof (*lp->addrs);
-    void *newaddrs;
-
-    newaddrs = realloc (lp->addrs, newsize);
-    if (newaddrs == NULL)
-        return ENOMEM;
-    lp->addrs = newaddrs;
-    for (i = lp->space; i < newspace; i++) {
-        lp->addrs[i].ai = NULL;
-        lp->addrs[i].freefn = NULL;
-        lp->addrs[i].data = NULL;
-    }
-    lp->space = newspace;
-    return 0;
-}
-#define grow_list krb5int_grow_addrlist
-
-/* Free up everything pointed to by the addrlist structure, but don't
+/* Free up everything pointed to by the serverlist structure, but don't
    free the structure itself.  */
 void
-krb5int_free_addrlist (struct addrlist *lp)
+k5_free_serverlist (struct serverlist *list)
 {
     size_t i;
-    for (i = 0; i < lp->naddrs; i++)
-        if (lp->addrs[i].freefn)
-            (lp->addrs[i].freefn)(lp->addrs[i].data);
-    free (lp->addrs);
-    lp->addrs = NULL;
-    lp->naddrs = lp->space = 0;
-}
-#define free_list krb5int_free_addrlist
 
-static int
-translate_ai_error (int err)
-{
-    switch (err) {
-    case 0:
-        return 0;
-    case EAI_BADFLAGS:
-    case EAI_FAMILY:
-    case EAI_SOCKTYPE:
-    case EAI_SERVICE:
-        /* All of these indicate bad inputs to getaddrinfo.  */
-        return EINVAL;
-    case EAI_AGAIN:
-        /* Translate to standard errno code.  */
-        return EAGAIN;
-    case EAI_MEMORY:
-        /* Translate to standard errno code.  */
-        return ENOMEM;
-#ifdef EAI_ADDRFAMILY
-    case EAI_ADDRFAMILY:
-#endif
-#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
-    case EAI_NODATA:
-#endif
-    case EAI_NONAME:
-        /* Name not known or no address data, but no error.  Do
-           nothing more.  */
-        return 0;
-#ifdef EAI_OVERFLOW
-    case EAI_OVERFLOW:
-        /* An argument buffer overflowed.  */
-        return EINVAL;          /* XXX */
-#endif
-#ifdef EAI_SYSTEM
-    case EAI_SYSTEM:
-        /* System error, obviously.  */
-        return errno;
-#endif
-    default:
-        /* An error code we haven't handled?  */
-        return EINVAL;
-    }
+    for (i = 0; i < list->nservers; i++)
+        free(list->servers[i].hostname);
+    free(list->servers);
+    list->servers = NULL;
+    list->nservers = 0;
 }
 
 #include <stdarg.h>
@@ -197,120 +130,70 @@
 #if 0
 extern void krb5int_debug_fprint(const char *, ...);
 #define dprint krb5int_debug_fprint
-#define print_addrlist krb5int_print_addrlist
-extern void print_addrlist (const struct addrlist *a);
 #else
 static inline void dprint(const char *fmt, ...) { }
-static inline void print_addrlist(const struct addrlist *a) { }
 #endif
 
-static int
-add_addrinfo_to_list(struct addrlist *lp, struct addrinfo *a,
-                     void (*freefn)(void *), void *data)
+/* Make room for a new server entry in list and return a pointer to the new
+ * entry.  (Do not increment list->nservers.) */
+static struct server_entry *
+new_server_entry(struct serverlist *list)
 {
-    int err;
+    struct server_entry *newservers, *entry;
+    size_t newspace = (list->nservers + 1) * sizeof(struct server_entry);
 
-    dprint("\tadding %p=%A to %p (naddrs=%d space=%d)\n", a, a, lp,
-           lp->naddrs, lp->space);
-
-    if (lp->naddrs == lp->space) {
-        err = grow_list (lp, 1);
-        if (err) {
-            Tprintf ("grow_list failed %d\n", err);
-            return err;
-        }
-    }
-    Tprintf("setting element %d\n", lp->naddrs);
-    lp->addrs[lp->naddrs].ai = a;
-    lp->addrs[lp->naddrs].freefn = freefn;
-    lp->addrs[lp->naddrs].data = data;
-    lp->naddrs++;
-    Tprintf ("\tcount is now %lu: ", (unsigned long) lp->naddrs);
-    print_addrlist(lp);
-    Tprintf("\n");
-    return 0;
+    newservers = realloc(list->servers, newspace);
+    if (newservers == NULL)
+        return NULL;
+    list->servers = newservers;
+    entry = &newservers[list->nservers];
+    memset(entry, 0, sizeof(*entry));
+    return entry;
 }
 
-#define add_host_to_list krb5int_add_host_to_list
-
-static void
-call_freeaddrinfo(void *data)
+/* Add an address entry to list. */
+static int
+add_addr_to_list(struct serverlist *list, int socktype, int family,
+                 size_t addrlen, struct sockaddr *addr)
 {
-    /* Strict interpretation of the C standard says we can't assume
-       that the ABI for f(void*) and f(struct foo *) will be
-       compatible.  Use this stub just to be paranoid.  */
-    freeaddrinfo(data);
+    struct server_entry *entry;
+
+    entry = new_server_entry(list);
+    if (entry == NULL)
+        return ENOMEM;
+    entry->socktype = socktype;
+    entry->family = family;
+    entry->hostname = NULL;
+    entry->addrlen = addrlen;
+    memcpy(&entry->addr, addr, addrlen);
+    list->nservers++;
+    return 0;
 }
 
-int
-krb5int_add_host_to_list (struct addrlist *lp, const char *hostname,
-                          int port, int secport,
-                          int socktype, int family)
+/* Add a hostname entry to list. */
+static int
+add_host_to_list(struct serverlist *list, const char *hostname, int port,
+                 int socktype, int family)
 {
-    struct addrinfo *addrs, *a, *anext, hint;
-    int err, result;
-    char portbuf[10], secportbuf[10];
-    void (*freefn)(void *);
+    struct server_entry *entry;
 
-    Tprintf ("adding hostname %s, ports %d,%d, family %d, socktype %d\n",
-             hostname, ntohs (port), ntohs (secport),
-             family, socktype);
-
-    memset(&hint, 0, sizeof(hint));
-    hint.ai_family = family;
-    hint.ai_socktype = socktype;
-#ifdef AI_NUMERICSERV
-    hint.ai_flags = AI_NUMERICSERV;
-#endif
-    result = snprintf(portbuf, sizeof(portbuf), "%d", ntohs(port));
-    if (SNPRINTF_OVERFLOW(result, sizeof(portbuf)))
-        /* XXX */
-        return EINVAL;
-    result = snprintf(secportbuf, sizeof(secportbuf), "%d", ntohs(secport));
-    if (SNPRINTF_OVERFLOW(result, sizeof(secportbuf)))
-        return EINVAL;
-    err = getaddrinfo (hostname, portbuf, &hint, &addrs);
-    if (err) {
-        Tprintf ("\tgetaddrinfo(\"%s\", \"%s\", ...)\n\treturns %d: %s\n",
-                 hostname, portbuf, err, gai_strerror (err));
-        return translate_ai_error (err);
-    }
-    freefn = call_freeaddrinfo;
-    anext = 0;
-    for (a = addrs; a != 0 && err == 0; a = anext, freefn = 0) {
-        anext = a->ai_next;
-        err = add_addrinfo_to_list (lp, a, freefn, a);
-    }
-    if (err || secport == 0)
-        goto egress;
-    if (socktype == 0)
-        socktype = SOCK_DGRAM;
-    else if (socktype != SOCK_DGRAM)
-        goto egress;
-    hint.ai_family = AF_INET;
-    err = getaddrinfo (hostname, secportbuf, &hint, &addrs);
-    if (err) {
-        err = translate_ai_error (err);
-        goto egress;
-    }
-    freefn = call_freeaddrinfo;
-    for (a = addrs; a != 0 && err == 0; a = anext, freefn = 0) {
-        anext = a->ai_next;
-        err = add_addrinfo_to_list (lp, a, freefn, a);
-    }
-egress:
-    /* XXX Memory leaks possible here if add_addrinfo_to_list fails.  */
-    return err;
+    entry = new_server_entry(list);
+    if (entry == NULL)
+        return ENOMEM;
+    entry->socktype = socktype;
+    entry->family = family;
+    entry->hostname = strdup(hostname);
+    if (entry->hostname == NULL)
+        return ENOMEM;
+    entry->port = port;
+    list->nservers++;
+    return 0;
 }
 
-/*
- * returns count of number of addresses found
- */
-
 static krb5_error_code
 locate_srv_conf_1(krb5_context context, const krb5_data *realm,
-                       const char * name, struct addrlist *addrlist,
-                       int socktype, int udpport, int sec_udpport, int family)
+                       const char * name, struct serverlist *serverlist,
+                       int socktype, int udpport, int sec_udpport)
 {
     const char  *realm_srv_names[4];
     char **hostlist, *host, *port, *cp;
@@ -350,15 +233,10 @@
 
     if (count == 0) {
         profile_free_list(hostlist);
-        addrlist->naddrs = 0;
+        serverlist->nservers = 0;
         return 0;
     }
 
-#ifdef HAVE_NETINET_IN_H
-    if (sec_udpport)
-        count = count * 2;
-#endif
-
     for (i=0; hostlist[i]; i++) {
         int p1, p2;
 
@@ -398,43 +276,33 @@
             *cp = '\0';
         }
 
-        if (socktype != 0)
-            code = add_host_to_list(addrlist, host, p1, p2, socktype, family);
-        else {
-            code = add_host_to_list(addrlist, host, p1, p2, SOCK_DGRAM,
-                                    family);
-            if (code == 0)
-                code = add_host_to_list(addrlist, host, p1, p2, SOCK_STREAM,
-                                        family);
-        }
-        if (code) {
-            Tprintf ("error %d (%s) returned from add_host_to_list\n", code,
-                     error_message (code));
-            if (hostlist)
-                profile_free_list (hostlist);
-            return code;
-        }
+        code = add_host_to_list(serverlist, host, p1, socktype, AF_UNSPEC);
+        /* Second port is for IPv4 UDP only, and should possibly go away as
+         * it was originally a krb4 compatibility measure. */
+        if (code == 0 && p2 != 0 &&
+            (socktype == 0 || socktype == SOCK_DGRAM))
+            code = add_host_to_list(serverlist, host, p2, SOCK_DGRAM, AF_INET);
+        if (code)
+            goto cleanup;
     }
 
-    if (hostlist)
-        profile_free_list(hostlist);
-
-    return 0;
+cleanup:
+    profile_free_list(hostlist);
+    return code;
 }
 
 #ifdef TEST
 static krb5_error_code
 krb5_locate_srv_conf(krb5_context context, const krb5_data *realm,
-                     const char *name, struct addrlist *al, int udpport,
+                     const char *name, struct serverlist *al, int udpport,
                      int sec_udpport)
 {
     krb5_error_code ret;
 
-    ret = locate_srv_conf_1(context, realm, name, al, 0, udpport,
-                            sec_udpport, 0);
+    ret = locate_srv_conf_1(context, realm, name, al, 0, udpport, sec_udpport);
     if (ret)
         return ret;
-    if (al->naddrs == 0)        /* Couldn't resolve any KDC names */
+    if (al->nservers == 0)        /* Couldn't resolve any KDC names */
         return KRB5_REALM_CANT_RESOLVE;
     return 0;
 }
@@ -442,56 +310,35 @@
 
 #ifdef KRB5_DNS_LOOKUP
 static krb5_error_code
-locate_srv_dns_1 (const krb5_data *realm,
-                       const char *service,
-                       const char *protocol,
-                       struct addrlist *addrlist,
-                       int family)
+locate_srv_dns_1(const krb5_data *realm, const char *service,
+                 const char *protocol, struct serverlist *serverlist)
 {
-    struct srv_dns_entry *head = NULL;
-    struct srv_dns_entry *entry = NULL, *next;
+    struct srv_dns_entry *head = NULL, *entry = NULL;
     krb5_error_code code = 0;
+    int socktype;
 
     code = krb5int_make_srv_query_realm(realm, service, protocol, &head);
     if (code)
         return 0;
 
-    /*
-     * Okay!  Now we've got a linked list of entries sorted by
-     * priority.  Start looking up A records and returning
-     * addresses.
-     */
-
     if (head == NULL)
         return 0;
 
     /* Check for the "." case indicating no support.  */
-    if (head->next == 0 && head->host[0] == 0) {
-        free(head->host);
-        free(head);
-        return KRB5_ERR_NO_SERVICE;
+    if (head->next == NULL && head->host[0] == '\0') {
+        code = KRB5_ERR_NO_SERVICE;
+        goto cleanup;
     }
 
-    Tprintf ("walking answer list:\n");
-    for (entry = head; entry != NULL; entry = next) {
-        Tprintf ("\tport=%d host=%s\n", entry->port, entry->host);
-        next = entry->next;
-        code = add_host_to_list (addrlist, entry->host, htons (entry->port), 0,
-                                 (strcmp("_tcp", protocol)
-                                  ? SOCK_DGRAM
-                                  : SOCK_STREAM), family);
-        if (code) {
-            break;
-        }
-        if (entry == head) {
-            free(entry->host);
-            free(entry);
-            head = next;
-            entry = 0;
-        }
+    for (entry = head; entry != NULL; entry = entry->next) {
+        socktype = (strcmp(protocol, "_tcp") == 0) ? SOCK_STREAM : SOCK_DGRAM;
+        code = add_host_to_list(serverlist, entry->host, htons(entry->port),
+                                socktype, AF_UNSPEC);
+        if (code)
+            goto cleanup;
     }
-    Tprintf ("[end]\n");
 
+cleanup:
     krb5int_free_srv_dns_data(head);
     return code;
 }
@@ -507,50 +354,27 @@
 
 struct module_callback_data {
     int out_of_mem;
-    struct addrlist *lp;
+    struct serverlist *list;
 };
 
 static int
-module_callback (void *cbdata, int socktype, struct sockaddr *sa)
+module_callback(void *cbdata, int socktype, struct sockaddr *sa)
 {
     struct module_callback_data *d = cbdata;
-    struct {
-        struct addrinfo ai;
-        union {
-            struct sockaddr_in sin;
-#ifdef KRB5_USE_INET6
-            struct sockaddr_in6 sin6;
-#endif
-        } u;
-    } *x;
+    size_t addrlen;
 
     if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
         return 0;
-    if (sa->sa_family != AF_INET
+    if (sa->sa_family == AF_INET)
+        addrlen = sizeof(struct sockaddr_in);
 #ifdef KRB5_USE_INET6
-        && sa->sa_family != AF_INET6
+    else if (sa->sa_family == AF_INET6)
+        addrlen = sizeof(struct sockaddr_in6);
 #endif
-    )
+    else
         return 0;
-    x = calloc (1, sizeof (*x));
-    if (x == 0) {
-        d->out_of_mem = 1;
-        return 1;
-    }
-    x->ai.ai_addr = (struct sockaddr *) &x->u;
-    x->ai.ai_socktype = socktype;
-    x->ai.ai_family = sa->sa_family;
-    if (sa->sa_family == AF_INET) {
-        x->u.sin = *(struct sockaddr_in *)sa;
-        x->ai.ai_addrlen = sizeof(struct sockaddr_in);
-    }
-#ifdef KRB5_USE_INET6
-    if (sa->sa_family == AF_INET6) {
-        x->u.sin6 = *(struct sockaddr_in6 *)sa;
-        x->ai.ai_addrlen = sizeof(struct sockaddr_in6);
-    }
-#endif
-    if (add_addrinfo_to_list (d->lp, &x->ai, free, x) != 0) {
+    if (add_addr_to_list(d->list, socktype, sa->sa_family, addrlen,
+                         sa) != 0) {
         /* Assumes only error is ENOMEM.  */
         d->out_of_mem = 1;
         return 1;
@@ -559,9 +383,9 @@
 }
 
 static krb5_error_code
-module_locate_server (krb5_context ctx, const krb5_data *realm,
-                      struct addrlist *addrlist,
-                      enum locate_service_type svc, int socktype, int family)
+module_locate_server(krb5_context ctx, const krb5_data *realm,
+                     struct serverlist *serverlist,
+                     enum locate_service_type svc, int socktype)
 {
     struct krb5plugin_service_locate_result *res = NULL;
     krb5_error_code code;
@@ -573,7 +397,7 @@
     const char *msg;
 
     Tprintf("in module_locate_server\n");
-    cbdata.lp = addrlist;
+    cbdata.list = serverlist;
     if (!PLUGIN_DIR_OPEN (&ctx->libkrb5_plugins)) {
 
         code = krb5int_open_plugin_dirs (objdirs, NULL, &ctx->libkrb5_plugins,
@@ -614,8 +438,16 @@
         if (code)
             continue;
 
-        code = vtbl->lookup(blob, svc, realmz, socktype, family,
+        code = vtbl->lookup(blob, svc, realmz,
+                            (socktype != 0) ? socktype : SOCK_DGRAM, AF_UNSPEC,
                             module_callback, &cbdata);
+        /* Also ask for TCP addresses if we got UDP addresses and want both. */
+        if (code == 0 && socktype == 0) {
+            code = vtbl->lookup(blob, svc, realmz, SOCK_STREAM, AF_UNSPEC,
+                                module_callback, &cbdata);
+            if (code == KRB5_PLUGIN_NO_HANDLE)
+                code = 0;
+        }
         vtbl->fini(blob);
         if (code == KRB5_PLUGIN_NO_HANDLE) {
             /* Module passes, keep going.  */
@@ -643,17 +475,16 @@
 
     /* Got something back, yippee.  */
     Tprintf("now have %lu addrs in list %p\n",
-            (unsigned long) addrlist->naddrs, addrlist);
-    print_addrlist(addrlist);
+            (unsigned long) serverlist->nservers, serverlist);
     free(realmz);
     krb5int_free_plugin_dir_data (ptrs);
     return 0;
 }
 
 static krb5_error_code
-prof_locate_server (krb5_context context, const krb5_data *realm,
-                    struct addrlist *addrlist,
-                    enum locate_service_type svc, int socktype, int family)
+prof_locate_server(krb5_context context, const krb5_data *realm,
+                   struct serverlist *serverlist, enum locate_service_type svc,
+                   int socktype)
 {
     const char *profname;
     int dflport1, dflport2 = 0;
@@ -689,14 +520,14 @@
         return EBUSY;           /* XXX */
     }
 
-    return locate_srv_conf_1(context, realm, profname, addrlist, socktype,
-                             dflport1, dflport2, family);
+    return locate_srv_conf_1(context, realm, profname, serverlist, socktype,
+                             dflport1, dflport2);
 }
 
 static krb5_error_code
-dns_locate_server (krb5_context context, const krb5_data *realm,
-                   struct addrlist *addrlist,
-                   enum locate_service_type svc, int socktype, int family)
+dns_locate_server(krb5_context context, const krb5_data *realm,
+                  struct serverlist *serverlist, enum locate_service_type svc,
+                  int socktype)
 {
     const char *dnsname;
     int use_dns = _krb5_use_dns_kdc(context);
@@ -727,12 +558,12 @@
 
     code = 0;
     if (socktype == SOCK_DGRAM || socktype == 0) {
-        code = locate_srv_dns_1(realm, dnsname, "_udp", addrlist, family);
+        code = locate_srv_dns_1(realm, dnsname, "_udp", serverlist);
         if (code)
             Tprintf("dns udp lookup returned error %d\n", code);
     }
     if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) {
-        code = locate_srv_dns_1(realm, dnsname, "_tcp", addrlist, family);
+        code = locate_srv_dns_1(realm, dnsname, "_tcp", serverlist);
         if (code)
             Tprintf("dns tcp lookup returned error %d\n", code);
     }
@@ -744,15 +575,14 @@
  */
 
 krb5_error_code
-krb5int_locate_server (krb5_context context, const krb5_data *realm,
-                       struct addrlist *addrlist,
-                       enum locate_service_type svc,
-                       int socktype, int family)
+k5_locate_server(krb5_context context, const krb5_data *realm,
+                 struct serverlist *serverlist, enum locate_service_type svc,
+                 int socktype)
 {
     krb5_error_code code;
-    struct addrlist al = ADDRLIST_INIT;
+    struct serverlist al = SERVERLIST_INIT;
 
-    *addrlist = al;
+    *serverlist = al;
 
     if (realm == NULL || realm->data == NULL || realm->data[0] == 0) {
         krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE,
@@ -760,7 +590,7 @@
         return KRB5_REALM_CANT_RESOLVE;
     }
 
-    code = module_locate_server(context, realm, &al, svc, socktype, family);
+    code = module_locate_server(context, realm, &al, svc, socktype);
     Tprintf("module_locate_server returns %d\n", code);
     if (code == KRB5_PLUGIN_NO_HANDLE) {
         /*
@@ -769,13 +599,12 @@
          * config file.
          */
 
-        code = prof_locate_server(context, realm, &al, svc, socktype, family);
+        code = prof_locate_server(context, realm, &al, svc, socktype);
 
 #ifdef KRB5_DNS_LOOKUP
         if (code) {             /* Try DNS for all profile errors?  */
             krb5_error_code code2;
-            code2 = dns_locate_server(context, realm, &al, svc, socktype,
-                                      family);
+            code2 = dns_locate_server(context, realm, &al, svc, socktype);
             if (code2 != KRB5_PLUGIN_NO_HANDLE)
                 code = code2;
         }
@@ -786,36 +615,31 @@
     }
     if (code == 0)
         Tprintf ("krb5int_locate_server found %d addresses\n",
-                 al.naddrs);
+                 al.nservers);
     else
         Tprintf ("krb5int_locate_server returning error code %d/%s\n",
                  code, error_message(code));
     if (code != 0) {
-        if (al.space)
-            free_list (&al);
+        k5_free_serverlist(&al);
         return code;
     }
-    if (al.naddrs == 0) {       /* No good servers */
-        if (al.space)
-            free_list (&al);
+    if (al.nservers == 0) {       /* No good servers */
+        k5_free_serverlist(&al);
         krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE,
-                               "Cannot resolve network address for KDC in realm \"%.*s\"",
-                               realm->length, realm->data);
-
+                               "Cannot resolve servers for KDC in "
+                               "realm \"%.*s\"", realm->length, realm->data);
         return KRB5_REALM_CANT_RESOLVE;
     }
-    *addrlist = al;
+    *serverlist = al;
     return 0;
 }
 
 krb5_error_code
-krb5_locate_kdc(krb5_context context, const krb5_data *realm,
-                struct addrlist *addrlist,
-                int get_masters, int socktype, int family)
+k5_locate_kdc(krb5_context context, const krb5_data *realm,
+              struct serverlist *serverlist, int get_masters, int socktype)
 {
-    return krb5int_locate_server(context, realm, addrlist,
-                                 (get_masters
-                                  ? locate_service_master_kdc
-                                  : locate_service_kdc),
-                                 socktype, family);
+    enum locate_service_type stype;
+
+    stype = get_masters ? locate_service_master_kdc : locate_service_kdc;
+    return k5_locate_server(context, realm, serverlist, stype, socktype);
 }

Modified: trunk/src/lib/krb5/os/os-proto.h
===================================================================
--- trunk/src/lib/krb5/os/os-proto.h	2011-02-13 19:12:36 UTC (rev 24634)
+++ trunk/src/lib/krb5/os/os-proto.h	2011-02-13 21:14:00 UTC (rev 24635)
@@ -31,10 +31,35 @@
 #ifndef KRB5_LIBOS_INT_PROTO__
 #define KRB5_LIBOS_INT_PROTO__
 
-struct addrlist;
-krb5_error_code krb5_locate_kdc(krb5_context, const krb5_data *,
-                                struct addrlist *, int, int, int);
+#include <krb5/locate_plugin.h>
 
+/* A single server hostname or address. */
+struct server_entry {
+    char *hostname;             /* NULL -> use addrlen/addr instead */
+    int port;                   /* Used only if hostname set */
+    int socktype;               /* May be 0 for UDP/TCP if hostname set */
+    int family;                 /* May be 0 (aka AF_UNSPEC) if hostname set */
+    size_t addrlen;
+    struct sockaddr_storage addr;
+};
+
+/* A list of server hostnames/addresses. */
+struct serverlist {
+    struct server_entry *servers;
+    size_t nservers;
+};
+#define SERVERLIST_INIT { NULL, 0 }
+
+krb5_error_code k5_locate_server(krb5_context, const krb5_data *realm,
+                                 struct serverlist *,
+                                 enum locate_service_type svc, int socktype);
+
+krb5_error_code k5_locate_kdc(krb5_context context, const krb5_data *realm,
+                              struct serverlist *serverlist, int get_masters,
+                              int socktype);
+
+void k5_free_serverlist(struct serverlist *);
+
 #ifdef HAVE_NETINET_IN_H
 krb5_error_code krb5_unpack_full_ipaddr(krb5_context,
                                         const krb5_address *,
@@ -60,15 +85,15 @@
 int _krb5_use_dns_kdc (krb5_context);
 int _krb5_conf_boolean (const char *);
 
-krb5_error_code krb5int_sendto(krb5_context context, const krb5_data *message,
-                               const struct addrlist *addrs,
-                               struct sendto_callback_info* callback_info,
-                               krb5_data *reply, struct sockaddr *localaddr,
-                               socklen_t *localaddrlen,
-                               struct sockaddr *remoteaddr, socklen_t *remoteaddrlen,
-                               int *addr_used,
-                               int (*msg_handler)(krb5_context, const krb5_data *, void *),
-                               void *msg_handler_data);
+krb5_error_code k5_sendto(krb5_context context, const krb5_data *message,
+                          const struct serverlist *addrs,
+                          int socktype1, int socktype2,
+                          struct sendto_callback_info *callback_info,
+                          krb5_data *reply, struct sockaddr *remoteaddr,
+                          socklen_t *remoteaddrlen, int *server_used,
+                          int (*msg_handler)(krb5_context, const krb5_data *,
+                                             void *),
+                          void *msg_handler_data);
 
 krb5_error_code krb5int_get_fq_local_hostname(char *, size_t);
 

Modified: trunk/src/lib/krb5/os/sendto_kdc.c
===================================================================
--- trunk/src/lib/krb5/os/sendto_kdc.c	2011-02-13 19:12:36 UTC (rev 24634)
+++ trunk/src/lib/krb5/os/sendto_kdc.c	2011-02-13 21:14:00 UTC (rev 24635)
@@ -253,61 +253,21 @@
 #endif
 }
 
-#define print_addrlist krb5int_print_addrlist
-static void
-print_addrlist (const struct addrlist *a)
-{
-    size_t i;
-    dprint("%d{", a->naddrs);
-    for (i = 0; i < a->naddrs; i++)
-        dprint("%s%p=%A", i ? "," : "", (void*)a->addrs[i].ai, a->addrs[i].ai);
-    dprint("}");
-}
-
 static int
-merge_addrlists (struct addrlist *dest, struct addrlist *src)
+in_addrlist(struct server_entry *entry, struct serverlist *list)
 {
-    /* Wouldn't it be nice if we could filter out duplicates?  The
-       alloc/free handling makes that pretty difficult though.  */
-    int err;
     size_t i;
+    struct server_entry *le;
 
-    dprint("merging addrlists:\n\tlist1: ");
-    for (i = 0; i < dest->naddrs; i++)
-        dprint(" %A", dest->addrs[i].ai);
-    dprint("\n\tlist2: ");
-    for (i = 0; i < src->naddrs; i++)
-        dprint(" %A", src->addrs[i].ai);
-    dprint("\n");
-
-    err = krb5int_grow_addrlist (dest, src->naddrs);
-    if (err)
-        return err;
-    for (i = 0; i < src->naddrs; i++) {
-        dest->addrs[dest->naddrs + i] = src->addrs[i];
-        src->addrs[i].ai = 0;
-        src->addrs[i].freefn = 0;
-    }
-    dest->naddrs += i;
-    src->naddrs = 0;
-
-    dprint("\tout:   ");
-    for (i = 0; i < dest->naddrs; i++)
-        dprint(" %A", dest->addrs[i].ai);
-    dprint("\n");
-
-    return 0;
-}
-
-static int
-in_addrlist (struct addrinfo *thisaddr, struct addrlist *list)
-{
-    size_t i;
-    for (i = 0; i < list->naddrs; i++) {
-        if (thisaddr->ai_addrlen == list->addrs[i].ai->ai_addrlen
-            && !memcmp(thisaddr->ai_addr, list->addrs[i].ai->ai_addr,
-                       thisaddr->ai_addrlen))
+    for (i = 0; i < list->nservers; i++) {
+        le = &list->servers[i];
+        if (entry->hostname != NULL && le->hostname != NULL &&
+            strcmp(entry->hostname, le->hostname) == 0)
             return 1;
+        if (entry->hostname == NULL && le->hostname == NULL &&
+            entry->addrlen == le->addrlen &&
+            memcmp(&entry->addr, &le->addr, entry->addrlen) == 0)
+            return 1;
     }
     return 0;
 }
@@ -348,13 +308,13 @@
  */
 
 krb5_error_code
-krb5_sendto_kdc (krb5_context context, const krb5_data *message,
-                 const krb5_data *realm, krb5_data *reply,
-                 int *use_master, int tcp_only)
+krb5_sendto_kdc(krb5_context context, const krb5_data *message,
+                const krb5_data *realm, krb5_data *reply, int *use_master,
+                int tcp_only)
 {
-    krb5_error_code retval, retval2;
-    struct addrlist addrs;
-    int socktype1 = 0, socktype2 = 0, addr_used;
+    krb5_error_code retval, err;
+    struct serverlist servers;
+    int socktype1 = 0, socktype2 = 0, server_used;
 
     /*
      * find KDC location(s) for realm
@@ -390,8 +350,6 @@
         context->udp_pref_limit = tmp;
     }
 
-    retval = (*use_master ? KRB5_KDC_UNREACH : KRB5_REALM_UNKNOWN);
-
     if (tcp_only)
         socktype1 = SOCK_STREAM, socktype2 = 0;
     else if (message->length <= (unsigned int) context->udp_pref_limit)
@@ -399,69 +357,44 @@
     else
         socktype1 = SOCK_STREAM, socktype2 = SOCK_DGRAM;
 
-    retval = krb5_locate_kdc(context, realm, &addrs, *use_master, socktype1, 0);
-    if (socktype2) {
-        struct addrlist addrs2;
+    retval = k5_locate_kdc(context, realm, &servers, *use_master,
+                           tcp_only ? SOCK_STREAM : 0);
+    if (retval)
+        return retval;
 
-        retval2 = krb5_locate_kdc(context, realm, &addrs2, *use_master,
-                                  socktype2, 0);
-#if 0
-        if (retval2 == 0) {
-            (void) merge_addrlists(&addrs, &addrs2);
-            krb5int_free_addrlist(&addrs2);
-            retval = 0;
-        } else if (retval == KRB5_REALM_CANT_RESOLVE) {
-            retval = retval2;
+    retval = k5_sendto(context, message, &servers, socktype1, socktype2,
+                       NULL, reply, NULL, NULL, &server_used,
+                       check_for_svc_unavailable, &err);
+    if (retval == KRB5_KDC_UNREACH) {
+        if (err == KDC_ERR_SVC_UNAVAILABLE) {
+            retval = KRB5KDC_ERR_SVC_UNAVAILABLE;
+        } else {
+            krb5_set_error_message(context, retval,
+                                   "Cannot contact any KDC for realm '%.*s'",
+                                   realm->length, realm->data);
         }
-#else
-        retval = retval2;
-        if (retval == 0) {
-            (void) merge_addrlists(&addrs, &addrs2);
-            krb5int_free_addrlist(&addrs2);
-        }
-#endif
     }
+    if (retval)
+        goto cleanup;
 
-    if (addrs.naddrs > 0) {
-        krb5_error_code err = 0;
-
-        retval = krb5int_sendto (context, message, &addrs, 0, reply, 0, 0,
-                                 0, 0, &addr_used, check_for_svc_unavailable, &err);
-        switch (retval) {
-        case 0:
-            /*
-             * Set use_master to 1 if we ended up talking to a master when
-             * we didn't explicitly request to
-             */
-            if (*use_master == 0) {
-                struct addrlist addrs3;
-                retval = krb5_locate_kdc(context, realm, &addrs3, 1,
-                                         addrs.addrs[addr_used].ai->ai_socktype,
-                                         addrs.addrs[addr_used].ai->ai_family);
-                if (retval == 0) {
-                    if (in_addrlist(addrs.addrs[addr_used].ai, &addrs3))
-                        *use_master = 1;
-                    krb5int_free_addrlist (&addrs3);
-                }
-                TRACE_SENDTO_KDC_MASTER(context, *use_master);
-            }
-            krb5int_free_addrlist (&addrs);
-            return 0;
-        default:
-            break;
-            /* Cases here are for constructing useful error messages.  */
-        case KRB5_KDC_UNREACH:
-            if (err == KDC_ERR_SVC_UNAVAILABLE) {
-                retval = KRB5KDC_ERR_SVC_UNAVAILABLE;
-            } else {
-                krb5_set_error_message(context, retval,
-                                       "Cannot contact any KDC for realm '%.*s'",
-                                       realm->length, realm->data);
-            }
-            break;
+    /* Set use_master to 1 if we ended up talking to a master when we didn't
+     * explicitly request to. */
+    if (*use_master == 0) {
+        struct serverlist mservers;
+        struct server_entry *entry = &servers.servers[server_used];
+        retval = k5_locate_kdc(context, realm, &mservers, TRUE,
+                               entry->socktype);
+        if (retval == 0) {
+            if (in_addrlist(entry, &mservers))
+                *use_master = 1;
+            k5_free_serverlist(&mservers);
         }
-        krb5int_free_addrlist (&addrs);
+        TRACE_SENDTO_KDC_MASTER(context, *use_master);
+        retval = 0;
     }
+
+cleanup:
+    k5_free_serverlist(&servers);
     return retval;
 }
 
@@ -604,17 +537,24 @@
     }
 }
 
-
-
-static void
-setup_connection (struct conn_state *state, struct addrinfo *ai,
-                  const krb5_data *message, char **udpbufp)
+static krb5_error_code
+add_connection(struct conn_state **conns, struct addrinfo *ai,
+               size_t server_index, const krb5_data *message, char **udpbufp)
 {
+    struct conn_state *state, **tailptr;
+
+    state = calloc(1, sizeof(*state));
+    if (state == NULL)
+        return ENOMEM;
     state->state = INITIALIZING;
     state->err = 0;
     state->x.out.sgp = state->x.out.sgbuf;
-    state->addr = ai;
+    state->socktype = ai->ai_socktype;
+    state->family = ai->ai_family;
+    state->addrlen = ai->ai_addrlen;
+    memcpy(&state->addr, ai->ai_addr, ai->ai_addrlen);
     state->fd = INVALID_SOCKET;
+    state->server_index = server_index;
     SG_SET(&state->x.out.sgbuf[1], 0, 0);
     if (ai->ai_socktype == SOCK_STREAM) {
         /*
@@ -637,34 +577,139 @@
         state->service = service_udp_fd;
         set_conn_state_msg_length (state, message);
 
-        if (*udpbufp == 0) {
+        if (*udpbufp == NULL) {
             *udpbufp = malloc(krb5_max_dgram_size);
-            if (*udpbufp == 0) {
-                dperror("malloc(krb5_max_dgram_size)");
-                state->state = FAILED;
-                return;
-            }
+            if (*udpbufp == 0)
+                return ENOMEM;
         }
         state->x.in.buf = *udpbufp;
         state->x.in.bufsize = krb5_max_dgram_size;
     }
+
+    /* Chain the new state onto the tail of the list. */
+    for (tailptr = conns; *tailptr != NULL; tailptr = &(*tailptr)->next);
+    *tailptr = state;
+
+    return 0;
 }
 
 static int
+translate_ai_error (int err)
+{
+    switch (err) {
+    case 0:
+        return 0;
+    case EAI_BADFLAGS:
+    case EAI_FAMILY:
+    case EAI_SOCKTYPE:
+    case EAI_SERVICE:
+        /* All of these indicate bad inputs to getaddrinfo.  */
+        return EINVAL;
+    case EAI_AGAIN:
+        /* Translate to standard errno code.  */
+        return EAGAIN;
+    case EAI_MEMORY:
+        /* Translate to standard errno code.  */
+        return ENOMEM;
+#ifdef EAI_ADDRFAMILY
+    case EAI_ADDRFAMILY:
+#endif
+#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
+    case EAI_NODATA:
+#endif
+    case EAI_NONAME:
+        /* Name not known or no address data, but no error.  Do
+           nothing more.  */
+        return 0;
+#ifdef EAI_OVERFLOW
+    case EAI_OVERFLOW:
+        /* An argument buffer overflowed.  */
+        return EINVAL;          /* XXX */
+#endif
+#ifdef EAI_SYSTEM
+    case EAI_SYSTEM:
+        /* System error, obviously.  */
+        return errno;
+#endif
+    default:
+        /* An error code we haven't handled?  */
+        return EINVAL;
+    }
+}
+
+/*
+ * Resolve the entry in servers with index ind, adding connections to the list
+ * *conns.  Connections are added for each of socktype1 and (if not zero)
+ * socktype2.  message and udpbufp are used to initialize the connections; see
+ * add_connection above.  If no addresses are available for an entry but no
+ * internal name resolution failure occurs, return 0 without adding any new
+ * connections.
+ */
+static krb5_error_code
+resolve_server(krb5_context context, const struct serverlist *servers,
+               size_t ind, int socktype1, int socktype2,
+               const krb5_data *message, char **udpbufp,
+               struct conn_state **conns)
+{
+    krb5_error_code retval;
+    struct server_entry *entry = &servers->servers[ind];
+    struct addrinfo *addrs, *a, hint, ai;
+    int err, result;
+    char portbuf[64];
+
+    /* Skip any stray entries of socktypes we don't want. */
+    if (entry->socktype != 0 && entry->socktype != socktype1 &&
+        entry->socktype != socktype2)
+        return 0;
+
+    if (entry->hostname == NULL) {
+        ai.ai_socktype = entry->socktype;
+        ai.ai_family = entry->family;
+        ai.ai_addrlen = entry->addrlen;
+        ai.ai_addr = (struct sockaddr *)&entry->addr;
+        return add_connection(conns, &ai, ind, message, udpbufp);
+    }
+
+    memset(&hint, 0, sizeof(hint));
+    hint.ai_family = entry->family;
+    hint.ai_socktype = (entry->socktype != 0) ? entry->socktype : socktype1;
+#ifdef AI_NUMERICSERV
+    hint.ai_flags = AI_NUMERICSERV;
+#endif
+    result = snprintf(portbuf, sizeof(portbuf), "%d", ntohs(entry->port));
+    if (SNPRINTF_OVERFLOW(result, sizeof(portbuf)))
+        return EINVAL;
+    TRACE_SENDTO_KDC_RESOLVING(context, entry->hostname);
+    err = getaddrinfo(entry->hostname, portbuf, &hint, &addrs);
+    if (err)
+        return translate_ai_error(err);
+    /* Add each address with the preferred socktype. */
+    retval = 0;
+    for (a = addrs; a != 0 && retval == 0; a = a->ai_next)
+        retval = add_connection(conns, a, ind, message, udpbufp);
+    if (retval == 0 && entry->socktype == 0 && socktype2 != 0) {
+        /* Add each address again with the non-preferred socktype. */
+        for (a = addrs; a != 0 && retval == 0; a = a->ai_next) {
+            a->ai_socktype = socktype2;
+            retval = add_connection(conns, a, ind, message, udpbufp);
+        }
+    }
+    return retval;
+}
+
+static int
 start_connection(krb5_context context, struct conn_state *state,
                  struct select_state *selstate,
-                 struct sendto_callback_info *callback_info,
-                 krb5_data *callback_buffer)
+                 struct sendto_callback_info *callback_info)
 {
     int fd, e;
-    struct addrinfo *ai = state->addr;
 
     dprint("start_connection(@%p)\ngetting %s socket in family %d...", state,
-           ai->ai_socktype == SOCK_STREAM ? "stream" : "dgram", ai->ai_family);
-    fd = socket(ai->ai_family, ai->ai_socktype, 0);
+           state->socktype == SOCK_STREAM ? "stream" : "dgram", state->family);
+    fd = socket(state->family, state->socktype, 0);
     if (fd == INVALID_SOCKET) {
         state->err = SOCKET_ERRNO;
-        dprint("socket: %m creating with af %d\n", state->err, ai->ai_family);
+        dprint("socket: %m creating with af %d\n", state->err, state->family);
         return -1;              /* try other hosts */
     }
 #ifndef _WIN32 /* On Windows FD_SETSIZE is a count, not a max value.  */
@@ -677,7 +722,7 @@
 #endif
     set_cloexec_fd(fd);
     /* Make it non-blocking.  */
-    if (ai->ai_socktype == SOCK_STREAM) {
+    if (state->socktype == SOCK_STREAM) {
         static const int one = 1;
         static const struct linger lopt = { 0, 0 };
 
@@ -685,12 +730,11 @@
             dperror("sendto_kdc: ioctl(FIONBIO)");
         if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt)))
             dperror("sendto_kdc: setsockopt(SO_LINGER)");
-        TRACE_SENDTO_KDC_TCP_CONNECT(context, ai);
+        TRACE_SENDTO_KDC_TCP_CONNECT(context, state);
     }
 
     /* Start connecting to KDC.  */
-    dprint(" fd %d; connecting to %A...\n", fd, ai);
-    e = connect(fd, ai->ai_addr, ai->ai_addrlen);
+    e = connect(fd, (struct sockaddr *)&state->addr, state->addrlen);
     if (e != 0) {
         /*
          * This is the path that should be followed for non-blocking
@@ -724,9 +768,8 @@
      */
     if (callback_info) {
 
-        e = callback_info->pfn_callback(state,
-                                        callback_info->context,
-                                        callback_buffer);
+        e = callback_info->pfn_callback(state, callback_info->context,
+                                        &state->callback_buffer);
         if (e != 0) {
             dprint("callback failed: %m\n", e);
             (void) closesocket(fd);
@@ -736,24 +779,20 @@
             return -3;
         }
 
-        dprint("callback %p (message=%d@%p)\n",
-               state,
-               callback_buffer->length,
-               callback_buffer->data);
-
-        set_conn_state_msg_length( state, callback_buffer );
+        set_conn_state_msg_length(state, &state->callback_buffer);
     }
 
-    if (ai->ai_socktype == SOCK_DGRAM) {
+    if (state->socktype == SOCK_DGRAM) {
         /* Send it now.  */
         ssize_t ret;
         sg_buf *sg = &state->x.out.sgbuf[0];
 
-        TRACE_SENDTO_KDC_UDP_SEND_INITIAL(context, ai);
+        TRACE_SENDTO_KDC_UDP_SEND_INITIAL(context, state);
         dprint("sending %d bytes on fd %d\n", SG_LEN(sg), state->fd);
         ret = send(state->fd, SG_BUF(sg), SG_LEN(sg), 0);
         if (ret < 0 || (size_t) ret != SG_LEN(sg)) {
-            TRACE_SENDTO_KDC_UDP_ERROR_SEND_INITIAL(context, ai, SOCKET_ERRNO);
+            TRACE_SENDTO_KDC_UDP_ERROR_SEND_INITIAL(context, state,
+                                                    SOCKET_ERRNO);
             dperror("sendto");
             (void) closesocket(state->fd);
             state->fd = INVALID_SOCKET;
@@ -763,21 +802,6 @@
             state->state = READING;
         }
     }
-#ifdef DEBUG
-    if (debug) {
-        struct sockaddr_storage ss;
-        socklen_t sslen = sizeof(ss);
-        if (getsockname(state->fd, (struct sockaddr *)&ss, &sslen) == 0) {
-            struct addrinfo hack_ai;
-            memset(&hack_ai, 0, sizeof(hack_ai));
-            hack_ai.ai_addr = (struct sockaddr *) &ss;
-            hack_ai.ai_addrlen = sslen;
-            hack_ai.ai_socktype = SOCK_DGRAM;
-            hack_ai.ai_family = ai->ai_family;
-            dprint("local socket address is %A\n", &hack_ai);
-        }
-    }
-#endif
     FD_SET(state->fd, &selstate->rfds);
     if (state->state == CONNECTING || state->state == WRITING)
         FD_SET(state->fd, &selstate->wfds);
@@ -799,8 +823,7 @@
 static int
 maybe_send(krb5_context context, struct conn_state *conn,
            struct select_state *selstate,
-           struct sendto_callback_info *callback_info,
-           krb5_data *callback_buffer)
+           struct sendto_callback_info *callback_info)
 {
     sg_buf *sg;
     ssize_t ret;
@@ -808,10 +831,8 @@
     dprint("maybe_send(@%p) state=%s type=%s\n", conn,
            state_strings[conn->state],
            conn->is_udp ? "udp" : "tcp");
-    if (conn->state == INITIALIZING) {
-        return start_connection(context, conn, selstate, callback_info,
-                                callback_buffer);
-    }
+    if (conn->state == INITIALIZING)
+        return start_connection(context, conn, selstate, callback_info);
 
     /* Did we already shut down this channel?  */
     if (conn->state == FAILED) {
@@ -819,7 +840,7 @@
         return -1;
     }
 
-    if (conn->addr->ai_socktype == SOCK_STREAM) {
+    if (conn->socktype == SOCK_STREAM) {
         dprint("skipping stream socket\n");
         /* The select callback will handle flushing any data we
            haven't written yet, and we only write it once.  */
@@ -828,12 +849,11 @@
 
     /* UDP - retransmit after a previous attempt timed out. */
     sg = &conn->x.out.sgbuf[0];
-    TRACE_SENDTO_KDC_UDP_SEND_RETRY(context, conn->addr);
+    TRACE_SENDTO_KDC_UDP_SEND_RETRY(context, conn);
     dprint("sending %d bytes on fd %d\n", SG_LEN(sg), conn->fd);
     ret = send(conn->fd, SG_BUF(sg), SG_LEN(sg), 0);
     if (ret < 0 || (size_t) ret != SG_LEN(sg)) {
-        TRACE_SENDTO_KDC_UDP_ERROR_SEND_INITIAL(context, conn->addr,
-                                                SOCKET_ERRNO);
+        TRACE_SENDTO_KDC_UDP_ERROR_SEND_RETRY(context, conn, SOCKET_ERRNO);
         dperror("send");
         /* Keep connection alive, we'll try again next pass.
 
@@ -907,7 +927,7 @@
             /* Bad -- the KDC shouldn't be sending to us first.  */
             e = EINVAL /* ?? */;
         kill_conn:
-            TRACE_SENDTO_KDC_TCP_DISCONNECT(context, conn->addr);
+            TRACE_SENDTO_KDC_TCP_DISCONNECT(context, conn);
             kill_conn(conn, selstate, e);
             if (e == EINVAL) {
                 closesocket(conn->fd);
@@ -936,7 +956,7 @@
          */
         e = get_so_error(conn->fd);
         if (e) {
-            TRACE_SENDTO_KDC_TCP_ERROR_CONNECT(context, conn->addr, e);
+            TRACE_SENDTO_KDC_TCP_ERROR_CONNECT(context, conn, e);
             dprint("socket error on write fd: %m", e);
             goto kill_conn;
         }
@@ -958,12 +978,12 @@
                ((conn->x.out.sg_count == 2 ? SG_LEN(&conn->x.out.sgp[1]) : 0)
                 + SG_LEN(&conn->x.out.sgp[0])),
                conn->fd);
-        TRACE_SENDTO_KDC_TCP_SEND(context, conn->addr);
+        TRACE_SENDTO_KDC_TCP_SEND(context, conn);
         nwritten = SOCKET_WRITEV(conn->fd, conn->x.out.sgp,
                                  conn->x.out.sg_count, tmp);
         if (nwritten < 0) {
             e = SOCKET_ERRNO;
-            TRACE_SENDTO_KDC_TCP_ERROR_SEND(context, conn->addr, e);
+            TRACE_SENDTO_KDC_TCP_ERROR_SEND(context, conn, e);
             dprint("failed: %m\n", e);
             goto kill_conn;
         }
@@ -1018,7 +1038,7 @@
                 e = nread ? SOCKET_ERRNO : ECONNRESET;
                 free(conn->x.in.buf);
                 conn->x.in.buf = 0;
-                TRACE_SENDTO_KDC_TCP_ERROR_RECV(context, conn->addr, e);
+                TRACE_SENDTO_KDC_TCP_ERROR_RECV(context, conn, e);
                 goto kill_conn;
             }
             conn->x.in.n_left -= nread;
@@ -1033,7 +1053,7 @@
                                 conn->x.in.bufsizebytes + conn->x.in.bufsizebytes_read,
                                 4 - conn->x.in.bufsizebytes_read);
             if (nread < 0) {
-                TRACE_SENDTO_KDC_TCP_ERROR_RECV_LEN(context, conn->addr, e);
+                TRACE_SENDTO_KDC_TCP_ERROR_RECV_LEN(context, conn, e);
                 e = SOCKET_ERRNO;
                 goto kill_conn;
             }
@@ -1078,7 +1098,7 @@
 
     nread = recv(conn->fd, conn->x.in.buf, conn->x.in.bufsize, 0);
     if (nread < 0) {
-        TRACE_SENDTO_KDC_UDP_ERROR_RECV(context, conn->addr, SOCKET_ERRNO);
+        TRACE_SENDTO_KDC_UDP_ERROR_RECV(context, conn, SOCKET_ERRNO);
         kill_conn(conn, selstate, SOCKET_ERRNO);
         return 0;
     }
@@ -1086,16 +1106,24 @@
     return 1;
 }
 
-static int
-service_fds (krb5_context context,
-             struct select_state *selstate,
-             struct conn_state *conns, size_t n_conns, int *winning_conn,
-             struct select_state *seltemp,
-             int (*msg_handler)(krb5_context, const krb5_data *, void *),
-             void *msg_handler_data)
+static krb5_boolean
+service_fds(krb5_context context, struct select_state *selstate, int interval,
+            struct conn_state *conns, struct select_state *seltemp,
+            int (*msg_handler)(krb5_context, const krb5_data *, void *),
+            void *msg_handler_data, struct conn_state **winner_out)
 {
     int e, selret;
+    struct timeval now;
+    struct conn_state *state;
 
+    *winner_out = NULL;
+
+    e = getcurtime(&now);
+    if (e)
+        return 1;
+    selstate->end_time = now;
+    selstate->end_time.tv_sec += interval;
+
     e = 0;
     while (selstate->nfds > 0) {
         unsigned int i;
@@ -1113,53 +1141,43 @@
             return 0;
 
         /* Got something on a socket, process it.  */
-        for (i = 0; i <= (unsigned int)selstate->max && selret > 0 && i < n_conns; i++) {
+        for (state = conns; state != NULL; state = state->next) {
             int ssflags;
 
-            if (conns[i].fd == INVALID_SOCKET)
+            if (state->fd == INVALID_SOCKET)
                 continue;
             ssflags = 0;
-            if (FD_ISSET(conns[i].fd, &seltemp->rfds))
-                ssflags |= SSF_READ, selret--;
-            if (FD_ISSET(conns[i].fd, &seltemp->wfds))
-                ssflags |= SSF_WRITE, selret--;
-            if (FD_ISSET(conns[i].fd, &seltemp->xfds))
-                ssflags |= SSF_EXCEPTION, selret--;
+            if (FD_ISSET(state->fd, &seltemp->rfds))
+                ssflags |= SSF_READ;
+            if (FD_ISSET(state->fd, &seltemp->wfds))
+                ssflags |= SSF_WRITE;
+            if (FD_ISSET(state->fd, &seltemp->xfds))
+                ssflags |= SSF_EXCEPTION;
             if (!ssflags)
                 continue;
 
-            dprint("handling flags '%s%s%s' on fd %d (%A) in state %s\n",
-                   (ssflags & SSF_READ) ? "r" : "",
-                   (ssflags & SSF_WRITE) ? "w" : "",
-                   (ssflags & SSF_EXCEPTION) ? "x" : "",
-                   conns[i].fd, conns[i].addr,
-                   state_strings[(int) conns[i].state]);
-
-            if (conns[i].service(context, &conns[i], selstate, ssflags)) {
+            if (state->service(context, state, selstate, ssflags)) {
                 int stop = 1;
 
                 if (msg_handler != NULL) {
                     krb5_data reply;
 
-                    reply.data = conns[i].x.in.buf;
-                    reply.length = conns[i].x.in.pos - conns[i].x.in.buf;
+                    reply.data = state->x.in.buf;
+                    reply.length = state->x.in.pos - state->x.in.buf;
 
                     stop = (msg_handler(context, &reply, msg_handler_data) != 0);
                 }
 
                 if (stop) {
                     dprint("fd service routine says we're done\n");
-                    *winning_conn = i;
+                    *winner_out = state;
                     return 1;
                 }
             }
         }
     }
-    if (e != 0) {
-        dprint("select returned %m\n", e);
-        *winning_conn = -1;
+    if (e != 0)
         return 1;
-    }
     return 0;
 }
 
@@ -1186,59 +1204,34 @@
  */
 
 krb5_error_code
-krb5int_sendto (krb5_context context, const krb5_data *message,
-                const struct addrlist *addrs,
-                struct sendto_callback_info* callback_info, krb5_data *reply,
-                struct sockaddr *localaddr, socklen_t *localaddrlen,
-                struct sockaddr *remoteaddr, socklen_t *remoteaddrlen,
-                int *addr_used,
-                /* return 0 -> keep going, 1 -> quit */
-                int (*msg_handler)(krb5_context, const krb5_data *, void *),
-                void *msg_handler_data)
+k5_sendto(krb5_context context, const krb5_data *message,
+          const struct serverlist *servers, int socktype1, int socktype2,
+          struct sendto_callback_info* callback_info, krb5_data *reply,
+          struct sockaddr *remoteaddr, socklen_t *remoteaddrlen,
+          int *server_used,
+          /* return 0 -> keep going, 1 -> quit */
+          int (*msg_handler)(krb5_context, const krb5_data *, void *),
+          void *msg_handler_data)
 {
-    int pass;
-    int delay_this_pass = 2;
+    int pass, delay;
     krb5_error_code retval;
-    struct conn_state *conns = NULL;
-    krb5_data *callback_data = NULL;
-    size_t i, n_conns = 0, host;
-    struct select_state *sel_state = NULL;
-    struct timeval now;
-    int winning_conn = -1, e = 0;
+    struct conn_state *conns = NULL, *state, **tailptr, *next, *winner;
+    size_t s;
+    struct select_state *sel_state = NULL, *seltemp;
     char *udpbuf = NULL;
+    krb5_boolean done = FALSE;
 
-    if (message)
-        dprint("krb5int_sendto(message=%d@%p, addrlist=", message->length, message->data);
-    else
-        dprint("krb5int_sendto(callback=%p, addrlist=", callback_info);
-    print_addrlist(addrs);
-    dprint(")\n");
-
     reply->data = 0;
     reply->length = 0;
 
-    conns = calloc(addrs->naddrs, sizeof(struct conn_state));
-    if (conns == NULL)
-        return ENOMEM;
-
-    if (callback_info) {
-        callback_data = calloc(addrs->naddrs, sizeof(krb5_data));
-        if (callback_data == NULL) {
-            retval = ENOMEM;
-            goto egress;
-        }
-    }
-
-    for (i = 0; i < addrs->naddrs; i++)
-        conns[i].fd = INVALID_SOCKET;
-
     /* One for use here, listing all our fds in use, and one for
-       temporary use in service_fds, for the fds of interest.  */
+     * temporary use in service_fds, for the fds of interest.  */
     sel_state = malloc(2 * sizeof(*sel_state));
     if (sel_state == NULL) {
         retval = ENOMEM;
-        goto egress;
+        goto cleanup;
     }
+    seltemp = &sel_state[1];
     sel_state->max = 0;
     sel_state->nfds = 0;
     sel_state->end_time.tv_sec = sel_state->end_time.tv_usec = 0;
@@ -1246,100 +1239,93 @@
     FD_ZERO(&sel_state->wfds);
     FD_ZERO(&sel_state->xfds);
 
+    /* First pass: resolve server hosts, communicate with resulting addresses
+     * of the preferred socktype, and wait 1s for an answer from each. */
+    for (s = 0; s < servers->nservers && !done; s++) {
+        /* Find the current tail pointer. */
+        for (tailptr = &conns; *tailptr != NULL; tailptr = &(*tailptr)->next);
+        retval = resolve_server(context, servers, s, socktype1, socktype2,
+                                message, &udpbuf, &conns);
+        if (retval)
+            goto cleanup;
+        for (state = *tailptr; state != NULL && !done; state = state->next) {
+            /* Contact each new connection whose socktype matches socktype1. */
+            if (state->socktype != socktype1)
+                continue;
+            if (maybe_send(context, state, sel_state, callback_info))
+                continue;
+            done = service_fds(context, sel_state, 1, conns, seltemp,
+                               msg_handler, msg_handler_data, &winner);
+        }
+    }
 
-    /* Set up connections.  */
-    for (host = 0; host < addrs->naddrs; host++) {
-        setup_connection(&conns[host], addrs->addrs[host].ai, message,
-                         &udpbuf);
+    /* Complete the first pass by contacting servers of the non-preferred
+     * socktype (if given), waiting 1s for an answer from each. */
+    for (state = conns; state != NULL && !done; state = state->next) {
+        if (state->socktype != socktype2)
+            continue;
+        if (maybe_send(context, state, sel_state, callback_info))
+            continue;
+        done = service_fds(context, sel_state, 1, state, seltemp, msg_handler,
+                           msg_handler_data, &winner);
     }
-    n_conns = addrs->naddrs;
-    for (pass = 0; pass < MAX_PASS; pass++) {
-        /* Possible optimization: Make only one pass if TCP only.
-           Stop making passes if all UDP ports are closed down.  */
-        dprint("pass %d delay=%d\n", pass, delay_this_pass);
-        for (host = 0; host < n_conns; host++) {
-            dprint("host %d\n", host);
 
-            /* Send to the host, wait for a response, then move on. */
-            if (maybe_send(context, &conns[host], sel_state, callback_info,
-                           (callback_info ? &callback_data[host] : NULL)))
-                continue;
+    /* Wait for two seconds at the end of the first pass. */
+    if (!done) {
+        done = service_fds(context, sel_state, 2, conns, seltemp, msg_handler,
+                           msg_handler_data, &winner);
+    }
 
-            retval = getcurtime(&now);
-            if (retval)
-                goto egress;
-            sel_state->end_time = now;
-            sel_state->end_time.tv_sec += 1;
-            e = service_fds(context, sel_state, conns, host+1, &winning_conn,
-                            sel_state+1, msg_handler, msg_handler_data);
-            if (e)
+    /* Make remaining passes over all of the connections. */
+    delay = 4;
+    for (pass = 1; pass < MAX_PASS && !done; pass++) {
+        for (state = conns; state != NULL && !done; state = state->next) {
+            if (maybe_send(context, state, sel_state, callback_info))
+                continue;
+            done = service_fds(context, sel_state, 1, conns, seltemp,
+                               msg_handler, msg_handler_data, &winner);
+            if (sel_state->nfds == 0)
                 break;
-            if (pass > 0 && sel_state->nfds == 0)
-                /*
-                 * After the first pass, if we close all fds, break
-                 * out right away.  During the first pass, it's okay,
-                 * we're probably about to open another connection.
-                 */
-                break;
         }
-        if (e)
-            break;
-        retval = getcurtime(&now);
-        if (retval)
-            goto egress;
-        /* Possible optimization: Find a way to integrate this select
-           call with the last one from the above loop, if the loop
-           actually calls select.  */
-        sel_state->end_time.tv_sec += delay_this_pass;
-        e = service_fds(context, sel_state, conns, host+1, &winning_conn,
-                        sel_state+1, msg_handler, msg_handler_data);
-        if (e)
-            break;
+        /* Wait for the delay backoff at the end of this pass. */
+        if (!done) {
+            done = service_fds(context, sel_state, delay, conns, seltemp,
+                               msg_handler, msg_handler_data, &winner);
+        }
         if (sel_state->nfds == 0)
             break;
-        delay_this_pass *= 2;
+        delay *= 2;
     }
 
-    if (sel_state->nfds == 0) {
-        /* No addresses?  */
+    if (sel_state->nfds == 0 || !done || winner == NULL) {
         retval = KRB5_KDC_UNREACH;
-        goto egress;
+        goto cleanup;
     }
-    if (e == 0 || winning_conn < 0) {
-        retval = KRB5_KDC_UNREACH;
-        goto egress;
-    }
     /* Success!  */
-    TRACE_SENDTO_KDC_RESPONSE(context, conns[winning_conn].addr);
-    reply->data = conns[winning_conn].x.in.buf;
-    reply->length = (conns[winning_conn].x.in.pos
-                     - conns[winning_conn].x.in.buf);
-    dprint("returning %d bytes in buffer %p\n",
-           (int) reply->length, reply->data);
+    TRACE_SENDTO_KDC_RESPONSE(context, winner);
+    reply->data = winner->x.in.buf;
+    reply->length = winner->x.in.pos - winner->x.in.buf;
     retval = 0;
-    conns[winning_conn].x.in.buf = 0;
-    if (addr_used)
-        *addr_used = winning_conn;
-    if (localaddr != 0 && localaddrlen != 0 && *localaddrlen > 0)
-        (void) getsockname(conns[winning_conn].fd, localaddr, localaddrlen);
+    winner->x.in.buf = NULL;
+    if (server_used != NULL)
+        *server_used = winner->server_index;
+    if (remoteaddr != NULL && remoteaddrlen != 0 && *remoteaddrlen > 0)
+        (void)getpeername(winner->fd, remoteaddr, remoteaddrlen);
 
-    if (remoteaddr != 0 && remoteaddrlen != 0 && *remoteaddrlen > 0)
-        (void) getpeername(conns[winning_conn].fd, remoteaddr, remoteaddrlen);
-
-egress:
-    for (i = 0; i < n_conns; i++) {
-        if (conns[i].fd != INVALID_SOCKET)
-            closesocket(conns[i].fd);
-        if (conns[i].state == READING && conns[i].x.in.buf != udpbuf)
-            free(conns[i].x.in.buf);
+cleanup:
+    for (state = conns; state != NULL; state = next) {
+        next = state->next;
+        if (state->fd != INVALID_SOCKET)
+            closesocket(state->fd);
+        if (state->state == READING && state->x.in.buf != udpbuf)
+            free(state->x.in.buf);
         if (callback_info) {
             callback_info->pfn_cleanup(callback_info->context,
-                                       &callback_data[i]);
+                                       &state->callback_buffer);
         }
+        free(state);
     }
 
-    free(callback_data);
-    free(conns);
     if (reply->data != udpbuf)
         free(udpbuf);
     free(sel_state);

Modified: trunk/src/lib/krb5/os/t_locate_kdc.c
===================================================================
--- trunk/src/lib/krb5/os/t_locate_kdc.c	2011-02-13 19:12:36 UTC (rev 24634)
+++ trunk/src/lib/krb5/os/t_locate_kdc.c	2011-02-13 21:14:00 UTC (rev 24635)
@@ -19,7 +19,7 @@
 
 const char *prog;
 
-struct addrlist al;
+struct serverlist sl;
 
 static void
 kfatal (krb5_error_code err)
@@ -48,25 +48,29 @@
 static void
 print_addrs (void)
 {
-    int i;
+    size_t i;
+    int err;
 
-    int naddrs = al.naddrs;
+    printf("%d servers:\n", (int)sl.nservers);
+    for (i = 0; i < sl.nservers; i++) {
+        struct server_entry *entry = &sl.servers[i];
+        char hostbuf[NI_MAXHOST], srvbuf[NI_MAXSERV];
 
-    printf ("%d addresses:\n", naddrs);
-    for (i = 0; i < naddrs; i++) {
-        int err;
-        struct addrinfo *ai = al.addrs[i].ai;
-        char hostbuf[NI_MAXHOST], srvbuf[NI_MAXSERV];
-        err = getnameinfo (ai->ai_addr, ai->ai_addrlen,
-                           hostbuf, sizeof (hostbuf),
-                           srvbuf, sizeof (srvbuf),
-                           NI_NUMERICHOST | NI_NUMERICSERV);
-        if (err)
-            printf ("%2d: getnameinfo returns error %d=%s\n",
-                    i, err, gai_strerror (err));
-        else
-            printf ("%2d: address %s\t%s\tport %s\n", i, hostbuf,
-                    stypename (ai->ai_socktype), srvbuf);
+        if (entry->hostname != NULL) {
+            printf("%2d: host %s\t%s\tport %d\n", (int)i, entry->hostname,
+                   stypename(entry->socktype), ntohs(entry->port));
+            continue;
+        }
+        err = getnameinfo((struct sockaddr *)&entry->addr, entry->addrlen,
+                          hostbuf, sizeof(hostbuf), srvbuf, sizeof(srvbuf),
+                          NI_NUMERICHOST | NI_NUMERICSERV);
+        if (err) {
+            printf("%2d: getnameinfo returns error %d=%s\n", (int)i, err,
+                   gai_strerror(err));
+        } else {
+            printf("%2d: address %s\t%s\tport %s\n", (int)i, hostbuf,
+                   stypename(entry->socktype), srvbuf);
+        }
     }
 }
 
@@ -116,22 +120,22 @@
 
     switch (how) {
     case LOOKUP_CONF:
-        err = krb5_locate_srv_conf (ctx, &realm, "kdc", &al,
-                                    htons (88), htons (750));
+        err = krb5_locate_srv_conf(ctx, &realm, "kdc", &sl,
+                                   htons(88), htons(750));
         break;
 
     case LOOKUP_DNS:
-        err = locate_srv_dns_1 (&realm, "_kerberos", "_udp", &al, 0);
+        err = locate_srv_dns_1(&realm, "_kerberos", "_udp", &sl);
         break;
 
     case LOOKUP_WHATEVER:
-        err = krb5_locate_kdc (ctx, &realm, &al, master, 0, 0);
+        err = k5_locate_kdc(ctx, &realm, &sl, master, 0);
         break;
     }
     if (err) kfatal (err);
-    print_addrs ();
+    print_addrs();
 
-    krb5int_free_addrlist (&al);
-    krb5_free_context (ctx);
+    k5_free_serverlist(&sl);
+    krb5_free_context(ctx);
     return 0;
 }

Modified: trunk/src/lib/krb5/os/t_std_conf.c
===================================================================
--- trunk/src/lib/krb5/os/t_std_conf.c	2011-02-13 19:12:36 UTC (rev 24634)
+++ trunk/src/lib/krb5/os/t_std_conf.c	2011-02-13 21:14:00 UTC (rev 24635)
@@ -105,27 +105,30 @@
 static void
 test_locate_kdc(krb5_context ctx, char *realm)
 {
-    struct addrlist addrs;
-    int     i;
-    int     get_masters=0;
+    struct serverlist servers;
+    size_t i;
+    int get_masters = FALSE;
     krb5_data rlm;
     krb5_error_code retval;
 
     rlm.data = realm;
     rlm.length = strlen(realm);
-    retval = krb5_locate_kdc(ctx, &rlm, &addrs, get_masters, 0, 0);
+    retval = k5_locate_kdc(ctx, &rlm, &servers, get_masters, 0);
     if (retval) {
         com_err("krb5_locate_kdc", retval, 0);
         return;
     }
     printf("krb_locate_kdc(%s) returned:", realm);
-    for (i=0; i < addrs.naddrs; i++) {
-        struct addrinfo *ai = addrs.addrs[i].ai;
-        switch (ai->ai_family) {
+    for (i = 0; i < servers.nservers; i++) {
+        struct server_entry *entry = &servers.servers[i];
+        if (entry->hostname) {
+            printf(" host:%s/%d", entry->hostname, ntohs(entry->port));
+            continue;
+        }
+        switch (entry->family) {
         case AF_INET:
         {
-            struct sockaddr_in *s_sin;
-            s_sin = (struct sockaddr_in *) ai->ai_addr;
+            struct sockaddr_in *s_sin = (struct sockaddr_in *)&entry->addr;
             printf(" inet:%s/%d", inet_ntoa(s_sin->sin_addr),
                    ntohs(s_sin->sin_port));
         }
@@ -133,9 +136,8 @@
 #ifdef KRB5_USE_INET6
         case AF_INET6:
         {
-            struct sockaddr_in6 *s_sin6;
+            struct sockaddr_in6 *s_sin6 = (struct sockaddr_in6 *)&entry->addr;
             int j;
-            s_sin6 = (struct sockaddr_in6 *) ai->ai_addr;
             printf(" inet6");
             for (j = 0; j < 8; j++)
                 printf(":%x",
@@ -146,11 +148,11 @@
         }
 #endif
         default:
-            printf(" unknown-af-%d", ai->ai_family);
+            printf(" unknown-af-%d", entry->family);
             break;
         }
     }
-    krb5int_free_addrlist(&addrs);
+    k5_free_serverlist(&servers);
     printf("\n");
 }
 

Modified: trunk/src/lib/krb5/os/trace.c
===================================================================
--- trunk/src/lib/krb5/os/trace.c	2011-02-13 19:12:36 UTC (rev 24634)
+++ trunk/src/lib/krb5/os/trace.c	2011-02-13 21:14:00 UTC (rev 24635)
@@ -40,6 +40,7 @@
  */
 
 #include "k5-int.h"
+#include "cm.h"
 
 #ifndef DISABLE_TRACING
 
@@ -71,7 +72,7 @@
     krb5_error_code kerr;
     size_t len, i;
     int err;
-    struct addrinfo *ai;
+    struct conn_state *cs;
     const krb5_data *d;
     krb5_data data;
     char addrbuf[NI_MAXHOST], portbuf[NI_MAXSERV], tmpbuf[200], *str;
@@ -136,24 +137,24 @@
                     krb5int_buf_add(&buf, str);
                 free(str);
             }
-        } else if (strcmp(tmpbuf, "addrinfo") == 0) {
-	    ai = va_arg(ap, struct addrinfo *);
-	    if (ai->ai_socktype == SOCK_DGRAM)
-		krb5int_buf_add(&buf, "dgram");
-	    else if (ai->ai_socktype == SOCK_STREAM)
-		krb5int_buf_add(&buf, "stream");
-	    else
-		krb5int_buf_add_fmt(&buf, "socktype%d", ai->ai_socktype);
+        } else if (strcmp(tmpbuf, "connstate") == 0) {
+            cs = va_arg(ap, struct conn_state *);
+            if (cs->socktype == SOCK_DGRAM)
+                krb5int_buf_add(&buf, "dgram");
+            else if (cs->socktype == SOCK_STREAM)
+                krb5int_buf_add(&buf, "stream");
+            else
+                krb5int_buf_add_fmt(&buf, "socktype%d", cs->socktype);
 
-	    if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
+            if (getnameinfo((struct sockaddr *)&cs->addr, cs->addrlen,
                             addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf),
                             NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
-		if (ai->ai_addr->sa_family == AF_UNSPEC)
-		    krb5int_buf_add(&buf, " AF_UNSPEC");
-		else
-		    krb5int_buf_add_fmt(&buf, " af%d", ai->ai_addr->sa_family);
-	    } else
-		krb5int_buf_add_fmt(&buf, " %s:%s", addrbuf, portbuf);
+                if (cs->family == AF_UNSPEC)
+                    krb5int_buf_add(&buf, " AF_UNSPEC");
+                else
+                    krb5int_buf_add_fmt(&buf, " af%d", cs->family);
+            } else
+                krb5int_buf_add_fmt(&buf, " %s:%s", addrbuf, portbuf);
         } else if (strcmp(tmpbuf, "data") == 0) {
 	    d = va_arg(ap, krb5_data *);
             if (d == NULL || (d->length != 0 && d->data == NULL))




More information about the cvs-krb5 mailing list