Defer hostname lookups in krb5_sendto_kdc
authorGreg Hudson <ghudson@mit.edu>
Sun, 13 Feb 2011 21:14:00 +0000 (21:14 +0000)
committerGreg Hudson <ghudson@mit.edu>
Sun, 13 Feb 2011 21:14:00 +0000 (21:14 +0000)
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.

ticket: 6868

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@24635 dc483132-0cff-0310-8789-dd5450dbe970

13 files changed:
src/include/cm.h
src/include/k5-int.h
src/include/k5-trace.h
src/lib/krb5/libkrb5.exports
src/lib/krb5/os/accessor.c
src/lib/krb5/os/changepw.c
src/lib/krb5/os/hst_realm.c
src/lib/krb5/os/locate_kdc.c
src/lib/krb5/os/os-proto.h
src/lib/krb5/os/sendto_kdc.c
src/lib/krb5/os/t_locate_kdc.c
src/lib/krb5/os/t_std_conf.c
src/lib/krb5/os/trace.c

index cf5ff222bcf020472fd9ff514373ab52bae00e90..2bb5ca98057bf6ae140f74f8915fc906c10768d0 100644 (file)
@@ -62,7 +62,10 @@ struct conn_state {
     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 @@ struct conn_state {
         } out;
         struct incoming_krb5_message in;
     } x;
+    krb5_data callback_buffer;
+    size_t server_index;
+    struct conn_state *next;
 };
 
 struct sendto_callback_info {
index 92ff1f0435923fc6b8a3d5bc424115c3a7a18e8d..adcc97eb2cf1a62c83294df064ae20db59beaa23 100644 (file)
@@ -590,7 +590,6 @@ extern char *strdup (const char *);
 
 #include "k5-gmt_mktime.h"
 
-struct addrlist;
 struct sendto_callback_info;
 
 /* libos.spec */
@@ -621,33 +620,6 @@ krb5_os_hostaddr(krb5_context, const char *, krb5_address ***);
 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 @@ void krb5int_free_srv_dns_data(struct srv_dns_entry *);
 /* 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 @@ typedef struct _krb5int_access {
     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);
 
index a4a0b83de6e163581df82f6c8253cd3eacd6cec3..0df77fc19b0e2ffee8ee9b81dcaa63eec2e63240 100644 (file)
@@ -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
               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))
index 6866813975d96ddbbf934e5f88662eab7b2a4e2c..98f914d9d37485c4bf3e4a5ab155d79f6f31a0d7 100644 (file)
@@ -107,6 +107,8 @@ initialize_krb5_error_table
 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_ops
 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_copy_data_contents_add0
 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
index fa97c5789e853edff1f8cd7291812e8576557de1..a0cdce6b4802a6de203604c034454a2e0a177fa7 100644 (file)
@@ -53,20 +53,8 @@ krb5int_accessor(krb5int_access *internals, krb5_int32 version)
             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),
index 0e15932c285d8ae6c5b0917998d3ad22f05d6075..6ebe8dbbbd9381085c1096c8e9bfae2dc6946dad 100644 (file)
@@ -59,31 +59,31 @@ struct sendto_callback_context {
 
 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 @@ change_set_password(krb5_context context,
 {
     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 @@ change_set_password(krb5_context context,
     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 @@ change_set_password(krb5_context context,
     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 @@ change_set_password(krb5_context context,
         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 @@ change_set_password(krb5_context context,
                                        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 @@ change_set_password(krb5_context context,
             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 @@ cleanup:
     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);
 
index 5c4e21d6a00a7f9f978764ff8415ce15fd641ebc..168c1846a19f89aef78d33d83aec5f9387f69843 100644 (file)
@@ -450,7 +450,7 @@ domain_heuristic(krb5_context context, const char *domain,
                  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 @@ domain_heuristic(krb5_context context, const char *domain,
         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;
index 9515c07b4b98f0d48af3927cb36b60f53ade2612..b395375fc76a2bac0569715092ea3b09512d582b 100644 (file)
@@ -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 @@ _krb5_use_dns_realm(krb5_context context)
 
 #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 @@ Tprintf(const char *fmt, ...)
 #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);
+
+    newservers = realloc(list->servers, newspace);
+    if (newservers == NULL)
+        return NULL;
+    list->servers = newservers;
+    entry = &newservers[list->nservers];
+    memset(entry, 0, sizeof(*entry));
+    return entry;
+}
 
-    dprint("\tadding %p=%A to %p (naddrs=%d space=%d)\n", a, a, lp,
-           lp->naddrs, lp->space);
+/* 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)
+{
+    struct server_entry *entry;
 
-    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");
+    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;
 }
 
-#define add_host_to_list krb5int_add_host_to_list
-
-static void
-call_freeaddrinfo(void *data)
+/* Add a hostname entry to list. */
+static int
+add_host_to_list(struct serverlist *list, const char *hostname, int port,
+                 int socktype, int family)
 {
-    /* 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;
 
-int
-krb5int_add_host_to_list (struct addrlist *lp, const char *hostname,
-                          int port, int secport,
-                          int socktype, int family)
-{
-    struct addrinfo *addrs, *a, *anext, hint;
-    int err, result;
-    char portbuf[10], secportbuf[10];
-    void (*freefn)(void *);
-
-    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 @@ locate_srv_conf_1(krb5_context context, const krb5_data *realm,
 
     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 @@ locate_srv_conf_1(krb5_context context, const krb5_data *realm,
             *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 @@ krb5_locate_srv_conf(krb5_context context, const krb5_data *realm,
 
 #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 @@ static const char *objdirs[] = { LIBDIR "/krb5/plugins/libkrb5", NULL };
 
 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 @@ module_callback (void *cbdata, int socktype, struct sockaddr *sa)
 }
 
 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 @@ module_locate_server (krb5_context ctx, const krb5_data *realm,
     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 @@ module_locate_server (krb5_context ctx, const krb5_data *realm,
         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 @@ module_locate_server (krb5_context ctx, const krb5_data *realm,
 
     /* 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 @@ prof_locate_server (krb5_context context, const krb5_data *realm,
         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 @@ dns_locate_server (krb5_context context, const krb5_data *realm,
 
     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 @@ dns_locate_server (krb5_context context, const krb5_data *realm,
  */
 
 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 @@ krb5int_locate_server (krb5_context context, const krb5_data *realm,
         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 @@ krb5int_locate_server (krb5_context context, const krb5_data *realm,
          * 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 @@ krb5int_locate_server (krb5_context context, const krb5_data *realm,
     }
     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);
 }
index 8fc114013dd5865813453d09b381bdea45614162..2fb353ea01179edf95dff4ac141c2a2e2ce270a1 100644 (file)
 #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,
@@ -60,15 +85,15 @@ int _krb5_use_dns_realm (krb5_context);
 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);
 
index 4cdffe716a29be004687078bc4b3d58519dcc51f..242dcb54fdb2fe84aa660acf878f4845ab6f8d62 100644 (file)
@@ -253,60 +253,20 @@ krb5int_debug_fprint (const char *fmt, ...)
 #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 @@ check_for_svc_unavailable (krb5_context context,
  */
 
 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 @@ krb5_sendto_kdc (krb5_context context, const krb5_data *message,
         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 @@ krb5_sendto_kdc (krb5_context context, const krb5_data *message,
     else
         socktype1 = SOCK_STREAM, socktype2 = SOCK_DGRAM;
 
-    retval = krb5_locate_kdc(context, realm, &addrs, *use_master, socktype1, 0);
-    if (socktype2) {
-        struct addrlist addrs2;
-
-        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_locate_kdc(context, realm, &servers, *use_master,
+                           tcp_only ? SOCK_STREAM : 0);
+    if (retval)
+        return retval;
+
+    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)
+        goto cleanup;
+
+    /* 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) {
-            (void) merge_addrlists(&addrs, &addrs2);
-            krb5int_free_addrlist(&addrs2);
+            if (in_addrlist(entry, &mservers))
+                *use_master = 1;
+            k5_free_serverlist(&mservers);
         }
-#endif
+        TRACE_SENDTO_KDC_MASTER(context, *use_master);
+        retval = 0;
     }
 
-    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;
-        }
-        krb5int_free_addrlist (&addrs);
-    }
+cleanup:
+    k5_free_serverlist(&servers);
     return retval;
 }
 
@@ -604,17 +537,24 @@ set_conn_state_msg_length (struct conn_state *state, const krb5_data *message)
     }
 }
 
-
-
-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 @@ setup_connection (struct conn_state *state, struct addrinfo *ai,
         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 @@ start_connection(krb5_context context, struct conn_state *state,
 #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 @@ start_connection(krb5_context context, struct conn_state *state,
             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 @@ start_connection(krb5_context context, struct conn_state *state,
      */
     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 @@ start_connection(krb5_context context, struct conn_state *state,
             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 @@ start_connection(krb5_context context, struct conn_state *state,
             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 @@ start_connection(krb5_context context, struct conn_state *state,
 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 @@ maybe_send(krb5_context context, struct conn_state *conn,
     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 @@ maybe_send(krb5_context context, struct conn_state *conn,
         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 @@ maybe_send(krb5_context context, struct conn_state *conn,
 
     /* 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 @@ service_tcp_fd(krb5_context context, struct conn_state *conn,
             /* 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 @@ service_tcp_fd(krb5_context context, struct conn_state *conn,
          */
         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 @@ service_tcp_fd(krb5_context context, struct conn_state *conn,
                ((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 @@ service_tcp_fd(krb5_context context, struct conn_state *conn,
                 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 @@ service_tcp_fd(krb5_context context, struct conn_state *conn,
                                 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 @@ service_udp_fd(krb5_context context, struct conn_state *conn,
 
     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,15 +1106,23 @@ service_udp_fd(krb5_context context, struct conn_state *conn,
     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) {
@@ -1113,53 +1141,43 @@ service_fds (krb5_context context,
             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 @@ service_fds (krb5_context context,
  */
 
 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;
-
-    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");
+    krb5_boolean done = FALSE;
 
     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 @@ krb5int_sendto (krb5_context context, const krb5_data *message,
     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);
+        }
+    }
+
+    /* 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);
+    }
 
-    /* Set up connections.  */
-    for (host = 0; host < addrs->naddrs; host++) {
-        setup_connection(&conns[host], addrs->addrs[host].ai, message,
-                         &udpbuf);
+    /* 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);
     }
-    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;
 
-            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)
-                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.
-                 */
+    /* 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 (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?  */
-        retval = KRB5_KDC_UNREACH;
-        goto egress;
-    }
-    if (e == 0 || winning_conn < 0) {
+    if (sel_state->nfds == 0 || !done || winner == NULL) {
         retval = KRB5_KDC_UNREACH;
-        goto egress;
+        goto cleanup;
     }
     /* 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);
-
-    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);
+    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);
+
+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);
index 1fc297eb3ab175765a9231f21dbfcaf85342dfbd..5453a4cb3cf0e49eb5c750a644e05e6497e3bd32 100644 (file)
@@ -19,7 +19,7 @@ enum {
 
 const char *prog;
 
-struct addrlist al;
+struct serverlist sl;
 
 static void
 kfatal (krb5_error_code err)
@@ -48,25 +48,29 @@ stypename (int stype)
 static void
 print_addrs (void)
 {
-    int i;
+    size_t i;
+    int err;
 
-    int naddrs = al.naddrs;
-
-    printf ("%d addresses:\n", naddrs);
-    for (i = 0; i < naddrs; i++) {
-        int err;
-        struct addrinfo *ai = al.addrs[i].ai;
+    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];
-        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 @@ main (int argc, char *argv[])
 
     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;
 }
index 072ac30581070579106ebe751822c23542b58dda..59ca7d605a197528698aa8800b9223c00248d4b2 100644 (file)
@@ -105,27 +105,30 @@ test_get_krbhst(krb5_context ctx, char *realm)
 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 @@ test_locate_kdc(krb5_context ctx, char *realm)
 #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 @@ test_locate_kdc(krb5_context ctx, char *realm)
         }
 #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");
 }
 
index 3138aaf0f9e6a09461aa07aac5b3108561b476bd..ae3b20cc2904ddb742e8e3d50a70ae0e6080edd9 100644 (file)
@@ -40,6 +40,7 @@
  */
 
 #include "k5-int.h"
+#include "cm.h"
 
 #ifndef DISABLE_TRACING
 
@@ -71,7 +72,7 @@ trace_format(krb5_context context, const char *fmt, va_list ap)
     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 @@ trace_format(krb5_context context, const char *fmt, va_list ap)
                     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))