From 0d2c1deeb761144a671ce37fa39ed8bc6667936f Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Mon, 4 Jun 2012 11:07:19 +0200 Subject: Only do one cycle when resolving a server Rename fo_get_server_name to fo_get_server_str_name fo_get_server_name() getter for a server name Allows to be more concise in tests and more defensive in resolve callbacks Only do one cycle when resolving a server https://fedorahosted.org/sssd/ticket/1214 Detect cycle in the fail over on subsequent resolve requests only --- src/providers/data_provider_fo.c | 83 +++++++++++++++++++++++++----- src/providers/dp_backend.h | 8 ++- src/providers/fail_over.c | 17 +++++- src/providers/fail_over.h | 5 ++ src/providers/ipa/ipa_common.c | 12 ++++- src/providers/krb5/krb5_auth.c | 51 +++++++++--------- src/providers/krb5/krb5_common.c | 2 +- src/providers/ldap/ldap_auth.c | 8 +-- src/providers/ldap/ldap_common.c | 13 ++++- src/providers/ldap/sdap_async_connection.c | 19 +++---- src/tests/fail_over-tests.c | 16 +++--- 11 files changed, 170 insertions(+), 64 deletions(-) diff --git a/src/providers/data_provider_fo.c b/src/providers/data_provider_fo.c index 58468c820..d5cb0a476 100644 --- a/src/providers/data_provider_fo.c +++ b/src/providers/data_provider_fo.c @@ -46,6 +46,7 @@ struct be_svc_data { bool run_callbacks; struct be_svc_callback *callbacks; + struct fo_server *first_resolved; }; struct be_failover_ctx { @@ -351,6 +352,7 @@ struct be_resolve_server_state { int attempts; struct fo_server *srv; + bool first_try; }; static void be_resolve_server_done(struct tevent_req *subreq); @@ -358,7 +360,8 @@ static void be_resolve_server_done(struct tevent_req *subreq); struct tevent_req *be_resolve_server_send(TALLOC_CTX *memctx, struct tevent_context *ev, struct be_ctx *ctx, - const char *service_name) + const char *service_name, + bool first_try) { struct tevent_req *req, *subreq; struct be_resolve_server_state *state; @@ -379,6 +382,7 @@ struct tevent_req *be_resolve_server_send(TALLOC_CTX *memctx, state->svc = svc; state->attempts = 0; + state->first_try = first_try; subreq = fo_resolve_service_send(state, ev, ctx->be_fo->resolv, @@ -408,31 +412,31 @@ static void be_resolve_server_done(struct tevent_req *subreq) switch (ret) { case EOK: if (!state->srv) { - tevent_req_error(req, EFAULT); - return; + ret = EFAULT; + goto fail; } break; case ENOENT: /* all servers have been tried and none * was found good, go offline */ - tevent_req_error(req, EIO); - return; + ret = EIO; + goto fail; default: /* mark server as bad and retry */ if (!state->srv) { - tevent_req_error(req, EFAULT); - return; + ret = EFAULT; + goto fail; } DEBUG(6, ("Couldn't resolve server (%s), resolver returned (%d)\n", - fo_get_server_name(state->srv), ret)); + fo_get_server_str_name(state->srv), ret)); state->attempts++; if (state->attempts >= 10) { DEBUG(2, ("Failed to find a server after 10 attempts\n")); - tevent_req_error(req, EIO); - return; + ret = EIO; + goto fail; } /* now try next one */ @@ -442,8 +446,8 @@ static void be_resolve_server_done(struct tevent_req *subreq) state->ctx->be_fo->fo_ctx, state->svc->fo_service); if (!subreq) { - tevent_req_error(req, ENOMEM); - return; + ret = ENOMEM; + goto fail; } tevent_req_set_callback(subreq, be_resolve_server_done, req); @@ -451,16 +455,31 @@ static void be_resolve_server_done(struct tevent_req *subreq) } /* all fine we got the server */ + if (state->svc->first_resolved == NULL || state->first_try == true) { + DEBUG(7, ("Saving the first resolved server\n")); + state->svc->first_resolved = state->srv; + } else if (state->svc->first_resolved == state->srv) { + DEBUG(2, ("The fail over cycled through all available servers\n")); + ret = ENOENT; + goto fail; + } - if (debug_level >= 4) { + if (debug_level >= 4 && fo_get_server_name(state->srv)) { struct resolv_hostent *srvaddr; char ipaddr[128]; srvaddr = fo_get_server_hostent(state->srv); + if (!srvaddr) { + DEBUG(3, ("FATAL: No hostent available for server (%s)\n", + fo_get_server_str_name(state->srv))); + ret = EFAULT; + goto fail; + } + inet_ntop(srvaddr->family, srvaddr->addr_list[0]->ipaddr, ipaddr, 128); DEBUG(4, ("Found address for server %s: [%s] TTL %d\n", - fo_get_server_name(state->srv), ipaddr, + fo_get_server_str_name(state->srv), ipaddr, srvaddr->addr_list[0]->ttl)); } @@ -481,6 +500,12 @@ static void be_resolve_server_done(struct tevent_req *subreq) } tevent_req_done(req); + return; + +fail: + DEBUG(7, ("Server resolution failed: %d\n", ret)); + state->svc->first_resolved = NULL; + tevent_req_error(req, ret); } int be_resolve_server_recv(struct tevent_req *req, struct fo_server **srv) @@ -527,3 +552,33 @@ void reset_fo(struct be_ctx *be_ctx) fo_reset_services(be_ctx->be_fo->fo_ctx); } +void be_fo_set_port_status(struct be_ctx *ctx, + struct fo_server *server, + enum port_status status) +{ + struct be_svc_data *svc; + struct fo_service *fsvc; + + fo_set_port_status(server, status); + + fsvc = fo_get_server_service(server); + if (!fsvc) { + DEBUG(3, ("BUG: No service associated with server\n")); + return; + } + + DLIST_FOR_EACH(svc, ctx->be_fo->svcs) { + if (svc->fo_service == fsvc) break; + } + + if (!svc) { + DEBUG(3, ("BUG: Unknown service\n")); + return; + } + + if (status == PORT_WORKING) { + /* We were successful in connecting to the server. Cycle through all + * available servers next time */ + svc->first_resolved = NULL; + } +} diff --git a/src/providers/dp_backend.h b/src/providers/dp_backend.h index 3d5e40bae..27ae9d73c 100644 --- a/src/providers/dp_backend.h +++ b/src/providers/dp_backend.h @@ -183,8 +183,14 @@ int be_fo_add_server(struct be_ctx *ctx, const char *service_name, struct tevent_req *be_resolve_server_send(TALLOC_CTX *memctx, struct tevent_context *ev, struct be_ctx *ctx, - const char *service_name); + const char *service_name, + bool first_try); int be_resolve_server_recv(struct tevent_req *req, struct fo_server **srv); + +void be_fo_set_port_status(struct be_ctx *ctx, + struct fo_server *server, + enum port_status status); + /* * Instruct fail-over to try next server on the next connect attempt. * Should be used after connection to service was unexpectedly dropped diff --git a/src/providers/fail_over.c b/src/providers/fail_over.c index 2a9631772..2b47e55cb 100644 --- a/src/providers/fail_over.c +++ b/src/providers/fail_over.c @@ -1354,7 +1354,16 @@ fo_get_server_port(struct fo_server *server) return server->port; } -const char *fo_get_server_name(struct fo_server *server) +const char * +fo_get_server_name(struct fo_server *server) +{ + if (!server->common) { + return NULL; + } + return server->common->name; +} + +const char *fo_get_server_str_name(struct fo_server *server) { if (!server->common) { if (fo_is_srv_lookup(server)) { @@ -1402,3 +1411,9 @@ void fo_reset_services(struct fo_ctx *fo_ctx) } } +struct fo_service * +fo_get_server_service(struct fo_server *server) +{ + if (!server) return NULL; + return server->service; +} diff --git a/src/providers/fail_over.h b/src/providers/fail_over.h index e5d6c525f..50c0dcf8d 100644 --- a/src/providers/fail_over.h +++ b/src/providers/fail_over.h @@ -169,6 +169,8 @@ int fo_get_server_port(struct fo_server *server); const char *fo_get_server_name(struct fo_server *server); +const char *fo_get_server_str_name(struct fo_server *server); + struct resolv_hostent *fo_get_server_hostent(struct fo_server *server); time_t fo_get_server_hostname_last_change(struct fo_server *server); @@ -176,4 +178,7 @@ time_t fo_get_server_hostname_last_change(struct fo_server *server); int fo_is_srv_lookup(struct fo_server *s); void fo_reset_services(struct fo_ctx *fo_ctx); + +struct fo_service *fo_get_server_service(struct fo_server *server); + #endif /* !__FAIL_OVER_H__ */ diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c index d5ab857d3..e7813f655 100644 --- a/src/providers/ipa/ipa_common.c +++ b/src/providers/ipa/ipa_common.c @@ -566,6 +566,7 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server) char *address; const char *safe_address; char *new_uri; + const char *srv_name; int ret; tmp_ctx = talloc_new(NULL); @@ -584,7 +585,7 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server) srvaddr = fo_get_server_hostent(server); if (!srvaddr) { DEBUG(1, ("FATAL: No hostent available for server (%s)\n", - fo_get_server_name(server))); + fo_get_server_str_name(server))); talloc_free(tmp_ctx); return; } @@ -612,7 +613,14 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server) return; } - new_uri = talloc_asprintf(service, "ldap://%s", fo_get_server_name(server)); + srv_name = fo_get_server_name(server); + if (srv_name == NULL) { + DEBUG(1, ("Could not get server host name\n")); + talloc_free(tmp_ctx); + return; + } + + new_uri = talloc_asprintf(service, "ldap://%s", srv_name); if (!new_uri) { DEBUG(2, ("Failed to copy URI ...\n")); talloc_free(tmp_ctx); diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c index 7b72660ad..24a6a4a4e 100644 --- a/src/providers/krb5/krb5_auth.c +++ b/src/providers/krb5/krb5_auth.c @@ -324,6 +324,9 @@ int krb5_auth_recv(struct tevent_req *req, int *pam_status, int *dp_err) return EOK; } +static struct tevent_req *krb5_next_kdc(struct tevent_req *req); +static struct tevent_req *krb5_next_kpasswd(struct tevent_req *req); + struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct be_ctx *be_ctx, @@ -511,16 +514,14 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx, kr->srv = NULL; kr->kpasswd_srv = NULL; - subreq = be_resolve_server_send(state, state->ev, state->be_ctx, - krb5_ctx->service->name); - if (subreq == NULL) { - DEBUG(1, ("be_resolve_server_send failed.\n")); - ret = ENOMEM; + + subreq = krb5_next_kdc(req); + if (!subreq) { + DEBUG(1, ("krb5_next_kdc failed.\n")); + ret = EIO; goto done; } - tevent_req_set_callback(subreq, krb5_resolve_kdc_done, req); - return req; done: @@ -551,16 +552,12 @@ static void krb5_resolve_kdc_done(struct tevent_req *subreq) kr->is_offline = true; } else { if (kr->krb5_ctx->kpasswd_service != NULL) { - subreq = be_resolve_server_send(state, state->ev, state->be_ctx, - kr->krb5_ctx->kpasswd_service->name); + subreq = krb5_next_kpasswd(req); if (subreq == NULL) { - DEBUG(1, ("be_resolve_server_send failed.\n")); - ret = ENOMEM; + DEBUG(1, ("krb5_next_kpasswd failed.\n")); + ret = EIO; goto failed; } - - tevent_req_set_callback(subreq, krb5_resolve_kpasswd_done, req); - return; } } @@ -710,7 +707,6 @@ done: } static struct tevent_req *krb5_next_server(struct tevent_req *req); -static struct tevent_req *krb5_next_kdc(struct tevent_req *req); static struct tevent_req *krb5_next_kpasswd(struct tevent_req *req); static void krb5_child_done(struct tevent_req *subreq) @@ -876,14 +872,16 @@ static void krb5_child_done(struct tevent_req *subreq) if (kr->kpasswd_srv != NULL) { /* ..which is unreachable by now.. */ if (msg_status == PAM_AUTHTOK_LOCK_BUSY) { - fo_set_port_status(kr->kpasswd_srv, PORT_NOT_WORKING); + be_fo_set_port_status(state->be_ctx, + kr->kpasswd_srv, PORT_NOT_WORKING); /* ..try to resolve next kpasswd server */ if (krb5_next_kpasswd(req) == NULL) { tevent_req_error(req, ENOMEM); } return; } else { - fo_set_port_status(kr->kpasswd_srv, PORT_WORKING); + be_fo_set_port_status(state->be_ctx, + kr->kpasswd_srv, PORT_WORKING); } } @@ -893,7 +891,7 @@ static void krb5_child_done(struct tevent_req *subreq) if (msg_status == PAM_AUTHINFO_UNAVAIL || (kr->kpasswd_srv == NULL && msg_status == PAM_AUTHTOK_LOCK_BUSY)) { if (kr->srv != NULL) { - fo_set_port_status(kr->srv, PORT_NOT_WORKING); + be_fo_set_port_status(state->be_ctx, kr->srv, PORT_NOT_WORKING); /* ..try to resolve next KDC */ if (krb5_next_kdc(req) == NULL) { tevent_req_error(req, ENOMEM); @@ -901,7 +899,7 @@ static void krb5_child_done(struct tevent_req *subreq) return; } } else if (kr->srv != NULL) { - fo_set_port_status(kr->srv, PORT_WORKING); + be_fo_set_port_status(state->be_ctx, kr->srv, PORT_WORKING); } /* Now only a successful authentication or password change is left. @@ -972,17 +970,20 @@ static struct tevent_req *krb5_next_server(struct tevent_req *req) switch (pd->cmd) { case SSS_PAM_AUTHENTICATE: case SSS_CMD_RENEW: - fo_set_port_status(state->kr->srv, PORT_NOT_WORKING); + be_fo_set_port_status(state->be_ctx, + state->kr->srv, PORT_NOT_WORKING); next_req = krb5_next_kdc(req); break; case SSS_PAM_CHAUTHTOK: case SSS_PAM_CHAUTHTOK_PRELIM: if (state->kr->kpasswd_srv) { - fo_set_port_status(state->kr->kpasswd_srv, PORT_NOT_WORKING); + be_fo_set_port_status(state->be_ctx, + state->kr->kpasswd_srv, PORT_NOT_WORKING); next_req = krb5_next_kpasswd(req); break; } else { - fo_set_port_status(state->kr->srv, PORT_NOT_WORKING); + be_fo_set_port_status(state->be_ctx, + state->kr->srv, PORT_NOT_WORKING); next_req = krb5_next_kdc(req); break; } @@ -1000,7 +1001,8 @@ static struct tevent_req *krb5_next_kdc(struct tevent_req *req) next_req = be_resolve_server_send(state, state->ev, state->be_ctx, - state->krb5_ctx->service->name); + state->krb5_ctx->service->name, + state->kr->srv == NULL ? true : false); if (next_req == NULL) { DEBUG(1, ("be_resolve_server_send failed.\n")); return NULL; @@ -1017,7 +1019,8 @@ static struct tevent_req *krb5_next_kpasswd(struct tevent_req *req) next_req = be_resolve_server_send(state, state->ev, state->be_ctx, - state->krb5_ctx->kpasswd_service->name); + state->krb5_ctx->kpasswd_service->name, + state->kr->kpasswd_srv == NULL ? true : false); if (next_req == NULL) { DEBUG(1, ("be_resolve_server_send failed.\n")); return NULL; diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c index 6e190d0c5..5fd164ed8 100644 --- a/src/providers/krb5/krb5_common.c +++ b/src/providers/krb5/krb5_common.c @@ -380,7 +380,7 @@ static void krb5_resolve_callback(void *private_data, struct fo_server *server) srvaddr = fo_get_server_hostent(server); if (!srvaddr) { DEBUG(1, ("FATAL: No hostent available for server (%s)\n", - fo_get_server_name(server))); + fo_get_server_str_name(server))); return; } diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c index 8109e247d..7a5ec4b73 100644 --- a/src/providers/ldap/ldap_auth.c +++ b/src/providers/ldap/ldap_auth.c @@ -512,7 +512,8 @@ static struct tevent_req *auth_get_server(struct tevent_req *req) next_req = be_resolve_server_send(state, state->ev, state->ctx->be, - state->sdap_service->name); + state->sdap_service->name, + state->srv == NULL ? true : false); if (!next_req) { DEBUG(1, ("be_resolve_server_send failed.\n")); return NULL; @@ -583,7 +584,8 @@ static void auth_connect_done(struct tevent_req *subreq) if (ret) { if (state->srv) { /* mark this server as bad if connection failed */ - fo_set_port_status(state->srv, PORT_NOT_WORKING); + be_fo_set_port_status(state->ctx->be, + state->srv, PORT_NOT_WORKING); } if (ret == ETIMEDOUT) { if (auth_get_server(req) == NULL) { @@ -595,7 +597,7 @@ static void auth_connect_done(struct tevent_req *subreq) tevent_req_error(req, ret); return; } else if (state->srv) { - fo_set_port_status(state->srv, PORT_WORKING); + be_fo_set_port_status(state->ctx->be, state->srv, PORT_WORKING); } ret = get_user_dn(state, state->ctx->be->sysdb, state->ctx->opts, diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c index 1291079a1..0fc61ea0a 100644 --- a/src/providers/ldap/ldap_common.c +++ b/src/providers/ldap/ldap_common.c @@ -534,6 +534,7 @@ static void sdap_uri_callback(void *private_data, struct fo_server *server) struct resolv_hostent *srvaddr; struct sockaddr_storage *sockaddr; const char *tmp; + const char *srv_name; char *new_uri; tmp_ctx = talloc_new(NULL); @@ -553,7 +554,7 @@ static void sdap_uri_callback(void *private_data, struct fo_server *server) srvaddr = fo_get_server_hostent(server); if (!srvaddr) { DEBUG(1, ("FATAL: No hostent available for server (%s)\n", - fo_get_server_name(server))); + fo_get_server_str_name(server))); talloc_free(tmp_ctx); return; } @@ -571,8 +572,16 @@ static void sdap_uri_callback(void *private_data, struct fo_server *server) DEBUG(1, ("Unknown service, using ldap\n")); tmp = SSS_LDAP_SRV_NAME; } + + srv_name = fo_get_server_name(server); + if (srv_name == NULL) { + DEBUG(1, ("Could not get server host name\n")); + talloc_free(tmp_ctx); + return; + } + new_uri = talloc_asprintf(service, "%s://%s:%d", - tmp, fo_get_server_name(server), + tmp, srv_name, fo_get_server_port(server)); } else { new_uri = talloc_strdup(service, tmp); diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c index 7bce2cdf8..59bc67468 100644 --- a/src/providers/ldap/sdap_async_connection.c +++ b/src/providers/ldap/sdap_async_connection.c @@ -844,7 +844,8 @@ static struct tevent_req *sdap_kinit_next_kdc(struct tevent_req *req) next_req = be_resolve_server_send(state, state->ev, state->be, - state->krb_service_name); + state->krb_service_name, + state->kdc_srv == NULL ? true : false); if (next_req == NULL) { DEBUG(1, ("be_resolve_server_send failed.\n")); return NULL; @@ -922,7 +923,7 @@ static void sdap_kinit_done(struct tevent_req *subreq) return; } else { if (kerr == KRB5_KDC_UNREACH) { - fo_set_port_status(state->kdc_srv, PORT_NOT_WORKING); + be_fo_set_port_status(state->be, state->kdc_srv, PORT_NOT_WORKING); nextreq = sdap_kinit_next_kdc(req); if (!nextreq) { tevent_req_error(req, ENOMEM); @@ -1148,7 +1149,6 @@ struct tevent_req *sdap_cli_connect_send(TALLOC_CTX *memctx, state->be = be; state->srv = NULL; state->srv_opts = NULL; - state->be = be; state->use_rootdse = !skip_rootdse; ret = sdap_cli_resolve_next(req); @@ -1171,7 +1171,8 @@ static int sdap_cli_resolve_next(struct tevent_req *req) /* NOTE: this call may cause service->uri to be refreshed * with a new valid server. Do not use service->uri before */ subreq = be_resolve_server_send(state, state->ev, - state->be, state->service->name); + state->be, state->service->name, + state->srv == NULL ? true : false); if (!subreq) { return ENOMEM; } @@ -1231,7 +1232,7 @@ static void sdap_cli_connect_done(struct tevent_req *subreq) talloc_zfree(subreq); if (ret) { /* retry another server */ - fo_set_port_status(state->srv, PORT_NOT_WORKING); + be_fo_set_port_status(state->be, state->srv, PORT_NOT_WORKING); ret = sdap_cli_resolve_next(req); if (ret != EOK) { tevent_req_error(req, ret); @@ -1305,7 +1306,7 @@ static void sdap_cli_rootdse_done(struct tevent_req *subreq) talloc_zfree(subreq); if (ret) { if (ret == ETIMEDOUT) { /* retry another server */ - fo_set_port_status(state->srv, PORT_NOT_WORKING); + be_fo_set_port_status(state->be, state->srv, PORT_NOT_WORKING); ret = sdap_cli_resolve_next(req); if (ret != EOK) { tevent_req_error(req, ret); @@ -1418,7 +1419,7 @@ static void sdap_cli_kinit_done(struct tevent_req *subreq) talloc_zfree(subreq); if (ret) { if (ret == ETIMEDOUT) { /* child timed out, retry another server */ - fo_set_port_status(state->srv, PORT_NOT_WORKING); + be_fo_set_port_status(state->be, state->srv, PORT_NOT_WORKING); ret = sdap_cli_resolve_next(req); if (ret != EOK) { tevent_req_error(req, ret); @@ -1502,7 +1503,7 @@ int sdap_cli_connect_recv(struct tevent_req *req, if (tevent_req_is_error(req, &tstate, &err)) { /* mark the server as bad if connection failed */ if (state->srv) { - fo_set_port_status(state->srv, PORT_NOT_WORKING); + be_fo_set_port_status(state->be, state->srv, PORT_NOT_WORKING); } else { if (can_retry) { *can_retry = false; @@ -1514,7 +1515,7 @@ int sdap_cli_connect_recv(struct tevent_req *req, } return EIO; } else if (state->srv) { - fo_set_port_status(state->srv, PORT_WORKING); + be_fo_set_port_status(state->be, state->srv, PORT_WORKING); } if (gsh) { diff --git a/src/tests/fail_over-tests.c b/src/tests/fail_over-tests.c index 84016dd9b..4b1e5791b 100644 --- a/src/tests/fail_over-tests.c +++ b/src/tests/fail_over-tests.c @@ -170,13 +170,15 @@ test_resolve_service_callback(struct tevent_req *req) if (task->new_server_status >= 0) fo_set_server_status(server, task->new_server_status); - he = fo_get_server_hostent(server); - fail_if(he == NULL, "%s: fo_get_server_hostent() returned NULL"); - for (i = 0; he->addr_list[i]; i++) { - char buf[256]; - - inet_ntop(he->family, he->addr_list[i]->ipaddr, buf, sizeof(buf)); - fail_if(strcmp(buf, "127.0.0.1") != 0 && strcmp(buf, "::1") != 0); + if (fo_get_server_name(server) != NULL) { + he = fo_get_server_hostent(server); + fail_if(he == NULL, "%s: fo_get_server_hostent() returned NULL"); + for (i = 0; he->addr_list[i]; i++) { + char buf[256]; + + inet_ntop(he->family, he->addr_list[i]->ipaddr, buf, sizeof(buf)); + fail_if(strcmp(buf, "127.0.0.1") != 0 && strcmp(buf, "::1") != 0); + } } } -- cgit