Backport most of the changes that were made between 1.6.2 and 1.6.3 to add a message-handler callback to the sendto() routines, and to use one for KDC access attempts which will cause us to not abort when we see a service-unavailable error. Index: src/include/k5-int.h =================================================================== --- src/include/k5-int.h (.../krb5-1-6-2-final) (revision 20213) +++ src/include/k5-int.h (.../krb5-1-6-3-final) (revision 20213) @@ -216,6 +216,10 @@ /* required */ #define KDC_ERR_SERVER_NOMATCH 26 /* Requested server and */ /* ticket don't match*/ +#define KDC_ERR_SVC_UNAVAILABLE 29 /* A service is not + * available that is + * required to process the + * request */ /* Application errors */ #define KRB_AP_ERR_BAD_INTEGRITY 31 /* Decrypt integrity check failed */ #define KRB_AP_ERR_TKT_EXPIRED 32 /* Ticket expired */ @@ -498,7 +502,9 @@ 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); + 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 krb5_get_krbhst (krb5_context, const krb5_data *, char *** ); krb5_error_code krb5_free_krbhst (krb5_context, char * const * ); @@ -1885,7 +1881,9 @@ 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 *); + 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, Index: src/lib/krb4/send_to_kdc.c =================================================================== --- src/lib/krb4/send_to_kdc.c (.../krb5-1-6-2-final) (revision 20213) +++ src/lib/krb4/send_to_kdc.c (.../krb5-1-6-3-final) (revision 20213) @@ -181,7 +181,7 @@ message.length = pkt->length; message.data = (char *)pkt->dat; /* XXX yuck */ retval = internals.sendto_udp(NULL, &message, &al, NULL, &reply, addr, - addrlen, NULL, 0, NULL); + addrlen, NULL, 0, NULL, NULL, NULL); DEB(("sendto_udp returns %d\n", retval)); free_al: internals.free_addrlist(&al); Index: src/lib/krb5/error_tables/krb5_err.et =================================================================== --- src/lib/krb5/error_tables/krb5_err.et (.../krb5-1-6-2-final) (revision 20213) +++ src/lib/krb5/error_tables/krb5_err.et (.../krb5-1-6-3-final) (revision 20213) @@ -68,7 +68,7 @@ error_code KRB5KDC_ERR_SERVER_NOMATCH, "Requested server and ticket don't match" error_code KRB5PLACEHOLD_27, "KRB5 error code 27" error_code KRB5PLACEHOLD_28, "KRB5 error code 28" -error_code KRB5PLACEHOLD_29, "KRB5 error code 29" +error_code KRB5KDC_ERR_SVC_UNAVAILABLE, "A service is not available that is required to process the request" error_code KRB5PLACEHOLD_30, "KRB5 error code 30" # vv 31 error_code KRB5KRB_AP_ERR_BAD_INTEGRITY, "Decrypt integrity check failed" Index: src/lib/krb5/os/changepw.c =================================================================== --- src/lib/krb5/os/changepw.c (.../krb5-1-6-2-final) (revision 20213) +++ src/lib/krb5/os/changepw.c (.../krb5-1-6-3-final) (revision 20213) @@ -247,6 +247,8 @@ NULL, ss2sa(&remote_addr), &addrlen, + NULL, + NULL, NULL ))) { /* if we're not using a stream socket, and it's an error which Index: src/lib/krb5/os/send524.c =================================================================== --- src/lib/krb5/os/send524.c (.../krb5-1-6-2-final) (revision 20213) +++ src/lib/krb5/os/send524.c (.../krb5-1-6-3-final) (revision 20213) @@ -98,7 +98,7 @@ if (al.naddrs == 0) return KRB5_REALM_UNKNOWN; - retval = krb5int_sendto (context, message, &al, NULL, reply, addr, addrlen, NULL, 0, NULL); + retval = krb5int_sendto (context, message, &al, NULL, reply, addr, addrlen, NULL, 0, NULL, NULL, NULL); krb5int_free_addrlist (&al); return retval; #else Index: src/lib/krb5/os/sendto_kdc.c =================================================================== --- src/lib/krb5/os/sendto_kdc.c (.../krb5-1-6-2-final) (revision 20213) +++ src/lib/krb5/os/sendto_kdc.c (.../krb5-1-6-3-final) (revision 20213) @@ -295,6 +295,30 @@ return 0; } +static int +check_for_svc_unavailable (krb5_context context, + const krb5_data *reply, + void *msg_handler_data) +{ + krb5_error_code *retval = (krb5_error_code *)msg_handler_data; + + *retval = 0; + + if (krb5_is_krb_error(reply)) { + krb5_error *err_reply; + + if (decode_krb5_error(reply, &err_reply) == 0) { + *retval = err_reply->error; + krb5_free_error(context, err_reply); + + /* Returning 0 means continue to next KDC */ + return (*retval != KDC_ERR_SVC_UNAVAILABLE); + } + } + + return 1; +} + /* * send the formatted request 'message' to a KDC for realm 'realm' and * return the response (if any) in 'reply'. @@ -381,9 +405,12 @@ } if (addrs.naddrs > 0) { + krb5_error_code err = 0; + retval = krb5int_sendto (context, message, &addrs, 0, reply, 0, 0, - 0, 0, &addr_used); - if (retval == 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 @@ -401,7 +428,19 @@ } 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); } return retval; @@ -1015,9 +1054,12 @@ } static int -service_fds (struct select_state *selstate, +service_fds (krb5_context context, + struct select_state *selstate, struct conn_state *conns, size_t n_conns, int *winning_conn, - struct select_state *seltemp) + struct select_state *seltemp, + int (*msg_handler)(krb5_context, const krb5_data *, void *), + void *msg_handler_data) { int e, selret; @@ -1056,9 +1098,22 @@ state_strings[(int) conns[i].state]); if (conns[i].service (&conns[i], selstate, ssflags)) { - dprint("fd service routine says we're done\n"); - *winning_conn = i; - return 1; + 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; + + stop = (msg_handler(context, &reply, msg_handler_data) != 0); + } + + if (stop) { + dprint("fd service routine says we're done\n"); + *winning_conn = i; + return 1; + } } } } @@ -1098,7 +1153,10 @@ 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 *addr_used, + /* return 0 -> keep going, 1 -> quit */ + int (*msg_handler)(krb5_context, const krb5_data *, void *), + void *msg_handler_data) { int i, pass; int delay_this_pass = 2; @@ -1185,8 +1243,8 @@ goto egress; sel_state->end_time = now; sel_state->end_time.tv_sec += 1; - e = service_fds(sel_state, conns, host+1, &winning_conn, - sel_state+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) @@ -1206,7 +1264,8 @@ 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(sel_state, conns, host+1, &winning_conn, sel_state+1); + e = service_fds(context, sel_state, conns, host+1, &winning_conn, + sel_state+1, msg_handler, msg_handler_data); if (e) break; if (sel_state->nfds == 0)