Keep track of which KDC answered the previous request for a given library context. When we want to send data to a KDC for a realm, if the one which answered last time is in the list of servers which might be consulted, move it to the top of the list. The aim is to compensate for servers which aren't responding, or which are taking so much time to respond, that a server to which we sent the request more than a second later responded before it. An artifact of this approach is that if the first server responds before we transmit to a second server, the behavior doesn't changed. Removing unresponsive servers from the list could, on pathological networks, leave us with no servers in the list, so we won't do that. --- krb5-1.4/src/include/k5-int.h 2004-12-08 15:20:12.000000000 -0500 +++ krb5-1.4/src/include/k5-int.h 2005-02-21 14:57:08.000000000 -0500 @@ -1047,6 +1047,8 @@ #ifdef KRB5_DNS_LOOKUP krb5_boolean profile_in_memory; #endif /* KRB5_DNS_LOOKUP */ + + krb5_data *previous_kdc, *previous_master_kdc; }; /* could be used in a table to find an etype and initialize a block */ --- krb5-1.4/src/lib/krb4/send_to_kdc.c 2004-02-27 00:24:38.000000000 -0500 +++ krb5-1.4/src/lib/krb4/send_to_kdc.c 2005-02-21 15:51:27.000000000 -0500 @@ -47,6 +47,7 @@ /* These are really defaults from getservbyname() or hardcoded. */ static int cached_krb_udp_port = 0; static int cached_krbsec_udp_port = 0; +static krb5_data *cached_kdc_address = NULL; int krb4int_send_to_kdc_addr(KTEXT, KTEXT, char *, struct sockaddr *, socklen_t *); @@ -176,12 +177,50 @@ if (al.naddrs == 0) { DEB (("%s: can't find any Kerberos host.\n", prog)); retval = SKDC_CANT; + } else { + int j, winner; + struct addrinfo *replier = NULL; + krb5_data *previous; + + previous = cached_kdc_address; + if (previous) + for (j = 0; j < al.naddrs; j++) + if ((al.addrs[j]->ai_addrlen == previous->length) && + (memcmp(al.addrs[j]->ai_addr, previous->data, + previous->length) == 0)) { + struct addrinfo *tmp; + tmp = al.addrs[0]; + al.addrs[0] = al.addrs[j]; + al.addrs[j] = tmp; + break; + } + message.length = pkt->length; + message.data = (char *)pkt->dat; /* XXX yuck */ + winner = -1; + retval = internals.sendto_udp(NULL, &message, &al, &reply, addr, + addrlen, &winner); + if ((winner >= 0) && (winner < al.naddrs)) { + previous = (krb5_data *) malloc (sizeof(krb5_data)); + replier = al.addrs[winner]; + if (previous) { + previous->length = 0; + previous->data = malloc (replier->ai_addrlen); + if (previous->data == NULL) { + free (previous); + previous = NULL; + } else { + memcpy (previous->data, replier->ai_addr, + replier->ai_addrlen); + previous->length = replier->ai_addrlen; + } + } + if (cached_kdc_address) { + free(cached_kdc_address->data); + free(cached_kdc_address); + } + cached_kdc_address = previous; + } } - - message.length = pkt->length; - message.data = (char *)pkt->dat; /* XXX yuck */ - retval = internals.sendto_udp(NULL, &message, &al, &reply, addr, - addrlen, NULL); DEB(("sendto_udp returns %d\n", retval)); free_al: internals.free_addrlist(&al); --- krb5-1.4/src/lib/krb5/os/sendto_kdc.c 2004-08-27 20:25:24.000000000 -0400 +++ krb5-1.4/src/lib/krb5/os/sendto_kdc.c 2005-02-22 01:21:16.000000000 -0500 @@ -332,8 +332,53 @@ } if (addrs.naddrs > 0) { + int j; + struct addrinfo *replier = NULL; + krb5_data *previous; + + if (*use_master) + previous = context->previous_master_kdc; + else + previous = context->previous_kdc; + if (previous) + for (j = 0; j < addrs.naddrs; j++) + if ((addrs.addrs[j]->ai_addrlen == previous->length) && + (memcmp(addrs.addrs[j]->ai_addr, previous->data, + previous->length) == 0)) { + struct addrinfo *tmp; + tmp = addrs.addrs[0]; + addrs.addrs[0] = addrs.addrs[j]; + addrs.addrs[j] = tmp; + break; + } + addr_used = -1; retval = krb5int_sendto (context, message, &addrs, reply, 0, 0, &addr_used); + if ((addr_used >= 0) && (addr_used < addrs.naddrs)) { + replier = addrs.addrs[addr_used]; + previous = (krb5_data *) malloc(sizeof(krb5_data)); + if (previous) { + previous->length = 0; + previous->data = malloc (replier->ai_addrlen); + if (previous->data == NULL) { + free (previous); + previous = NULL; + } else { + memcpy (previous->data, replier->ai_addr, + replier->ai_addrlen); + previous->length = replier->ai_addrlen; + } + } + if (*use_master) { + if (context->previous_master_kdc) + krb5_free_data (context, context->previous_master_kdc); + context->previous_master_kdc = previous; + } else { + if (context->previous_kdc) + krb5_free_data (context, context->previous_kdc); + context->previous_kdc = previous; + } + } if (retval == 0) { /* * Set use_master to 1 if we ended up talking to a master when --- krb5-1.4/src/lib/krb5/krb/init_ctx.c 2004-06-02 18:35:33.000000000 -0400 +++ krb5-1.4/src/lib/krb5/krb/init_ctx.c 2005-02-21 14:57:08.000000000 -0500 @@ -228,6 +228,10 @@ ctx->use_conf_ktypes = 0; ctx->udp_pref_limit = -1; + + ctx->previous_kdc = NULL; + ctx->previous_master_kdc = NULL; + *context = ctx; return 0; @@ -266,6 +270,11 @@ ctx->ser_ctx = 0; } + if (ctx->previous_kdc) + krb5_free_data(ctx, ctx->previous_kdc); + if (ctx->previous_master_kdc) + krb5_free_data(ctx, ctx->previous_master_kdc); + ctx->magic = 0; free(ctx); }