/*
SSSD
Fail over helper functions.
Authors:
Martin Nagy <mnagy@redhat.com>
Jakub Hrozek <jhrozek@redhat.com>
Copyright (C) Red Hat, Inc 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/time.h>
#include <errno.h>
#include <stdbool.h>
#include <strings.h>
#include <talloc.h>
#include "util/dlinklist.h"
#include "util/refcount.h"
#include "util/util.h"
#include "providers/fail_over.h"
#include "resolv/async_resolv.h"
#define STATUS_DIFF(p, now) ((now).tv_sec - (p)->last_status_change.tv_sec)
#define SERVER_NAME(s) ((s)->common ? (s)->common->name : "(no name)")
#define DEFAULT_PORT_STATUS PORT_NEUTRAL
#define DEFAULT_SERVER_STATUS SERVER_NAME_NOT_RESOLVED
#define DEFAULT_SRV_STATUS SRV_NEUTRAL
enum srv_lookup_status {
SRV_NEUTRAL, /* We didn't try this SRV lookup yet */
SRV_RESOLVED, /* This SRV lookup is resolved */
SRV_RESOLVE_ERROR, /* Could not resolve this SRV lookup */
SRV_EXPIRED /* Need to refresh the SRV query */
};
struct fo_ctx {
struct fo_service *service_list;
struct server_common *server_common_list;
struct fo_options *opts;
};
struct fo_service {
struct fo_service *prev;
struct fo_service *next;
struct fo_ctx *ctx;
char *name;
struct fo_server *active_server;
struct fo_server *last_tried_server;
struct fo_server *server_list;
};
struct fo_server {
struct fo_server *prev;
struct fo_server *next;
void *user_data;
int port;
int port_status;
struct srv_data *srv_data;
struct fo_service *service;
struct timeval last_status_change;
struct server_common *common;
};
struct server_common {
REFCOUNT_COMMON;
struct fo_ctx *ctx;
struct server_common *prev;
struct server_common *next;
char *name;
struct resolv_hostent *rhostent;
struct resolve_service_request *request_list;
int server_status;
struct timeval last_status_change;
};
struct srv_data {
char *dns_domain;
char *discovery_domain;
char *sssd_domain;
char *proto;
char *srv;
struct fo_server *meta;
int srv_lookup_status;
struct timeval last_status_change;
};
struct resolve_service_request {
struct resolve_service_request *prev;
struct resolve_service_request *next;
struct server_common *server_common;
struct tevent_req *req;
};
struct status {
int value;
struct timeval last_change;
};
struct fo_ctx *
fo_context_init(TALLOC_CTX *mem_ctx, struct fo_options *opts)
{
struct fo_ctx *ctx;
ctx = talloc_zero(mem_ctx, struct fo_ctx);
if (ctx == NULL) {
DEBUG(1, ("No memory\n"));
return NULL;
}
ctx->opts = talloc_zero(ctx, struct fo_options);
if (ctx->opts == NULL) {
DEBUG(1, ("No memory\n"));
return NULL;
}
ctx->opts->srv_retry_timeout = opts->srv_retry_timeout;
ctx->opts->retry_timeout = opts->retry_timeout;
ctx->opts->family_order = opts->family_order;
DEBUG(3, ("Created new fail over context, retry timeout is %d\n",
ctx->opts->retry_timeout));
return ctx;
}
static const char *
str_port_status(enum port_status status)
{
switch (status) {
case PORT_NEUTRAL:
return "neutral";
case PORT_WORKING:
return "working";
case PORT_NOT_WORKING:
return "not working";
}
return "unknown port status";
}
static const char *
str_srv_data_status(enum srv_lookup_status status)
{
switch (status) {
case SRV_NEUTRAL:
return "neutral";
case SRV_RESOLVED:
return "resolved";
case SRV_RESOLVE_ERROR:
return "not resolved";
case SRV_EXPIRED:
return "expired";
}
return "unknown SRV lookup status";
}
static const char *
str_server_status(enum server_status status)
{
switch (status) {
case SERVER_NAME_NOT_RESOLVED:
return "name not resolved";
case SERVER_RESOLVING_NAME:
return "resolving name";
case SERVER_NAME_RESOLVED:
return "name resolved";
case SERVER_WORKING:
return "working";
case SERVER_NOT_WORKING:
return "not working";
}
return "unknown server status";
}
int fo_is_srv_lookup(struct fo_server *s)
{
return s && s->srv_data;
}
static char *
get_srv_query(TALLOC_CTX *mem_ctx, struct fo_server *server)
{
char *query;
if (!fo_is_srv_lookup(server)) {
return NULL;
}
query = talloc_asprintf(mem_ctx, "_%s._%s.%s", server->srv_data->srv,
server->srv_data->proto,
server->srv_data->dns_domain);
return query;
}
static struct fo_server *
collapse_srv_lookup(struct fo_server *server)
{
struct fo_server *tmp, *meta;
meta = server->srv_data->meta;
DEBUG(4, ("Need to refresh SRV lookup for domain %s\n",
meta->srv_data->dns_domain));
if (server != meta) {
while (server->prev && server->prev->srv_data == meta->srv_data) {
tmp = server->prev;
DLIST_REMOVE(server->service->server_list, tmp);
talloc_zfree(tmp);
}
while (server->next && server->next->srv_data == meta->srv_data) {
tmp = server->next;
DLIST_REMOVE(server->service->server_list, tmp);
talloc_zfree(tmp);
}
if (server == server->service->active_server) {
server->service->active_server = NULL;
}
if (server == server->service->last_tried_server) {
server->service->last_tried_server = meta;
}
/* add back the meta server to denote SRV lookup */
DLIST_ADD_AFTER(server->service->server_list, meta, server);
DLIST_REMOVE(server->service->server_list, server);
talloc_zfree(server);
}
meta->srv_data->srv_lookup_status = SRV_NEUTRAL;
meta->srv_data->last_status_change.tv_sec = 0;
return meta;
}
static enum srv_lookup_status
get_srv_data_status(struct srv_data *data)
{
struct timeval tv;
time_t timeout;
timeout = data->meta->service->ctx->opts->srv_retry_timeout;
gettimeofday(&tv, NULL);
if (timeout && STATUS_DIFF(data, tv) > timeout) {
switch(data->srv_lookup_status) {
case SRV_EXPIRED:
case SRV_NEUTRAL:
break;
case SRV_RESOLVED:
data->srv_lookup_status = SRV_EXPIRED;
data->last_status_change.tv_sec = 0;
break;
case SRV_RESOLVE_ERROR:
data->srv_lookup_status = SRV_NEUTRAL;
data->last_status_change.tv_sec = 0;
break;
default:
DEBUG(1, ("Unknown state for SRV server!\n"));
}
}
return data->srv_lookup_status;
}
static void
set_srv_data_status(struct srv_data *data, enum srv_lookup_status status)
{
DEBUG(4, ("Marking SRV lookup of service '%s' as '%s'\n",
data->meta->service->name, str_srv_data_status(status)));
gettimeofday(&data->last_status_change, NULL);
data->srv_lookup_status = status;
}
|