95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang/*
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang SSSD
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang Fail over helper functions.
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang Authors:
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang Martin Nagy <mnagy@redhat.com>
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang Jakub Hrozek <jhrozek@redhat.com>
3e4305eb6432c65bb0dfe8d9af9707dfc92383eaChristian Maeder
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang Copyright (C) Red Hat, Inc 2010
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang This program is free software; you can redistribute it and/or modify
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang it under the terms of the GNU General Public License as published by
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang the Free Software Foundation; either version 3 of the License, or
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang (at your option) any later version.
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang This program is distributed in the hope that it will be useful,
67d5e49547d78aa56a8f9ba5e64a950b730eba66Till Mossakowski but WITHOUT ANY WARRANTY; without even the implied warranty of
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang GNU General Public License for more details.
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang You should have received a copy of the GNU General Public License
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang along with this program. If not, see <http://www.gnu.org/licenses/>.
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang*/
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang#include <sys/time.h>
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang#include <errno.h>
806bce66335c88260a63e7524b1efc68d8dfacc1Heng Jiang#include <stdbool.h>
806bce66335c88260a63e7524b1efc68d8dfacc1Heng Jiang#include <strings.h>
806bce66335c88260a63e7524b1efc68d8dfacc1Heng Jiang#include <talloc.h>
806bce66335c88260a63e7524b1efc68d8dfacc1Heng Jiang
806bce66335c88260a63e7524b1efc68d8dfacc1Heng Jiang#include "util/dlinklist.h"
806bce66335c88260a63e7524b1efc68d8dfacc1Heng Jiang#include "util/refcount.h"
806bce66335c88260a63e7524b1efc68d8dfacc1Heng Jiang#include "util/util.h"
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang#include "providers/fail_over.h"
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang#include "resolv/async_resolv.h"
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang#define STATUS_DIFF(p, now) ((now).tv_sec - (p)->last_status_change.tv_sec)
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang#define SERVER_NAME(s) ((s)->common ? (s)->common->name : "(no name)")
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang#define DEFAULT_PORT_STATUS PORT_NEUTRAL
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang#define DEFAULT_SERVER_STATUS SERVER_NAME_NOT_RESOLVED
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang#define DEFAULT_SRV_STATUS SRV_NEUTRAL
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiangenum srv_lookup_status {
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang SRV_NEUTRAL, /* We didn't try this SRV lookup yet */
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang SRV_RESOLVED, /* This SRV lookup is resolved */
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang SRV_RESOLVE_ERROR, /* Could not resolve this SRV lookup */
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang SRV_EXPIRED /* Need to refresh the SRV query */
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang};
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiangstruct fo_ctx {
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct fo_service *service_list;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct server_common *server_common_list;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct fo_options *opts;
806bce66335c88260a63e7524b1efc68d8dfacc1Heng Jiang
806bce66335c88260a63e7524b1efc68d8dfacc1Heng Jiang fo_srv_lookup_plugin_send_t srv_send_fn;
806bce66335c88260a63e7524b1efc68d8dfacc1Heng Jiang fo_srv_lookup_plugin_recv_t srv_recv_fn;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang void *srv_pvt;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang};
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiangstruct fo_service {
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct fo_service *prev;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct fo_service *next;
806bce66335c88260a63e7524b1efc68d8dfacc1Heng Jiang
806bce66335c88260a63e7524b1efc68d8dfacc1Heng Jiang struct fo_ctx *ctx;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang char *name;
806bce66335c88260a63e7524b1efc68d8dfacc1Heng Jiang struct fo_server *active_server;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct fo_server *last_tried_server;
c10df434f8e16b46ccf703bf2e38b799f7bcb38bHeng Jiang struct fo_server *server_list;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang /* Function pointed by user_data_cmp returns 0 if user_data is equal
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang * or nonzero value if not. Set to NULL if no user data comparison
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang * is needed in fail over duplicate servers detection.
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang */
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang datacmp_fn user_data_cmp;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang};
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiangstruct fo_server {
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang REFCOUNT_COMMON;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct fo_server *prev;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct fo_server *next;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang bool primary;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang void *user_data;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang int port;
c10df434f8e16b46ccf703bf2e38b799f7bcb38bHeng Jiang enum port_status port_status;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct srv_data *srv_data;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct fo_service *service;
c10df434f8e16b46ccf703bf2e38b799f7bcb38bHeng Jiang struct timeval last_status_change;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct server_common *common;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
6c8dc7ab7cb52a12cba748fe0f6b8d8d17a95eb9Heng Jiang TALLOC_CTX *fo_internal_owner;
6c8dc7ab7cb52a12cba748fe0f6b8d8d17a95eb9Heng Jiang};
0b31ea5ec0e20f17d55845a5d803c48466e01ca3Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangstruct server_common {
6c8dc7ab7cb52a12cba748fe0f6b8d8d17a95eb9Heng Jiang REFCOUNT_COMMON;
0b31ea5ec0e20f17d55845a5d803c48466e01ca3Heng Jiang
0b31ea5ec0e20f17d55845a5d803c48466e01ca3Heng Jiang struct fo_ctx *ctx;
0b31ea5ec0e20f17d55845a5d803c48466e01ca3Heng Jiang
0b31ea5ec0e20f17d55845a5d803c48466e01ca3Heng Jiang struct server_common *prev;
0b31ea5ec0e20f17d55845a5d803c48466e01ca3Heng Jiang struct server_common *next;
0b31ea5ec0e20f17d55845a5d803c48466e01ca3Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang char *name;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct resolv_hostent *rhostent;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct resolve_service_request *request_list;
31ac08a9e5233b83a63fd5aaac494c32305c4c77Heng Jiang enum server_status server_status;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct timeval last_status_change;
3e4305eb6432c65bb0dfe8d9af9707dfc92383eaChristian Maeder};
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
f0a4ace924cef940ca4cc646fa180df70ef405d8Klaus Luettichstruct srv_data {
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang char *dns_domain;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang char *discovery_domain;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang char *sssd_domain;
0b31ea5ec0e20f17d55845a5d803c48466e01ca3Heng Jiang char *proto;
0b31ea5ec0e20f17d55845a5d803c48466e01ca3Heng Jiang char *srv;
0b31ea5ec0e20f17d55845a5d803c48466e01ca3Heng Jiang
0b31ea5ec0e20f17d55845a5d803c48466e01ca3Heng Jiang struct fo_server *meta;
0b31ea5ec0e20f17d55845a5d803c48466e01ca3Heng Jiang
0b31ea5ec0e20f17d55845a5d803c48466e01ca3Heng Jiang int srv_lookup_status;
0b31ea5ec0e20f17d55845a5d803c48466e01ca3Heng Jiang int ttl;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct timeval last_status_change;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang};
6c8dc7ab7cb52a12cba748fe0f6b8d8d17a95eb9Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiangstruct resolve_service_request {
31ac08a9e5233b83a63fd5aaac494c32305c4c77Heng Jiang struct resolve_service_request *prev;
31ac08a9e5233b83a63fd5aaac494c32305c4c77Heng Jiang struct resolve_service_request *next;
806bce66335c88260a63e7524b1efc68d8dfacc1Heng Jiang
806bce66335c88260a63e7524b1efc68d8dfacc1Heng Jiang struct server_common *server_common;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct tevent_req *req;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct tevent_context *ev;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang};
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiangstruct status {
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang int value;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct timeval last_change;
6c8dc7ab7cb52a12cba748fe0f6b8d8d17a95eb9Heng Jiang};
6c8dc7ab7cb52a12cba748fe0f6b8d8d17a95eb9Heng Jiang
6c8dc7ab7cb52a12cba748fe0f6b8d8d17a95eb9Heng Jiangstruct fo_ctx *
6c8dc7ab7cb52a12cba748fe0f6b8d8d17a95eb9Heng Jiangfo_context_init(TALLOC_CTX *mem_ctx, struct fo_options *opts)
6c8dc7ab7cb52a12cba748fe0f6b8d8d17a95eb9Heng Jiang{
6c8dc7ab7cb52a12cba748fe0f6b8d8d17a95eb9Heng Jiang struct fo_ctx *ctx;
6c8dc7ab7cb52a12cba748fe0f6b8d8d17a95eb9Heng Jiang
6c8dc7ab7cb52a12cba748fe0f6b8d8d17a95eb9Heng Jiang ctx = talloc_zero(mem_ctx, struct fo_ctx);
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang if (ctx == NULL) {
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang DEBUG(SSSDBG_CRIT_FAILURE, "No memory\n");
6c8dc7ab7cb52a12cba748fe0f6b8d8d17a95eb9Heng Jiang return NULL;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang }
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang ctx->opts = talloc_zero(ctx, struct fo_options);
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang if (ctx->opts == NULL) {
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang DEBUG(SSSDBG_CRIT_FAILURE, "No memory\n");
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return NULL;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang }
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang ctx->opts->srv_retry_neg_timeout = opts->srv_retry_neg_timeout;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang ctx->opts->retry_timeout = opts->retry_timeout;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang ctx->opts->family_order = opts->family_order;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang ctx->opts->service_resolv_timeout = opts->service_resolv_timeout;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
806bce66335c88260a63e7524b1efc68d8dfacc1Heng Jiang DEBUG(SSSDBG_TRACE_FUNC,
806bce66335c88260a63e7524b1efc68d8dfacc1Heng Jiang "Created new fail over context, retry timeout is %ld\n",
806bce66335c88260a63e7524b1efc68d8dfacc1Heng Jiang ctx->opts->retry_timeout);
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return ctx;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang}
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiangstatic const char *
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiangstr_port_status(enum port_status status)
f0a4ace924cef940ca4cc646fa180df70ef405d8Klaus Luettich{
f0a4ace924cef940ca4cc646fa180df70ef405d8Klaus Luettich switch (status) {
f0a4ace924cef940ca4cc646fa180df70ef405d8Klaus Luettich case PORT_NEUTRAL:
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return "neutral";
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang case PORT_WORKING:
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return "working";
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang case PORT_NOT_WORKING:
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return "not working";
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang }
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return "unknown port status";
6c8dc7ab7cb52a12cba748fe0f6b8d8d17a95eb9Heng Jiang}
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiangstatic const char *
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiangstr_srv_data_status(enum srv_lookup_status status)
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang{
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang switch (status) {
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang case SRV_NEUTRAL:
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return "neutral";
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang case SRV_RESOLVED:
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return "resolved";
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang case SRV_RESOLVE_ERROR:
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return "not resolved";
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang case SRV_EXPIRED:
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return "expired";
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang }
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return "unknown SRV lookup status";
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang}
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiangstatic const char *
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiangstr_server_status(enum server_status status)
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang{
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang switch (status) {
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang case SERVER_NAME_NOT_RESOLVED:
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return "name not resolved";
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang case SERVER_RESOLVING_NAME:
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return "resolving name";
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang case SERVER_NAME_RESOLVED:
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return "name resolved";
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang case SERVER_WORKING:
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return "working";
175c9e5fde43fc804a8d25198133288669b9d54cKlaus Luettich case SERVER_NOT_WORKING:
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang return "not working";
175c9e5fde43fc804a8d25198133288669b9d54cKlaus Luettich }
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return "unknown server status";
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang}
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiangint fo_is_srv_lookup(struct fo_server *s)
b91c4dbd00294ce29ab1ae84ad4e8c93ca5ad943Heng Jiang{
b91c4dbd00294ce29ab1ae84ad4e8c93ca5ad943Heng Jiang return s && s->srv_data;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang}
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangstatic void fo_server_free(struct fo_server *server)
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang{
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang if (server == NULL) {
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang return;
b91c4dbd00294ce29ab1ae84ad4e8c93ca5ad943Heng Jiang }
b91c4dbd00294ce29ab1ae84ad4e8c93ca5ad943Heng Jiang
b91c4dbd00294ce29ab1ae84ad4e8c93ca5ad943Heng Jiang talloc_free(server->fo_internal_owner);
b91c4dbd00294ce29ab1ae84ad4e8c93ca5ad943Heng Jiang}
b91c4dbd00294ce29ab1ae84ad4e8c93ca5ad943Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangstatic struct fo_server *
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangcollapse_srv_lookup(struct fo_server **_server)
b91c4dbd00294ce29ab1ae84ad4e8c93ca5ad943Heng Jiang{
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang struct fo_server *tmp, *meta, *server;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang server = *_server;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang meta = server->srv_data->meta;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang DEBUG(SSSDBG_CONF_SETTINGS, "Need to refresh SRV lookup for domain %s\n",
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang meta->srv_data->dns_domain);
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang if (server != meta) {
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang while (server->prev && server->prev->srv_data == meta->srv_data) {
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang tmp = server->prev;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang DLIST_REMOVE(server->service->server_list, tmp);
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang fo_server_free(tmp);
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang }
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang while (server->next && server->next->srv_data == meta->srv_data) {
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang tmp = server->next;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang DLIST_REMOVE(server->service->server_list, tmp);
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang fo_server_free(tmp);
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang }
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang if (server == server->service->active_server) {
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang server->service->active_server = NULL;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang }
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang if (server == server->service->last_tried_server) {
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang server->service->last_tried_server = meta;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang }
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang /* add back the meta server to denote SRV lookup */
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang DLIST_ADD_AFTER(server->service->server_list, meta, server);
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang DLIST_REMOVE(server->service->server_list, server);
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang fo_server_free(server);
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang }
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang meta->srv_data->srv_lookup_status = SRV_NEUTRAL;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang meta->srv_data->last_status_change.tv_sec = 0;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang *_server = NULL;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang return meta;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang}
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangstatic enum srv_lookup_status
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangget_srv_data_status(struct srv_data *data)
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang{
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang struct timeval tv;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang time_t timeout;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang gettimeofday(&tv, NULL);
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang /* Determine timeout value based on state of previous lookup. */
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang if (data->srv_lookup_status == SRV_RESOLVE_ERROR) {
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang timeout = data->meta->service->ctx->opts->srv_retry_neg_timeout;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang } else {
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang timeout = data->ttl;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang }
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang if (STATUS_DIFF(data, tv) > timeout) {
c51b5677113ce7260c44afb3c5932eea6c875e27Heng Jiang switch(data->srv_lookup_status) {
67d5e49547d78aa56a8f9ba5e64a950b730eba66Till Mossakowski case SRV_EXPIRED:
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang case SRV_NEUTRAL:
67d5e49547d78aa56a8f9ba5e64a950b730eba66Till Mossakowski break;
67d5e49547d78aa56a8f9ba5e64a950b730eba66Till Mossakowski case SRV_RESOLVED:
67d5e49547d78aa56a8f9ba5e64a950b730eba66Till Mossakowski data->srv_lookup_status = SRV_EXPIRED;
67d5e49547d78aa56a8f9ba5e64a950b730eba66Till Mossakowski data->last_status_change.tv_sec = 0;
67d5e49547d78aa56a8f9ba5e64a950b730eba66Till Mossakowski break;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang case SRV_RESOLVE_ERROR:
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang data->srv_lookup_status = SRV_NEUTRAL;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang data->last_status_change.tv_sec = 0;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang DEBUG(SSSDBG_TRACE_FUNC,
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang "Changing state of SRV lookup from 'SRV_RESOLVE_ERROR' to "
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang "'SRV_NEUTRAL'.\n");
67d5e49547d78aa56a8f9ba5e64a950b730eba66Till Mossakowski break;
67d5e49547d78aa56a8f9ba5e64a950b730eba66Till Mossakowski default:
67d5e49547d78aa56a8f9ba5e64a950b730eba66Till Mossakowski DEBUG(SSSDBG_CRIT_FAILURE, "Unknown state for SRV server!\n");
67d5e49547d78aa56a8f9ba5e64a950b730eba66Till Mossakowski }
67d5e49547d78aa56a8f9ba5e64a950b730eba66Till Mossakowski }
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang return data->srv_lookup_status;
67d5e49547d78aa56a8f9ba5e64a950b730eba66Till Mossakowski}
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangstatic void
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangset_srv_data_status(struct srv_data *data, enum srv_lookup_status status)
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang{
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang DEBUG(SSSDBG_CONF_SETTINGS, "Marking SRV lookup of service '%s' as '%s'\n",
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang data->meta->service->name, str_srv_data_status(status));
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang gettimeofday(&data->last_status_change, NULL);
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang data->srv_lookup_status = status;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang}
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang/*
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang * This function will return the status of the server. If the status was
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang * last updated a long time ago, we will first reset the status.
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang */
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangstatic enum server_status
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangget_server_status(struct fo_server *server)
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang{
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang struct timeval tv;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang time_t timeout;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang if (server->common == NULL)
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang return SERVER_NAME_RESOLVED;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang DEBUG(SSSDBG_TRACE_LIBS,
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang "Status of server '%s' is '%s'\n", SERVER_NAME(server),
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang str_server_status(server->common->server_status));
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang timeout = server->service->ctx->opts->retry_timeout;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang gettimeofday(&tv, NULL);
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang if (timeout != 0 && server->common->server_status == SERVER_NOT_WORKING) {
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang if (STATUS_DIFF(server->common, tv) > timeout) {
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang DEBUG(SSSDBG_CONF_SETTINGS, "Reseting the server status of '%s'\n",
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang SERVER_NAME(server));
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang server->common->server_status = SERVER_NAME_NOT_RESOLVED;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang server->common->last_status_change.tv_sec = tv.tv_sec;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang }
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang }
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang if (server->common->rhostent && STATUS_DIFF(server->common, tv) >
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang server->common->rhostent->addr_list[0]->ttl) {
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang DEBUG(SSSDBG_CONF_SETTINGS,
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang "Hostname resolution expired, resetting the server "
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang "status of '%s'\n", SERVER_NAME(server));
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang fo_set_server_status(server, SERVER_NAME_NOT_RESOLVED);
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang }
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang return server->common->server_status;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang}
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang/*
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang * This function will return the status of the service. If the status was
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang * last updated a long time ago, we will first reset the status.
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang */
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangstatic enum port_status
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangget_port_status(struct fo_server *server)
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang{
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang struct timeval tv;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang time_t timeout;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang DEBUG(SSSDBG_TRACE_LIBS,
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang "Port status of port %d for server '%s' is '%s'\n", server->port,
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang SERVER_NAME(server), str_port_status(server->port_status));
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang timeout = server->service->ctx->opts->retry_timeout;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang if (timeout != 0 && server->port_status == PORT_NOT_WORKING) {
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang gettimeofday(&tv, NULL);
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang if (STATUS_DIFF(server, tv) > timeout) {
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang DEBUG(SSSDBG_CONF_SETTINGS,
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang "Reseting the status of port %d for server '%s'\n",
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang server->port, SERVER_NAME(server));
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang server->port_status = PORT_NEUTRAL;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang server->last_status_change.tv_sec = tv.tv_sec;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang }
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang }
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang return server->port_status;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang}
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangstatic int
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangserver_works(struct fo_server *server)
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang{
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang if (get_server_status(server) == SERVER_NOT_WORKING)
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang return 0;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang return 1;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang}
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangstatic int
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangservice_works(struct fo_server *server)
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang{
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang if (!server_works(server))
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang return 0;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang if (get_port_status(server) == PORT_NOT_WORKING)
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang return 0;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang return 1;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang}
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangstatic int
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangservice_destructor(struct fo_service *service)
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang{
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang DLIST_REMOVE(service->ctx->service_list, service);
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang return 0;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang}
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangint
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangfo_new_service(struct fo_ctx *ctx, const char *name,
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang datacmp_fn user_data_cmp,
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang struct fo_service **_service)
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang{
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang struct fo_service *service;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang int ret;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang DEBUG(SSSDBG_TRACE_FUNC, "Creating new service '%s'\n", name);
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang ret = fo_get_service(ctx, name, &service);
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang if (ret == EOK) {
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang DEBUG(SSSDBG_FUNC_DATA, "Service '%s' already exists\n", name);
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang if (_service) {
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang *_service = service;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang }
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang return EEXIST;
6c8dc7ab7cb52a12cba748fe0f6b8d8d17a95eb9Heng Jiang } else if (ret != ENOENT) {
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang return ret;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang }
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang service = talloc_zero(ctx, struct fo_service);
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang if (service == NULL)
67d5e49547d78aa56a8f9ba5e64a950b730eba66Till Mossakowski return ENOMEM;
67d5e49547d78aa56a8f9ba5e64a950b730eba66Till Mossakowski
b91c4dbd00294ce29ab1ae84ad4e8c93ca5ad943Heng Jiang service->name = talloc_strdup(service, name);
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang if (service->name == NULL) {
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang talloc_free(service);
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return ENOMEM;
c10df434f8e16b46ccf703bf2e38b799f7bcb38bHeng Jiang }
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang service->user_data_cmp = user_data_cmp;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang service->ctx = ctx;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang DLIST_ADD(ctx->service_list, service);
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
c10df434f8e16b46ccf703bf2e38b799f7bcb38bHeng Jiang talloc_set_destructor(service, service_destructor);
b3c65285705f6d184b5f8b00b1a328d96b6b19c5Heng Jiang if (_service) {
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang *_service = service;
b91c4dbd00294ce29ab1ae84ad4e8c93ca5ad943Heng Jiang }
c51b5677113ce7260c44afb3c5932eea6c875e27Heng Jiang
b91c4dbd00294ce29ab1ae84ad4e8c93ca5ad943Heng Jiang return EOK;
b3c65285705f6d184b5f8b00b1a328d96b6b19c5Heng Jiang}
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiangint
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangfo_get_service(struct fo_ctx *ctx, const char *name,
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct fo_service **_service)
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang{
b3c65285705f6d184b5f8b00b1a328d96b6b19c5Heng Jiang struct fo_service *service;
c51b5677113ce7260c44afb3c5932eea6c875e27Heng Jiang
c51b5677113ce7260c44afb3c5932eea6c875e27Heng Jiang DLIST_FOR_EACH(service, ctx->service_list) {
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang if (!strcmp(name, service->name)) {
b91c4dbd00294ce29ab1ae84ad4e8c93ca5ad943Heng Jiang *_service = service;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return EOK;
c10df434f8e16b46ccf703bf2e38b799f7bcb38bHeng Jiang }
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang }
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return ENOENT;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang}
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiangstatic int
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiangget_server_common(TALLOC_CTX *mem_ctx, struct fo_ctx *ctx, const char *name,
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct server_common **_common)
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang{
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang struct server_common *common;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
b91c4dbd00294ce29ab1ae84ad4e8c93ca5ad943Heng Jiang DLIST_FOR_EACH(common, ctx->server_common_list) {
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang if (!strcasecmp(name, common->name)) {
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang *_common = rc_reference(mem_ctx, struct server_common, common);
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang if (*_common == NULL)
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return ENOMEM;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return EOK;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang }
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang }
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang return ENOENT;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang}
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiangstatic int server_common_destructor(void *memptr)
b3c65285705f6d184b5f8b00b1a328d96b6b19c5Heng Jiang{
c51b5677113ce7260c44afb3c5932eea6c875e27Heng Jiang struct server_common *common;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
b3c65285705f6d184b5f8b00b1a328d96b6b19c5Heng Jiang common = talloc_get_type(memptr, struct server_common);
b3c65285705f6d184b5f8b00b1a328d96b6b19c5Heng Jiang if (common->request_list) {
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang DEBUG(SSSDBG_CRIT_FAILURE,
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang "BUG: pending requests still associated with this server\n");
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return -1;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang }
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang DLIST_REMOVE(common->ctx->server_common_list, common);
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return 0;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang}
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiangstatic struct server_common *
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiangcreate_server_common(TALLOC_CTX *mem_ctx, struct fo_ctx *ctx, const char *name)
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang{
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang struct server_common *common;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang common = rc_alloc(mem_ctx, struct server_common);
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang if (common == NULL) {
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return NULL;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang }
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang common->name = talloc_strdup(common, name);
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang if (common->name == NULL) {
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return NULL;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang }
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang common->ctx = ctx;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang common->prev = NULL;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang common->next = NULL;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang common->rhostent = NULL;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang common->request_list = NULL;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang common->server_status = DEFAULT_SERVER_STATUS;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang common->last_status_change.tv_sec = 0;
44ea7e3effe9200ccc6abd3231ae56cf5cfb0fb8Heng Jiang common->last_status_change.tv_usec = 0;
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang talloc_set_destructor((TALLOC_CTX *) common, server_common_destructor);
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang DLIST_ADD_END(ctx->server_common_list, common, struct server_common *);
95242ab07e9aa13b37c16cac36a75d190e1766e4Heng Jiang return common;
}
static struct fo_server *
fo_server_alloc(struct fo_service *service, int port,
void *user_data, bool primary)
{
static struct fo_server *server;
TALLOC_CTX *server_owner;
server_owner = talloc_new(service);
if (server_owner == NULL) {
return NULL;
}
server = rc_alloc(server_owner, struct fo_server);
if (server == NULL) {
return NULL;
}
server->fo_internal_owner = server_owner;
server->common = NULL;
server->next = NULL;
server->prev = NULL;
server->srv_data = NULL;
server->last_status_change.tv_sec = 0;
server->last_status_change.tv_usec = 0;
server->port = port;
server->user_data = user_data;
server->service = service;
server->port_status = DEFAULT_PORT_STATUS;
server->primary = primary;
return server;
}
int
fo_add_srv_server(struct fo_service *service, const char *srv,
const char *discovery_domain, const char *sssd_domain,
const char *proto, void *user_data)
{
struct fo_server *server;
DEBUG(SSSDBG_TRACE_FUNC,
"Adding new SRV server to service '%s' using '%s'.\n",
service->name, proto);
DLIST_FOR_EACH(server, service->server_list) {
/* Compare user data only if user_data_cmp and both arguments
* are not NULL.
*/
if (server->service->user_data_cmp && user_data && server->user_data) {
if (server->service->user_data_cmp(server->user_data, user_data)) {
continue;
}
}
if (fo_is_srv_lookup(server)) {
if (((discovery_domain == NULL &&
server->srv_data->dns_domain == NULL) ||
(discovery_domain != NULL &&
server->srv_data->dns_domain != NULL &&
strcasecmp(server->srv_data->dns_domain, discovery_domain) == 0)) &&
strcasecmp(server->srv_data->proto, proto) == 0) {
return EEXIST;
}
}
}
/* SRV servers are always primary */
server = fo_server_alloc(service, 0, user_data, true);
if (server == NULL) {
return ENOMEM;
}
/* add the SRV-specific data */
server->srv_data = talloc_zero(service, struct srv_data);
if (server->srv_data == NULL)
return ENOMEM;
server->srv_data->proto = talloc_strdup(server->srv_data, proto);
server->srv_data->srv = talloc_strdup(server->srv_data, srv);
if (server->srv_data->proto == NULL ||
server->srv_data->srv == NULL)
return ENOMEM;
if (discovery_domain) {
server->srv_data->discovery_domain = talloc_strdup(server->srv_data,
discovery_domain);
if (server->srv_data->discovery_domain == NULL)
return ENOMEM;
server->srv_data->dns_domain = talloc_strdup(server->srv_data,
discovery_domain);
if (server->srv_data->dns_domain == NULL)
return ENOMEM;
}
server->srv_data->sssd_domain =
talloc_strdup(server->srv_data, sssd_domain);
if (server->srv_data->sssd_domain == NULL)
return ENOMEM;
server->srv_data->meta = server;
server->srv_data->srv_lookup_status = DEFAULT_SRV_STATUS;
server->srv_data->last_status_change.tv_sec = 0;
DLIST_ADD_END(service->server_list, server, struct fo_server *);
return EOK;
}
static struct fo_server *
create_fo_server(struct fo_service *service, const char *name,
int port, void *user_data, bool primary)
{
struct fo_server *server;
int ret;
server = fo_server_alloc(service, port, user_data, primary);
if (server == NULL)
return NULL;
server->port = port;
server->user_data = user_data;
server->service = service;
server->port_status = DEFAULT_PORT_STATUS;
server->primary = primary;
if (name != NULL) {
ret = get_server_common(server, service->ctx, name, &server->common);
if (ret == ENOENT) {
server->common = create_server_common(server, service->ctx, name);
if (server->common == NULL) {
fo_server_free(server);
return NULL;
}
} else if (ret != EOK) {
fo_server_free(server);
return NULL;
}
}
return server;
}
int
fo_get_server_count(struct fo_service *service)
{
struct fo_server *server;
int count = 0;
DLIST_FOR_EACH(server, service->server_list) {
count++;
}
return count;
}
static bool fo_server_match(struct fo_server *server,
const char *name,
int port,
void *user_data)
{
if (server->port != port) {
return false;
}
/* Compare user data only if user_data_cmp and both arguments
* are not NULL.
*/
if (server->service->user_data_cmp && server->user_data && user_data) {
if (server->service->user_data_cmp(server->user_data, user_data)) {
return false;
}
}
if (name == NULL && server->common == NULL) {
return true;
}
if (name != NULL &&
server->common != NULL && server->common->name != NULL) {
if (!strcasecmp(name, server->common->name))
return true;
}
return false;
}
static bool fo_server_cmp(struct fo_server *s1, struct fo_server *s2)
{
char *name = NULL;
if (s2->common != NULL) {
name = s2->common->name;
}
return fo_server_match(s1, name, s2->port, s2->user_data);
}
static bool fo_server_exists(struct fo_server *list,
const char *name,
int port,
void *user_data)
{
struct fo_server *server = NULL;
DLIST_FOR_EACH(server, list) {
if (fo_server_match(server, name, port, user_data)) {
return true;
}
}
return false;
}
static errno_t fo_add_server_to_list(struct fo_server **to_list,
struct fo_server *check_list,
struct fo_server *server,
const char *service_name)
{
const char *debug_name = NULL;
const char *name = NULL;
bool exists;
if (server->common == NULL || server->common->name == NULL) {
debug_name = "(no name)";
name = NULL;
} else {
debug_name = server->common->name;
name = server->common->name;
}
exists = fo_server_exists(check_list, name, server->port,
server->user_data);
if (exists) {
DEBUG(SSSDBG_TRACE_FUNC, "Server '%s:%d' for service '%s' "
"is already present\n", debug_name, server->port, service_name);
return EEXIST;
}
DLIST_ADD_END(*to_list, server, struct fo_server *);
DEBUG(SSSDBG_TRACE_FUNC, "Inserted %s server '%s:%d' to service "
"'%s'\n", (server->primary ? "primary" : "backup"),
debug_name, server->port, service_name);
return EOK;
}
static errno_t fo_add_server_list(struct fo_service *service,
struct fo_server *after_server,
struct fo_server_info *servers,
size_t num_servers,
struct srv_data *srv_data,
void *user_data,
bool primary,
struct fo_server **_last_server)
{
struct fo_server *server = NULL;
struct fo_server *last_server = NULL;
struct fo_server *srv_list = NULL;
size_t i;
errno_t ret;
for (i = 0; i < num_servers; i++) {
server = create_fo_server(service, servers[i].host, servers[i].port,
user_data, primary);
if (server == NULL) {
return ENOMEM;
}
server->srv_data = srv_data;
ret = fo_add_server_to_list(&srv_list, service->server_list,
server, service->name);
if (ret != EOK) {
fo_server_free(server);
continue;
}
last_server = server;
}
if (srv_list != NULL) {
DLIST_ADD_LIST_AFTER(service->server_list, after_server,
srv_list, struct fo_server *);
}
if (_last_server != NULL) {
*_last_server = last_server == NULL ? after_server : last_server;
}
return EOK;
}
int
fo_add_server(struct fo_service *service, const char *name, int port,
void *user_data, bool primary)
{
struct fo_server *server;
errno_t ret;
server = create_fo_server(service, name, port, user_data, primary);
if (!server) {
return ENOMEM;
}
ret = fo_add_server_to_list(&service->server_list, service->server_list,
server, service->name);
if (ret != EOK) {
fo_server_free(server);
}
return ret;
}
void fo_ref_server(TALLOC_CTX *ref_ctx,
struct fo_server *server)
{
if (server) {
server = rc_reference(ref_ctx, struct fo_server, server);
}
}
static int
get_first_server_entity(struct fo_service *service, struct fo_server **_server)
{
struct fo_server *server;
/* If we already have a working server, use that one. */
server = service->active_server;
if (server != NULL) {
if (service_works(server) && fo_is_server_primary(server)) {
goto done;
}
service->active_server = NULL;
}
/*
* Otherwise iterate through the server list.
*/
/* First, try primary servers after the last one we tried.
* (only if the last one was primary as well)
*/
if (service->last_tried_server != NULL &&
service->last_tried_server->primary) {
if (service->last_tried_server->port_status == PORT_NEUTRAL &&
server_works(service->last_tried_server)) {
server = service->last_tried_server;
goto done;
}
DLIST_FOR_EACH(server, service->last_tried_server->next) {
/* Go only through primary servers */
if (!server->primary) continue;
if (service_works(server)) {
goto done;
}
}
}
/* If none were found, try at the start, primary first */
DLIST_FOR_EACH(server, service->server_list) {
/* First iterate only over primary servers */
if (!server->primary) continue;
if (service_works(server)) {
goto done;
}
if (server == service->last_tried_server) {
break;
}
}
DLIST_FOR_EACH(server, service->server_list) {
/* Now iterate only over backup servers */
if (server->primary) continue;
if (service_works(server)) {
goto done;
}
}
service->last_tried_server = NULL;
return ENOENT;
done:
service->last_tried_server = server;
*_server = server;
return EOK;
}
static int
resolve_service_request_destructor(struct resolve_service_request *request)
{
DLIST_REMOVE(request->server_common->request_list, request);
return 0;
}
static int
set_lookup_hook(struct tevent_context *ev,
struct fo_server *server,
struct tevent_req *req)
{
struct resolve_service_request *request;
request = talloc(req, struct resolve_service_request);
if (request == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "No memory\n");
talloc_free(request);
return ENOMEM;
}
request->server_common = rc_reference(request, struct server_common,
server->common);
if (request->server_common == NULL) {
talloc_free(request);
return ENOMEM;
}
request->ev = ev;
request->req = req;
DLIST_ADD(server->common->request_list, request);
talloc_set_destructor(request, resolve_service_request_destructor);
return EOK;
}
/*******************************************************************
* Get server to connect to. *
*******************************************************************/
struct resolve_service_state {
struct fo_server *server;
struct resolv_ctx *resolv;
struct tevent_context *ev;
struct tevent_timer *timeout_handler;
struct fo_ctx *fo_ctx;
};
static errno_t fo_resolve_service_activate_timeout(struct tevent_req *req,
struct tevent_context *ev, const unsigned long timeout_seconds);
static void fo_resolve_service_cont(struct tevent_req *subreq);
static void fo_resolve_service_done(struct tevent_req *subreq);
static bool fo_resolve_service_server(struct tevent_req *req);
/* Forward declarations for SRV resolving */
static struct tevent_req *
resolve_srv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
struct resolv_ctx *resolv, struct fo_ctx *ctx,
struct fo_server *server);
static int
resolve_srv_recv(struct tevent_req *req, struct fo_server **server);
struct tevent_req *
fo_resolve_service_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
struct resolv_ctx *resolv, struct fo_ctx *ctx,
struct fo_service *service)
{
int ret;
struct fo_server *server;
struct tevent_req *req;
struct tevent_req *subreq;
struct resolve_service_state *state;
DEBUG(SSSDBG_CONF_SETTINGS,
"Trying to resolve service '%s'\n", service->name);
req = tevent_req_create(mem_ctx, &state, struct resolve_service_state);
if (req == NULL)
return NULL;
state->resolv = resolv;
state->ev = ev;
state->fo_ctx = ctx;
ret = get_first_server_entity(service, &server);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,
"No available servers for service '%s'\n", service->name);
goto done;
}
/* Activate per-service timeout handler */
ret = fo_resolve_service_activate_timeout(req, ev,
ctx->opts->service_resolv_timeout);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "Could not set service timeout\n");
goto done;
}
if (fo_is_srv_lookup(server)) {
/* Don't know the server yet, must do a SRV lookup */
subreq = resolve_srv_send(state, ev, resolv,
ctx, server);
if (subreq == NULL) {
ret = ENOMEM;
goto done;
}
tevent_req_set_callback(subreq,
fo_resolve_service_cont,
req);
return req;
}
/* This is a regular server, just do hostname lookup */
state->server = server;
if (fo_resolve_service_server(req)) {
tevent_req_post(req, ev);
}
ret = EOK;
done:
if (ret != EOK) {
tevent_req_error(req, ret);
tevent_req_post(req, ev);
}
return req;
}
static void set_server_common_status(struct server_common *common,
enum server_status status);
static void
fo_resolve_service_timeout(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval tv, void *pvt)
{
struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
DEBUG(SSSDBG_MINOR_FAILURE, "Service resolving timeout reached\n");
tevent_req_error(req, ETIMEDOUT);
}
static errno_t
fo_resolve_service_activate_timeout(struct tevent_req *req,
struct tevent_context *ev,
const unsigned long timeout_seconds)
{
struct timeval tv;
struct resolve_service_state *state = tevent_req_data(req,
struct resolve_service_state);
tv = tevent_timeval_current();
tv = tevent_timeval_add(&tv, timeout_seconds, 0);
state->timeout_handler = tevent_add_timer(ev, state, tv,
fo_resolve_service_timeout, req);
if (state->timeout_handler == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_timer failed.\n");
return ENOMEM;
}
DEBUG(SSSDBG_TRACE_INTERNAL, "Resolve timeout set to %lu seconds\n",
timeout_seconds);
return EOK;
}
/* SRV resolving finished, see if we got server to work with */
static void
fo_resolve_service_cont(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(subreq,
struct tevent_req);
struct resolve_service_state *state = tevent_req_data(req,
struct resolve_service_state);
int ret;
ret = resolve_srv_recv(subreq, &state->server);
talloc_zfree(subreq);
/* We will proceed normally on ERR_SRV_DUPLICATES and if the server
* is already being resolved, we hook to that request. */
if (ret != EOK && ret != ERR_SRV_DUPLICATES) {
tevent_req_error(req, ret);
return;
}
fo_resolve_service_server(req);
}
static bool
fo_resolve_service_server(struct tevent_req *req)
{
struct resolve_service_state *state = tevent_req_data(req,
struct resolve_service_state);
struct tevent_req *subreq;
int ret;
switch (get_server_status(state->server)) {
case SERVER_NAME_NOT_RESOLVED: /* Request name resolution. */
subreq = resolv_gethostbyname_send(state->server->common,
state->ev, state->resolv,
state->server->common->name,
state->fo_ctx->opts->family_order,
default_host_dbs);
if (subreq == NULL) {
tevent_req_error(req, ENOMEM);
return true;
}
tevent_req_set_callback(subreq, fo_resolve_service_done,
state->server->common);
fo_set_server_status(state->server, SERVER_RESOLVING_NAME);
/* FALLTHROUGH */
case SERVER_RESOLVING_NAME:
/* Name resolution is already under way. Just add ourselves into the
* waiting queue so we get notified after the operation is finished. */
ret = set_lookup_hook(state->ev, state->server, req);
if (ret != EOK) {
tevent_req_error(req, ret);
return true;
}
break;
default: /* The name is already resolved. Return immediately. */
tevent_req_done(req);
return true;
}
return false;
}
static void
fo_resolve_service_done(struct tevent_req *subreq)
{
struct server_common *common = tevent_req_callback_data(subreq,
struct server_common);
int resolv_status;
struct resolve_service_request *request;
int ret;
if (common->rhostent != NULL) {
talloc_zfree(common->rhostent);
}
ret = resolv_gethostbyname_recv(subreq, common,
&resolv_status, NULL,
&common->rhostent);
talloc_zfree(subreq);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failed to resolve server '%s': %s\n",
common->name,
resolv_strerror(resolv_status));
/* If the resolver failed to resolve a hostname but did not
* encounter an error, tell the caller to retry another server.
*
* If there are no more servers to try, the next request would
* just shortcut with ENOENT.
*/
if (ret == ENOENT) {
ret = EAGAIN;
}
set_server_common_status(common, SERVER_NOT_WORKING);
} else {
set_server_common_status(common, SERVER_NAME_RESOLVED);
}
/* Take care of all requests for this server. */
while ((request = common->request_list) != NULL) {
DLIST_REMOVE(common->request_list, request);
/* If the request callback decresed refcount on the returned
* server, we would have crashed as common would not be valid
* anymore. Rather schedule the notify for next tev iteration
*/
tevent_req_defer_callback(request->req, request->ev);
if (ret) {
tevent_req_error(request->req, ret);
} else {
tevent_req_done(request->req);
}
}
}
int
fo_resolve_service_recv(struct tevent_req *req,
TALLOC_CTX *ref_ctx,
struct fo_server **server)
{
struct resolve_service_state *state;
state = tevent_req_data(req, struct resolve_service_state);
/* always return the server if asked for, otherwise the caller
* cannot mark it as faulty in case we return an error */
if (server != NULL) {
fo_ref_server(ref_ctx, state->server);
*server = state->server;
}
TEVENT_REQ_RETURN_ON_ERROR(req);
return EOK;
}
/*******************************************************************
* Resolve the server to connect to using a SRV query. *
*******************************************************************/
static void resolve_srv_done(struct tevent_req *subreq);
struct resolve_srv_state {
struct fo_server *meta;
struct fo_service *service;
struct fo_server *out;
struct resolv_ctx *resolv;
struct tevent_context *ev;
struct fo_ctx *fo_ctx;
};
static struct tevent_req *
resolve_srv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
struct resolv_ctx *resolv, struct fo_ctx *ctx,
struct fo_server *server)
{
int ret;
struct tevent_req *req;
struct tevent_req *subreq;
struct resolve_srv_state *state;
int status;
req = tevent_req_create(mem_ctx, &state, struct resolve_srv_state);
if (req == NULL)
return NULL;
state->service = server->service;
state->ev = ev;
state->resolv = resolv;
state->fo_ctx = ctx;
state->meta = server->srv_data->meta;
status = get_srv_data_status(server->srv_data);
DEBUG(SSSDBG_FUNC_DATA, "The status of SRV lookup is %s\n",
str_srv_data_status(status));
switch(status) {
case SRV_EXPIRED: /* Need a refresh */
state->meta = collapse_srv_lookup(&server);
/* FALLTHROUGH.
* "server" might be invalid now if the SRV
* query collapsed
* */
case SRV_NEUTRAL: /* Request SRV lookup */
if (server != NULL && server != state->meta) {
/* A server created by expansion of meta server was marked as
* neutral. We have to collapse the servers and issue new
* SRV resolution. */
state->meta = collapse_srv_lookup(&server);
}
if (ctx->srv_send_fn == NULL || ctx->srv_recv_fn == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "No SRV lookup plugin is set\n");
ret = ENOTSUP;
goto done;
}
subreq = ctx->srv_send_fn(state, ev,
state->meta->srv_data->srv,
state->meta->srv_data->proto,
state->meta->srv_data->discovery_domain,
ctx->srv_pvt);
if (subreq == NULL) {
ret = ENOMEM;
goto done;
}
tevent_req_set_callback(subreq, resolve_srv_done, req);
break;
case SRV_RESOLVE_ERROR: /* query could not be resolved but don't retry yet */
ret = EIO;
state->out = server;
/* The port status was reseted to neutral but we still haven't reached
* timeout to try to resolve SRV record again. We will set the port
* status back to not working. */
fo_set_port_status(state->meta, PORT_NOT_WORKING);
goto done;
case SRV_RESOLVED: /* The query is resolved and valid. Return. */
state->out = server;
tevent_req_done(req);
tevent_req_post(req, state->ev);
return req;
default:
DEBUG(SSSDBG_CRIT_FAILURE,
"Unexpected status %d for a SRV server\n", status);
ret = EIO;
goto done;
}
ret = EOK;
done:
if (ret != EOK) {
tevent_req_error(req, ret);
tevent_req_post(req, ev);
}
return req;
}
static void
resolve_srv_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(subreq,
struct tevent_req);
struct resolve_srv_state *state = tevent_req_data(req,
struct resolve_srv_state);
struct fo_server *last_server = NULL;
struct fo_server_info *primary_servers = NULL;
struct fo_server_info *backup_servers = NULL;
size_t num_primary_servers = 0;
size_t num_backup_servers = 0;
char *dns_domain = NULL;
int ret;
uint32_t ttl;
ret = state->fo_ctx->srv_recv_fn(state, subreq, &dns_domain, &ttl,
&primary_servers, &num_primary_servers,
&backup_servers, &num_backup_servers);
talloc_free(subreq);
switch (ret) {
case EOK:
if ((num_primary_servers == 0 || primary_servers == NULL)
&& (num_backup_servers == 0 || backup_servers == NULL)) {
DEBUG(SSSDBG_CRIT_FAILURE, "SRV lookup plugin returned EOK but "
"no servers\n");
ret = EFAULT;
goto done;
}
state->meta->srv_data->ttl = ttl;
talloc_zfree(state->meta->srv_data->dns_domain);
state->meta->srv_data->dns_domain = talloc_steal(state->meta->srv_data,
dns_domain);
last_server = state->meta;
if (primary_servers != NULL) {
ret = fo_add_server_list(state->service, last_server,
primary_servers, num_primary_servers,
state->meta->srv_data,
state->meta->user_data,
true, &last_server);
if (ret != EOK) {
goto done;
}
}
if (backup_servers != NULL) {
ret = fo_add_server_list(state->service, last_server,
backup_servers, num_backup_servers,
state->meta->srv_data,
state->meta->user_data,
false, &last_server);
if (ret != EOK) {
goto done;
}
}
if (last_server == state->meta) {
/* SRV lookup returned only those servers that are already present.
* This may happen only when an ongoing SRV resolution already
* exist. We will return server, but won't set any state. */
DEBUG(SSSDBG_TRACE_FUNC, "SRV lookup did not return "
"any new server.\n");
ret = ERR_SRV_DUPLICATES;
/* Since no new server is returned, state->meta->next is NULL.
* We return last tried server if possible which is server
* from previous resolution of SRV record, and first server
* otherwise. */
if (state->service->last_tried_server != NULL) {
state->out = state->service->last_tried_server;
goto done;
}
state->out = state->service->server_list;
goto done;
}
/* At least one new server was inserted.
* We will return the first new server. */
if (state->meta->next == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE,
"BUG: state->meta->next is NULL\n");
ret = ERR_INTERNAL;
goto done;
}
state->out = state->meta->next;
/* And remove meta server from the server list. It will be
* inserted again during srv collapse. */
DLIST_REMOVE(state->service->server_list, state->meta);
if (state->service->last_tried_server == state->meta) {
state->service->last_tried_server = state->out;
}
set_srv_data_status(state->meta->srv_data, SRV_RESOLVED);
ret = EOK;
break;
case ERR_SRV_NOT_FOUND:
/* fall through */
case ERR_SRV_LOOKUP_ERROR:
fo_set_port_status(state->meta, PORT_NOT_WORKING);
/* fall through */
default:
DEBUG(SSSDBG_OP_FAILURE, "Unable to resolve SRV [%d]: %s\n",
ret, sss_strerror(ret));
}
done:
if (ret == ERR_SRV_DUPLICATES) {
tevent_req_error(req, ret);
return;
} else if (ret != EOK) {
state->out = state->meta;
set_srv_data_status(state->meta->srv_data, SRV_RESOLVE_ERROR);
tevent_req_error(req, ret);
return;
}
tevent_req_done(req);
}
static int
resolve_srv_recv(struct tevent_req *req, struct fo_server **server)
{
struct resolve_srv_state *state = tevent_req_data(req,
struct resolve_srv_state);
/* always return the server if asked for, otherwise the caller
* cannot mark it as faulty in case we return an error */
if (server) {
*server = state->out;
}
TEVENT_REQ_RETURN_ON_ERROR(req);
return EOK;
}
/*******************************************************************
* Get Fully Qualified Domain Name of the host machine *
*******************************************************************/
static void
set_server_common_status(struct server_common *common,
enum server_status status)
{
DEBUG(SSSDBG_CONF_SETTINGS, "Marking server '%s' as '%s'\n", common->name,
str_server_status(status));
common->server_status = status;
gettimeofday(&common->last_status_change, NULL);
}
void
fo_set_server_status(struct fo_server *server, enum server_status status)
{
if (server->common == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Bug: Trying to set server status of a name-less server\n");
return;
}
set_server_common_status(server->common, status);
}
void
fo_set_port_status(struct fo_server *server, enum port_status status)
{
struct fo_server *siter;
DEBUG(SSSDBG_CONF_SETTINGS,
"Marking port %d of server '%s' as '%s'\n", server->port,
SERVER_NAME(server), str_port_status(status));
server->port_status = status;
gettimeofday(&server->last_status_change, NULL);
if (status == PORT_WORKING) {
fo_set_server_status(server, SERVER_WORKING);
server->service->active_server = server;
}
if (!server->common || !server->common->name) return;
/* It is possible to introduce duplicates when expanding SRV results
* into fo_server structures. Find the duplicates and set the same
* status */
DLIST_FOR_EACH(siter, server->service->server_list) {
if (fo_server_cmp(siter, server)) {
DEBUG(SSSDBG_TRACE_FUNC,
"Marking port %d of duplicate server '%s' as '%s'\n",
siter->port, SERVER_NAME(siter),
str_port_status(status));
siter->port_status = status;
gettimeofday(&siter->last_status_change, NULL);
}
}
}
struct fo_server *fo_get_active_server(struct fo_service *service)
{
return service->active_server;
}
void fo_try_next_server(struct fo_service *service)
{
struct fo_server *server;
if (!service) {
DEBUG(SSSDBG_CRIT_FAILURE, "Bug: No service supplied\n");
return;
}
server = service->active_server;
if (!server) {
return;
}
service->active_server = 0;
if (server->port_status == PORT_WORKING) {
server->port_status = PORT_NOT_WORKING;
}
}
void *
fo_get_server_user_data(struct fo_server *server)
{
return server->user_data;
}
int
fo_get_server_port(struct fo_server *server)
{
return server->port;
}
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)) {
return "SRV lookup meta-server";
}
return "unknown name";
}
return server->common->name;
}
struct resolv_hostent *
fo_get_server_hostent(struct fo_server *server)
{
if (server->common == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Bug: Trying to get hostent from a name-less server\n");
return NULL;
}
return server->common->rhostent;
}
bool
fo_is_server_primary(struct fo_server *server)
{
return server->primary;
}
time_t
fo_get_server_hostname_last_change(struct fo_server *server)
{
if (server->common == NULL) {
return 0;
}
return server->common->last_status_change.tv_sec;
}
time_t fo_get_service_retry_timeout(struct fo_service *svc)
{
if (svc == NULL || svc->ctx == NULL || svc->ctx->opts == NULL) {
return 0;
}
return svc->ctx->opts->retry_timeout;
}
void fo_reset_servers(struct fo_service *service)
{
struct fo_server *server;
DLIST_FOR_EACH(server, service->server_list) {
if (server->srv_data != NULL) {
set_srv_data_status(server->srv_data, SRV_NEUTRAL);
}
if (server->common) {
fo_set_server_status(server, SERVER_NAME_NOT_RESOLVED);
}
fo_set_port_status(server, PORT_NEUTRAL);
}
}
void fo_reset_services(struct fo_ctx *fo_ctx)
{
struct fo_service *service;
DEBUG(SSSDBG_TRACE_LIBS,
"Resetting all servers in all services\n");
DLIST_FOR_EACH(service, fo_ctx->service_list) {
fo_reset_servers(service);
}
}
bool fo_svc_has_server(struct fo_service *service, struct fo_server *server)
{
struct fo_server *srv;
DLIST_FOR_EACH(srv, service->server_list) {
if (srv == server) return true;
}
return false;
}
const char **fo_svc_server_list(TALLOC_CTX *mem_ctx,
struct fo_service *service,
size_t *_count)
{
const char **list;
const char *server;
struct fo_server *srv;
size_t count;
count = 0;
DLIST_FOR_EACH(srv, service->server_list) {
count++;
}
list = talloc_zero_array(mem_ctx, const char *, count + 1);
if (list == NULL) {
return NULL;
}
count = 0;
DLIST_FOR_EACH(srv, service->server_list) {
server = fo_get_server_name(srv);
if (server == NULL) {
/* _srv_ */
continue;
}
list[count] = talloc_strdup(list, server);
if (list[count] == NULL) {
talloc_free(list);
return NULL;
}
count++;
}
if (_count != NULL) {
*_count = count;
}
return list;
}
bool fo_set_srv_lookup_plugin(struct fo_ctx *ctx,
fo_srv_lookup_plugin_send_t send_fn,
fo_srv_lookup_plugin_recv_t recv_fn,
void *pvt)
{
if (ctx == NULL || send_fn == NULL || recv_fn == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Invalid parameters\n");
return false;
}
if (ctx->srv_send_fn != NULL || ctx->srv_recv_fn != NULL) {
DEBUG(SSSDBG_MINOR_FAILURE, "SRV lookup plugin is already set\n");
return false;
}
ctx->srv_send_fn = send_fn;
ctx->srv_recv_fn = recv_fn;
ctx->srv_pvt = talloc_steal(ctx, pvt);
return true;
}