Use a list of disconnected and connected sockets to talk to kpasswd servers, so we automatically try TCP if we fail to change the password UDP, or if the UDP-based server is just slow. This patch looks big, but most of it's actually whitespace because most of the logic is no longer called as part of a loop with UDP and TCP being used in different iterations. RT #5868. Index: src/lib/krb5/os/changepw.c =================================================================== --- src/lib/krb5/os/changepw.c (revision 20199) +++ src/lib/krb5/os/changepw.c (working copy) @@ -199,14 +199,14 @@ krb5_address remote_kaddr; krb5_boolean useTcp = 0; GETSOCKNAME_ARG3_TYPE addrlen; - krb5_error_code code = 0; + krb5_error_code code = 0, code2 = 0; char *code_string; - int local_result_code; + int local_result_code, i; struct sendto_callback_context callback_ctx; struct sendto_callback_info callback_info; struct sockaddr_storage remote_addr; - struct addrlist al = ADDRLIST_INIT; + struct addrlist al = ADDRLIST_INIT, al2 = ADDRLIST_INIT; memset( &callback_ctx, 0, sizeof(struct sendto_callback_context)); callback_ctx.context = context; @@ -225,109 +225,104 @@ &callback_ctx.ap_req))) goto cleanup; - do { - if ((code = krb5_locate_kpasswd(callback_ctx.context, - krb5_princ_realm(callback_ctx.context, - creds->server), - &al, useTcp))) - break; - + code = krb5_locate_kpasswd(callback_ctx.context, + krb5_princ_realm(callback_ctx.context, + creds->server), + &al, useTcp); + code2 = krb5_locate_kpasswd(callback_ctx.context, + krb5_princ_realm(callback_ctx.context, + creds->server), + &al2, !useTcp); + if ((al.naddrs + al2.naddrs) == 0) { + if (!code) + code = code2 ? code2 : KRB5_REALM_CANT_RESOLVE; + goto cleanup; + } + + if (al2.naddrs > 0) { + if (krb5int_grow_addrlist(&al, al2.naddrs)) + goto cleanup; + for (i = 0; i < al2.naddrs; i++) + al.addrs[al.naddrs++] = al2.addrs[i]; + al2.naddrs = 0; + } + - addrlen = sizeof(remote_addr); - - callback_info.context = (void*) &callback_ctx; - callback_info.pfn_callback = kpasswd_sendto_msg_callback; - callback_info.pfn_cleanup = kpasswd_sendto_msg_cleanup; - - if ((code = krb5int_sendto(callback_ctx.context, - NULL, - &al, - &callback_info, - &chpw_rep, - NULL, - NULL, - ss2sa(&remote_addr), - &addrlen, - NULL, - NULL, - NULL - ))) { - - /* - * Here we may want to switch to TCP on some errors. - * right? - */ - break; - } - + addrlen = sizeof(remote_addr); + + callback_info.context = (void*) &callback_ctx; + callback_info.pfn_callback = kpasswd_sendto_msg_callback; + callback_info.pfn_cleanup = kpasswd_sendto_msg_cleanup; + + if ((code = krb5int_sendto(callback_ctx.context, + NULL, + &al, + &callback_info, + &chpw_rep, + NULL, + NULL, + ss2sa(&remote_addr), + &addrlen, + NULL, + NULL, + NULL + ))) + goto cleanup; + - remote_kaddr.addrtype = ADDRTYPE_INET; - remote_kaddr.length = sizeof(ss2sin(&remote_addr)->sin_addr); - remote_kaddr.contents = (krb5_octet *) &ss2sin(&remote_addr)->sin_addr; - - if ((code = krb5_auth_con_setaddrs(callback_ctx.context, - callback_ctx.auth_context, - NULL, - &remote_kaddr))) - break; - + remote_kaddr.addrtype = ADDRTYPE_INET; + remote_kaddr.length = sizeof(ss2sin(&remote_addr)->sin_addr); + remote_kaddr.contents = (krb5_octet *) &ss2sin(&remote_addr)->sin_addr; + + if ((code = krb5_auth_con_setaddrs(callback_ctx.context, + callback_ctx.auth_context, + NULL, + &remote_kaddr))) + goto cleanup; + - if (set_password_for) - code = krb5int_rd_setpw_rep(callback_ctx.context, - callback_ctx.auth_context, - &chpw_rep, - &local_result_code, - result_string); - else - code = krb5int_rd_chpw_rep(callback_ctx.context, - callback_ctx.auth_context, - &chpw_rep, - &local_result_code, - result_string); - - if (code) { - if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !useTcp ) { - krb5int_free_addrlist (&al); - useTcp = 1; - continue; - } - - break; - } - - if (result_code) - *result_code = local_result_code; - + if (set_password_for) + code = krb5int_rd_setpw_rep(callback_ctx.context, + callback_ctx.auth_context, + &chpw_rep, + &local_result_code, + result_string); + else + code = krb5int_rd_chpw_rep(callback_ctx.context, + callback_ctx.auth_context, + &chpw_rep, + &local_result_code, + result_string); + + if (code) + goto cleanup; + + if (result_code) + *result_code = local_result_code; + - if (result_code_string) { - if (set_password_for) - code = krb5int_setpw_result_code_string(callback_ctx.context, - local_result_code, - (const char **)&code_string); - else - code = krb5_chpw_result_code_string(callback_ctx.context, - local_result_code, - &code_string); - if(code) - goto cleanup; - - result_code_string->length = strlen(code_string); - result_code_string->data = malloc(result_code_string->length); - if (result_code_string->data == NULL) { - code = ENOMEM; - goto cleanup; - } - 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; - } else { - break; - } - } while (TRUE); + if (result_code_string) { + if (set_password_for) + code = krb5int_setpw_result_code_string(callback_ctx.context, + local_result_code, + (const char **) &code_string); + else + code = krb5_chpw_result_code_string(callback_ctx.context, + local_result_code, + &code_string); + if (code) + goto cleanup; + + result_code_string->length = strlen(code_string); + result_code_string->data = malloc(result_code_string->length); + if (result_code_string->data == NULL) { + code = ENOMEM; + goto cleanup; + } + strncpy(result_code_string->data, code_string, result_code_string->length); + } cleanup: if (callback_ctx.auth_context != NULL) krb5_auth_con_free(callback_ctx.context, callback_ctx.auth_context); + krb5int_free_addrlist (&al2); krb5int_free_addrlist (&al); krb5_free_data_contents(callback_ctx.context, &callback_ctx.ap_req);