diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/include/cm.h | 8 | ||||
-rw-r--r-- | src/include/k5-int.h | 54 | ||||
-rw-r--r-- | src/include/k5-trace.h | 56 | ||||
-rw-r--r-- | src/lib/krb5/libkrb5.exports | 4 | ||||
-rw-r--r-- | src/lib/krb5/os/accessor.c | 12 | ||||
-rw-r--r-- | src/lib/krb5/os/changepw.c | 69 | ||||
-rw-r--r-- | src/lib/krb5/os/hst_realm.c | 6 | ||||
-rw-r--r-- | src/lib/krb5/os/locate_kdc.c | 456 | ||||
-rw-r--r-- | src/lib/krb5/os/os-proto.h | 49 | ||||
-rw-r--r-- | src/lib/krb5/os/sendto_kdc.c | 644 | ||||
-rw-r--r-- | src/lib/krb5/os/t_locate_kdc.c | 54 | ||||
-rw-r--r-- | src/lib/krb5/os/t_std_conf.c | 28 | ||||
-rw-r--r-- | src/lib/krb5/os/trace.c | 33 |
13 files changed, 626 insertions, 847 deletions
diff --git a/src/include/cm.h b/src/include/cm.h index cf5ff222b..2bb5ca980 100644 --- a/src/include/cm.h +++ b/src/include/cm.h @@ -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 { diff --git a/src/include/k5-int.h b/src/include/k5-int.h index 92ff1f043..adcc97eb2 100644 --- a/src/include/k5-int.h +++ b/src/include/k5-int.h @@ -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); diff --git a/src/include/k5-trace.h b/src/include/k5-trace.h index a4a0b83de..0df77fc19 100644 --- a/src/include/k5-trace.h +++ b/src/include/k5-trace.h @@ -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)) diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports index 686681397..98f914d9d 100644 --- a/src/lib/krb5/libkrb5.exports +++ b/src/lib/krb5/libkrb5.exports @@ -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 diff --git a/src/lib/krb5/os/accessor.c b/src/lib/krb5/os/accessor.c index fa97c5789..a0cdce6b4 100644 --- a/src/lib/krb5/os/accessor.c +++ b/src/lib/krb5/os/accessor.c @@ -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), diff --git a/src/lib/krb5/os/changepw.c b/src/lib/krb5/os/changepw.c index 0e15932c2..6ebe8dbbb 100644 --- a/src/lib/krb5/os/changepw.c +++ b/src/lib/krb5/os/changepw.c @@ -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); diff --git a/src/lib/krb5/os/hst_realm.c b/src/lib/krb5/os/hst_realm.c index 5c4e21d6a..168c1846a 100644 --- a/src/lib/krb5/os/hst_realm.c +++ b/src/lib/krb5/os/hst_realm.c @@ -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; diff --git a/src/lib/krb5/os/locate_kdc.c b/src/lib/krb5/os/locate_kdc.c index 9515c07b4..b395375fc 100644 --- a/src/lib/krb5/os/locate_kdc.c +++ b/src/lib/krb5/os/locate_kdc.c @@ -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); } diff --git a/src/lib/krb5/os/os-proto.h b/src/lib/krb5/os/os-proto.h index 8fc114013..2fb353ea0 100644 --- a/src/lib/krb5/os/os-proto.h +++ b/src/lib/krb5/os/os-proto.h @@ -31,9 +31,34 @@ #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); diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c index 4cdffe716..242dcb54f 100644 --- a/src/lib/krb5/os/sendto_kdc.c +++ b/src/lib/krb5/os/sendto_kdc.c @@ -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); diff --git a/src/lib/krb5/os/t_locate_kdc.c b/src/lib/krb5/os/t_locate_kdc.c index 1fc297eb3..5453a4cb3 100644 --- a/src/lib/krb5/os/t_locate_kdc.c +++ b/src/lib/krb5/os/t_locate_kdc.c @@ -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; } diff --git a/src/lib/krb5/os/t_std_conf.c b/src/lib/krb5/os/t_std_conf.c index 072ac3058..59ca7d605 100644 --- a/src/lib/krb5/os/t_std_conf.c +++ b/src/lib/krb5/os/t_std_conf.c @@ -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"); } diff --git a/src/lib/krb5/os/trace.c b/src/lib/krb5/os/trace.c index 3138aaf0f..ae3b20cc2 100644 --- a/src/lib/krb5/os/trace.c +++ b/src/lib/krb5/os/trace.c @@ -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)) |