LCOV - code coverage report
Current view: top level - providers - fail_over.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 505 697 72.5 %
Date: 2016-06-29 Functions: 48 60 80.0 %

          Line data    Source code
       1             : /*
       2             :    SSSD
       3             : 
       4             :    Fail over helper functions.
       5             : 
       6             :    Authors:
       7             :         Martin Nagy <mnagy@redhat.com>
       8             :         Jakub Hrozek <jhrozek@redhat.com>
       9             : 
      10             :    Copyright (C) Red Hat, Inc 2010
      11             : 
      12             :    This program is free software; you can redistribute it and/or modify
      13             :    it under the terms of the GNU General Public License as published by
      14             :    the Free Software Foundation; either version 3 of the License, or
      15             :    (at your option) any later version.
      16             : 
      17             :    This program is distributed in the hope that it will be useful,
      18             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      19             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      20             :    GNU General Public License for more details.
      21             : 
      22             :    You should have received a copy of the GNU General Public License
      23             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      24             : */
      25             : 
      26             : #include <sys/time.h>
      27             : 
      28             : #include <errno.h>
      29             : #include <stdbool.h>
      30             : #include <strings.h>
      31             : #include <talloc.h>
      32             : 
      33             : #include "util/dlinklist.h"
      34             : #include "util/refcount.h"
      35             : #include "util/util.h"
      36             : #include "providers/fail_over.h"
      37             : #include "resolv/async_resolv.h"
      38             : 
      39             : #define STATUS_DIFF(p, now) ((now).tv_sec - (p)->last_status_change.tv_sec)
      40             : #define SERVER_NAME(s) ((s)->common ? (s)->common->name : "(no name)")
      41             : 
      42             : #define DEFAULT_PORT_STATUS PORT_NEUTRAL
      43             : #define DEFAULT_SERVER_STATUS SERVER_NAME_NOT_RESOLVED
      44             : #define DEFAULT_SRV_STATUS SRV_NEUTRAL
      45             : 
      46             : enum srv_lookup_status {
      47             :     SRV_NEUTRAL,        /* We didn't try this SRV lookup yet */
      48             :     SRV_RESOLVED,       /* This SRV lookup is resolved       */
      49             :     SRV_RESOLVE_ERROR,   /* Could not resolve this SRV lookup */
      50             :     SRV_EXPIRED         /* Need to refresh the SRV query     */
      51             : };
      52             : 
      53             : struct fo_ctx {
      54             :     struct fo_service *service_list;
      55             :     struct server_common *server_common_list;
      56             : 
      57             :     struct fo_options *opts;
      58             : 
      59             :     fo_srv_lookup_plugin_send_t srv_send_fn;
      60             :     fo_srv_lookup_plugin_recv_t srv_recv_fn;
      61             :     void *srv_pvt;
      62             : };
      63             : 
      64             : struct fo_service {
      65             :     struct fo_service *prev;
      66             :     struct fo_service *next;
      67             : 
      68             :     struct fo_ctx *ctx;
      69             :     char *name;
      70             :     struct fo_server *active_server;
      71             :     struct fo_server *last_tried_server;
      72             :     struct fo_server *server_list;
      73             : 
      74             :     /* Function pointed by user_data_cmp returns 0 if user_data is equal
      75             :      * or nonzero value if not. Set to NULL if no user data comparison
      76             :      * is needed in fail over duplicate servers detection.
      77             :      */
      78             :     datacmp_fn user_data_cmp;
      79             : };
      80             : 
      81             : struct fo_server {
      82             :     REFCOUNT_COMMON;
      83             : 
      84             :     struct fo_server *prev;
      85             :     struct fo_server *next;
      86             : 
      87             :     bool primary;
      88             :     void *user_data;
      89             :     int port;
      90             :     enum port_status port_status;
      91             :     struct srv_data *srv_data;
      92             :     struct fo_service *service;
      93             :     struct timeval last_status_change;
      94             :     struct server_common *common;
      95             : 
      96             :     TALLOC_CTX *fo_internal_owner;
      97             : };
      98             : 
      99             : struct server_common {
     100             :     REFCOUNT_COMMON;
     101             : 
     102             :     struct fo_ctx *ctx;
     103             : 
     104             :     struct server_common *prev;
     105             :     struct server_common *next;
     106             : 
     107             :     char *name;
     108             :     struct resolv_hostent *rhostent;
     109             :     struct resolve_service_request *request_list;
     110             :     enum server_status server_status;
     111             :     struct timeval last_status_change;
     112             : };
     113             : 
     114             : struct srv_data {
     115             :     char *dns_domain;
     116             :     char *discovery_domain;
     117             :     char *sssd_domain;
     118             :     char *proto;
     119             :     char *srv;
     120             : 
     121             :     struct fo_server *meta;
     122             : 
     123             :     int srv_lookup_status;
     124             :     int ttl;
     125             :     struct timeval last_status_change;
     126             : };
     127             : 
     128             : struct resolve_service_request {
     129             :     struct resolve_service_request *prev;
     130             :     struct resolve_service_request *next;
     131             : 
     132             :     struct server_common *server_common;
     133             :     struct tevent_req *req;
     134             :     struct tevent_context *ev;
     135             : };
     136             : 
     137             : struct status {
     138             :     int value;
     139             :     struct timeval last_change;
     140             : };
     141             : 
     142             : struct fo_ctx *
     143          13 : fo_context_init(TALLOC_CTX *mem_ctx, struct fo_options *opts)
     144             : {
     145             :     struct fo_ctx *ctx;
     146             : 
     147          13 :     ctx = talloc_zero(mem_ctx, struct fo_ctx);
     148          13 :     if (ctx == NULL) {
     149           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "No memory\n");
     150           0 :         return NULL;
     151             :     }
     152          13 :     ctx->opts = talloc_zero(ctx, struct fo_options);
     153          13 :     if (ctx->opts == NULL) {
     154           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "No memory\n");
     155           0 :         return NULL;
     156             :     }
     157             : 
     158          13 :     ctx->opts->srv_retry_neg_timeout = opts->srv_retry_neg_timeout;
     159          13 :     ctx->opts->retry_timeout = opts->retry_timeout;
     160          13 :     ctx->opts->family_order  = opts->family_order;
     161          13 :     ctx->opts->service_resolv_timeout = opts->service_resolv_timeout;
     162             : 
     163          13 :     DEBUG(SSSDBG_TRACE_FUNC,
     164             :           "Created new fail over context, retry timeout is %ld\n",
     165             :            ctx->opts->retry_timeout);
     166          13 :     return ctx;
     167             : }
     168             : 
     169             : static const char *
     170           0 : str_port_status(enum port_status status)
     171             : {
     172           0 :     switch (status) {
     173             :     case PORT_NEUTRAL:
     174           0 :         return "neutral";
     175             :     case PORT_WORKING:
     176           0 :         return "working";
     177             :     case PORT_NOT_WORKING:
     178           0 :         return "not working";
     179             :     }
     180             : 
     181           0 :     return "unknown port status";
     182             : }
     183             : 
     184             : static const char *
     185           0 : str_srv_data_status(enum srv_lookup_status status)
     186             : {
     187           0 :     switch (status) {
     188             :     case SRV_NEUTRAL:
     189           0 :         return "neutral";
     190             :     case SRV_RESOLVED:
     191           0 :         return "resolved";
     192             :     case SRV_RESOLVE_ERROR:
     193           0 :         return "not resolved";
     194             :     case SRV_EXPIRED:
     195           0 :         return "expired";
     196             :     }
     197             : 
     198           0 :     return "unknown SRV lookup status";
     199             : }
     200             : 
     201             : static const char *
     202           0 : str_server_status(enum server_status status)
     203             : {
     204           0 :     switch (status) {
     205             :     case SERVER_NAME_NOT_RESOLVED:
     206           0 :         return "name not resolved";
     207             :     case SERVER_RESOLVING_NAME:
     208           0 :         return "resolving name";
     209             :     case SERVER_NAME_RESOLVED:
     210           0 :         return "name resolved";
     211             :     case SERVER_WORKING:
     212           0 :         return "working";
     213             :     case SERVER_NOT_WORKING:
     214           0 :         return "not working";
     215             :     }
     216             : 
     217           0 :     return "unknown server status";
     218             : }
     219             : 
     220          32 : int fo_is_srv_lookup(struct fo_server *s)
     221             : {
     222          32 :     return s && s->srv_data;
     223             : }
     224             : 
     225          12 : static void fo_server_free(struct fo_server *server)
     226             : {
     227          12 :     if (server == NULL) {
     228          12 :         return;
     229             :     }
     230             : 
     231          12 :     talloc_free(server->fo_internal_owner);
     232             : }
     233             : 
     234             : static struct fo_server *
     235           5 : collapse_srv_lookup(struct fo_server **_server)
     236             : {
     237             :     struct fo_server *tmp, *meta, *server;
     238             : 
     239           5 :     server = *_server;
     240           5 :     meta = server->srv_data->meta;
     241           5 :     DEBUG(SSSDBG_CONF_SETTINGS, "Need to refresh SRV lookup for domain %s\n",
     242             :               meta->srv_data->dns_domain);
     243             : 
     244           5 :     if (server != meta) {
     245          10 :         while (server->prev && server->prev->srv_data == meta->srv_data) {
     246           0 :             tmp = server->prev;
     247           0 :             DLIST_REMOVE(server->service->server_list, tmp);
     248           0 :             fo_server_free(tmp);
     249             :         }
     250          15 :         while (server->next && server->next->srv_data == meta->srv_data) {
     251           5 :             tmp = server->next;
     252           5 :             DLIST_REMOVE(server->service->server_list, tmp);
     253           5 :             fo_server_free(tmp);
     254             :         }
     255             : 
     256           5 :         if (server == server->service->active_server) {
     257           0 :             server->service->active_server = NULL;
     258             :         }
     259           5 :         if (server == server->service->last_tried_server) {
     260           5 :             server->service->last_tried_server = meta;
     261             :         }
     262             : 
     263             :         /* add back the meta server to denote SRV lookup */
     264           5 :         DLIST_ADD_AFTER(server->service->server_list, meta, server);
     265           5 :         DLIST_REMOVE(server->service->server_list, server);
     266           5 :         fo_server_free(server);
     267             :     }
     268             : 
     269           5 :     meta->srv_data->srv_lookup_status = SRV_NEUTRAL;
     270           5 :     meta->srv_data->last_status_change.tv_sec = 0;
     271             : 
     272           5 :     *_server = NULL;
     273             : 
     274           5 :     return meta;
     275             : }
     276             : 
     277             : static enum srv_lookup_status
     278          10 : get_srv_data_status(struct srv_data *data)
     279             : {
     280             :     struct timeval tv;
     281             :     time_t timeout;
     282             : 
     283          10 :     gettimeofday(&tv, NULL);
     284             : 
     285             :     /* Determine timeout value based on state of previous lookup. */
     286          10 :     if (data->srv_lookup_status == SRV_RESOLVE_ERROR) {
     287           0 :         timeout = data->meta->service->ctx->opts->srv_retry_neg_timeout;
     288             :     } else {
     289          10 :         timeout = data->ttl;
     290             :     }
     291             : 
     292          10 :     if (STATUS_DIFF(data, tv) > timeout) {
     293           7 :         switch(data->srv_lookup_status) {
     294             :         case SRV_EXPIRED:
     295             :         case SRV_NEUTRAL:
     296           3 :             break;
     297             :         case SRV_RESOLVED:
     298           4 :             data->srv_lookup_status = SRV_EXPIRED;
     299           4 :             data->last_status_change.tv_sec = 0;
     300           4 :             break;
     301             :         case SRV_RESOLVE_ERROR:
     302           0 :             data->srv_lookup_status = SRV_NEUTRAL;
     303           0 :             data->last_status_change.tv_sec = 0;
     304           0 :             DEBUG(SSSDBG_TRACE_FUNC,
     305             :                   "Changing state of SRV lookup from 'SRV_RESOLVE_ERROR' to "
     306             :                   "'SRV_NEUTRAL'.\n");
     307           0 :             break;
     308             :         default:
     309           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Unknown state for SRV server!\n");
     310             :         }
     311             :     }
     312             : 
     313          10 :     return data->srv_lookup_status;
     314             : }
     315             : 
     316             : static void
     317          10 : set_srv_data_status(struct srv_data *data, enum srv_lookup_status status)
     318             : {
     319          10 :     DEBUG(SSSDBG_CONF_SETTINGS, "Marking SRV lookup of service '%s' as '%s'\n",
     320             :               data->meta->service->name, str_srv_data_status(status));
     321             : 
     322          10 :     gettimeofday(&data->last_status_change, NULL);
     323          10 :     data->srv_lookup_status = status;
     324          10 : }
     325             : 
     326             : /*
     327             :  * This function will return the status of the server. If the status was
     328             :  * last updated a long time ago, we will first reset the status.
     329             :  */
     330             : static enum server_status
     331          61 : get_server_status(struct fo_server *server)
     332             : {
     333             :     struct timeval tv;
     334             :     time_t timeout;
     335             : 
     336          61 :     if (server->common == NULL)
     337           5 :         return SERVER_NAME_RESOLVED;
     338             : 
     339          56 :     DEBUG(SSSDBG_TRACE_LIBS,
     340             :           "Status of server '%s' is '%s'\n", SERVER_NAME(server),
     341             :               str_server_status(server->common->server_status));
     342             : 
     343          56 :     timeout = server->service->ctx->opts->retry_timeout;
     344          56 :     gettimeofday(&tv, NULL);
     345          56 :     if (timeout != 0 && server->common->server_status == SERVER_NOT_WORKING) {
     346          15 :         if (STATUS_DIFF(server->common, tv) > timeout) {
     347           0 :             DEBUG(SSSDBG_CONF_SETTINGS, "Reseting the server status of '%s'\n",
     348             :                       SERVER_NAME(server));
     349           0 :             server->common->server_status = SERVER_NAME_NOT_RESOLVED;
     350           0 :             server->common->last_status_change.tv_sec = tv.tv_sec;
     351             :         }
     352             :     }
     353             : 
     354          75 :     if (server->common->rhostent && STATUS_DIFF(server->common, tv) >
     355          19 :         server->common->rhostent->addr_list[0]->ttl) {
     356           0 :         DEBUG(SSSDBG_CONF_SETTINGS,
     357             :               "Hostname resolution expired, resetting the server "
     358             :                   "status of '%s'\n", SERVER_NAME(server));
     359           0 :         fo_set_server_status(server, SERVER_NAME_NOT_RESOLVED);
     360             :     }
     361             : 
     362          56 :     return server->common->server_status;
     363             : }
     364             : 
     365             : /*
     366             :  * This function will return the status of the service. If the status was
     367             :  * last updated a long time ago, we will first reset the status.
     368             :  */
     369             : static enum port_status
     370          18 : get_port_status(struct fo_server *server)
     371             : {
     372             :     struct timeval tv;
     373             :     time_t timeout;
     374             : 
     375          18 :     DEBUG(SSSDBG_TRACE_LIBS,
     376             :           "Port status of port %d for server '%s' is '%s'\n", server->port,
     377             :               SERVER_NAME(server), str_port_status(server->port_status));
     378             : 
     379          18 :     timeout = server->service->ctx->opts->retry_timeout;
     380          18 :     if (timeout != 0 && server->port_status == PORT_NOT_WORKING) {
     381           1 :         gettimeofday(&tv, NULL);
     382           1 :         if (STATUS_DIFF(server, tv) > timeout) {
     383           0 :             DEBUG(SSSDBG_CONF_SETTINGS,
     384             :                   "Reseting the status of port %d for server '%s'\n",
     385             :                       server->port, SERVER_NAME(server));
     386           0 :             server->port_status = PORT_NEUTRAL;
     387           0 :             server->last_status_change.tv_sec = tv.tv_sec;
     388             :         }
     389             :     }
     390             : 
     391          18 :     return server->port_status;
     392             : }
     393             : 
     394             : static int
     395          39 : server_works(struct fo_server *server)
     396             : {
     397          39 :     if (get_server_status(server) == SERVER_NOT_WORKING)
     398          15 :         return 0;
     399             : 
     400          24 :     return 1;
     401             : }
     402             : 
     403             : static int
     404          29 : service_works(struct fo_server *server)
     405             : {
     406          29 :     if (!server_works(server))
     407          11 :         return 0;
     408          18 :     if (get_port_status(server) == PORT_NOT_WORKING)
     409           1 :         return 0;
     410             : 
     411          17 :     return 1;
     412             : }
     413             : 
     414             : static int
     415          17 : service_destructor(struct fo_service *service)
     416             : {
     417          17 :     DLIST_REMOVE(service->ctx->service_list, service);
     418          17 :     return 0;
     419             : }
     420             : 
     421             : int
     422          18 : fo_new_service(struct fo_ctx *ctx, const char *name,
     423             :                datacmp_fn user_data_cmp,
     424             :                struct fo_service **_service)
     425             : {
     426             :     struct fo_service *service;
     427             :     int ret;
     428             : 
     429          18 :     DEBUG(SSSDBG_TRACE_FUNC, "Creating new service '%s'\n", name);
     430          18 :     ret = fo_get_service(ctx, name, &service);
     431          18 :     if (ret == EOK) {
     432           1 :         DEBUG(SSSDBG_FUNC_DATA, "Service '%s' already exists\n", name);
     433           1 :         if (_service) {
     434           1 :                 *_service = service;
     435             :         }
     436           1 :         return EEXIST;
     437          17 :     } else if (ret != ENOENT) {
     438           0 :         return ret;
     439             :     }
     440             : 
     441          17 :     service = talloc_zero(ctx, struct fo_service);
     442          17 :     if (service == NULL)
     443           0 :         return ENOMEM;
     444             : 
     445          17 :     service->name = talloc_strdup(service, name);
     446          17 :     if (service->name == NULL) {
     447           0 :         talloc_free(service);
     448           0 :         return ENOMEM;
     449             :     }
     450             : 
     451          17 :     service->user_data_cmp = user_data_cmp;
     452             : 
     453          17 :     service->ctx = ctx;
     454          17 :     DLIST_ADD(ctx->service_list, service);
     455             : 
     456          17 :     talloc_set_destructor(service, service_destructor);
     457          17 :     if (_service) {
     458          17 :         *_service = service;
     459             :     }
     460             : 
     461          17 :     return EOK;
     462             : }
     463             : 
     464             : int
     465          38 : fo_get_service(struct fo_ctx *ctx, const char *name,
     466             :                struct fo_service **_service)
     467             : {
     468             :     struct fo_service *service;
     469             : 
     470         137 :     DLIST_FOR_EACH(service, ctx->service_list) {
     471         110 :         if (!strcmp(name, service->name)) {
     472          11 :             *_service = service;
     473          11 :             return EOK;
     474             :         }
     475             :     }
     476             : 
     477          27 :     return ENOENT;
     478             : }
     479             : 
     480             : static int
     481          26 : get_server_common(TALLOC_CTX *mem_ctx, struct fo_ctx *ctx, const char *name,
     482             :                   struct server_common **_common)
     483             : {
     484             :     struct server_common *common;
     485             : 
     486          39 :     DLIST_FOR_EACH(common, ctx->server_common_list) {
     487          23 :         if (!strcasecmp(name, common->name)) {
     488          10 :             *_common = rc_reference(mem_ctx, struct server_common, common);
     489          10 :             if (*_common == NULL)
     490           0 :                 return ENOMEM;
     491          10 :             return EOK;
     492             :         }
     493             :     }
     494             : 
     495          16 :     return ENOENT;
     496             : }
     497             : 
     498          16 : static int server_common_destructor(void *memptr)
     499             : {
     500             :     struct server_common *common;
     501             : 
     502          16 :     common = talloc_get_type(memptr, struct server_common);
     503          16 :     if (common->request_list) {
     504           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     505             :               "BUG: pending requests still associated with this server\n");
     506           0 :         return -1;
     507             :     }
     508          16 :     DLIST_REMOVE(common->ctx->server_common_list, common);
     509             : 
     510          16 :     return 0;
     511             : }
     512             : 
     513             : static struct server_common *
     514          16 : create_server_common(TALLOC_CTX *mem_ctx, struct fo_ctx *ctx, const char *name)
     515             : {
     516             :     struct server_common *common;
     517             : 
     518          16 :     common = rc_alloc(mem_ctx, struct server_common);
     519          16 :     if (common == NULL) {
     520           0 :         return NULL;
     521             :     }
     522             : 
     523          16 :     common->name = talloc_strdup(common, name);
     524          16 :     if (common->name == NULL) {
     525           0 :         return NULL;
     526             :     }
     527             : 
     528          16 :     common->ctx = ctx;
     529          16 :     common->prev = NULL;
     530          16 :     common->next = NULL;
     531          16 :     common->rhostent = NULL;
     532          16 :     common->request_list = NULL;
     533          16 :     common->server_status = DEFAULT_SERVER_STATUS;
     534          16 :     common->last_status_change.tv_sec = 0;
     535          16 :     common->last_status_change.tv_usec = 0;
     536             : 
     537          16 :     talloc_set_destructor((TALLOC_CTX *) common, server_common_destructor);
     538          16 :     DLIST_ADD_END(ctx->server_common_list, common, struct server_common *);
     539          16 :     return common;
     540             : }
     541             : 
     542             : static struct fo_server *
     543          30 : fo_server_alloc(struct fo_service *service, int port,
     544             :                 void *user_data, bool primary)
     545             : {
     546             :     static struct fo_server *server;
     547             :     TALLOC_CTX *server_owner;
     548             : 
     549          30 :     server_owner = talloc_new(service);
     550          30 :     if (server_owner == NULL) {
     551           0 :         return NULL;
     552             :     }
     553             : 
     554          30 :     server = rc_alloc(server_owner, struct fo_server);
     555          30 :     if (server == NULL) {
     556           0 :         return NULL;
     557             :     }
     558             : 
     559          30 :     server->fo_internal_owner = server_owner;
     560             : 
     561          30 :     server->common = NULL;
     562          30 :     server->next = NULL;
     563          30 :     server->prev = NULL;
     564          30 :     server->srv_data = NULL;
     565          30 :     server->last_status_change.tv_sec = 0;
     566          30 :     server->last_status_change.tv_usec = 0;
     567             : 
     568          30 :     server->port = port;
     569          30 :     server->user_data = user_data;
     570          30 :     server->service = service;
     571          30 :     server->port_status = DEFAULT_PORT_STATUS;
     572          30 :     server->primary = primary;
     573             : 
     574          30 :     return server;
     575             : }
     576             : 
     577             : int
     578           3 : fo_add_srv_server(struct fo_service *service, const char *srv,
     579             :                   const char *discovery_domain, const char *sssd_domain,
     580             :                   const char *proto, void *user_data)
     581             : {
     582             :     struct fo_server *server;
     583             : 
     584           3 :     DEBUG(SSSDBG_TRACE_FUNC,
     585             :           "Adding new SRV server to service '%s' using '%s'.\n",
     586             :            service->name, proto);
     587             : 
     588           3 :     DLIST_FOR_EACH(server, service->server_list) {
     589             :         /* Compare user data only if user_data_cmp and both arguments
     590             :          * are not NULL.
     591             :          */
     592           0 :         if (server->service->user_data_cmp && user_data && server->user_data) {
     593           0 :             if (server->service->user_data_cmp(server->user_data, user_data)) {
     594           0 :                 continue;
     595             :             }
     596             :         }
     597             : 
     598           0 :         if (fo_is_srv_lookup(server)) {
     599           0 :             if (((discovery_domain == NULL &&
     600           0 :                     server->srv_data->dns_domain == NULL) ||
     601           0 :                  (discovery_domain != NULL &&
     602           0 :                          server->srv_data->dns_domain != NULL &&
     603           0 :                   strcasecmp(server->srv_data->dns_domain, discovery_domain) == 0)) &&
     604           0 :                 strcasecmp(server->srv_data->proto, proto) == 0) {
     605           0 :                 return EEXIST;
     606             :             }
     607             :         }
     608             :     }
     609             : 
     610             :     /* SRV servers are always primary */
     611           3 :     server = fo_server_alloc(service, 0, user_data, true);
     612           3 :     if (server == NULL) {
     613           0 :         return ENOMEM;
     614             :     }
     615             : 
     616             :     /* add the SRV-specific data */
     617           3 :     server->srv_data = talloc_zero(service, struct srv_data);
     618           3 :     if (server->srv_data == NULL)
     619           0 :         return ENOMEM;
     620             : 
     621           3 :     server->srv_data->proto = talloc_strdup(server->srv_data, proto);
     622           3 :     server->srv_data->srv = talloc_strdup(server->srv_data, srv);
     623           6 :     if (server->srv_data->proto == NULL ||
     624           3 :         server->srv_data->srv == NULL)
     625           0 :         return ENOMEM;
     626             : 
     627           3 :     if (discovery_domain) {
     628           3 :         server->srv_data->discovery_domain = talloc_strdup(server->srv_data,
     629             :                                                            discovery_domain);
     630           3 :         if (server->srv_data->discovery_domain == NULL)
     631           0 :             return ENOMEM;
     632           3 :         server->srv_data->dns_domain = talloc_strdup(server->srv_data,
     633             :                                                      discovery_domain);
     634           3 :         if (server->srv_data->dns_domain == NULL)
     635           0 :             return ENOMEM;
     636             :     }
     637             : 
     638           6 :     server->srv_data->sssd_domain =
     639           3 :             talloc_strdup(server->srv_data, sssd_domain);
     640           3 :     if (server->srv_data->sssd_domain == NULL)
     641           0 :         return ENOMEM;
     642             : 
     643           3 :     server->srv_data->meta = server;
     644           3 :     server->srv_data->srv_lookup_status = DEFAULT_SRV_STATUS;
     645           3 :     server->srv_data->last_status_change.tv_sec = 0;
     646             : 
     647           3 :     DLIST_ADD_END(service->server_list, server, struct fo_server *);
     648           3 :     return EOK;
     649             : }
     650             : 
     651             : static struct fo_server *
     652          27 : create_fo_server(struct fo_service *service, const char *name,
     653             :                  int port, void *user_data, bool primary)
     654             : {
     655             :     struct fo_server *server;
     656             :     int ret;
     657             : 
     658          27 :     server = fo_server_alloc(service, port, user_data, primary);
     659          27 :     if (server == NULL)
     660           0 :         return NULL;
     661             : 
     662          27 :     server->port = port;
     663          27 :     server->user_data = user_data;
     664          27 :     server->service = service;
     665          27 :     server->port_status = DEFAULT_PORT_STATUS;
     666          27 :     server->primary = primary;
     667             : 
     668          27 :     if (name != NULL) {
     669          26 :         ret = get_server_common(server, service->ctx, name, &server->common);
     670          26 :         if (ret == ENOENT) {
     671          16 :             server->common = create_server_common(server, service->ctx, name);
     672          16 :             if (server->common == NULL) {
     673           0 :                 fo_server_free(server);
     674           0 :                 return NULL;
     675             :             }
     676          10 :         } else if (ret != EOK) {
     677           0 :             fo_server_free(server);
     678           0 :             return NULL;
     679             :         }
     680             :     }
     681             : 
     682          27 :     return server;
     683             : }
     684             : 
     685             : int
     686           0 : fo_get_server_count(struct fo_service *service)
     687             : {
     688             :     struct fo_server *server;
     689           0 :     int count = 0;
     690             : 
     691           0 :     DLIST_FOR_EACH(server, service->server_list) {
     692           0 :         count++;
     693             :     }
     694             : 
     695           0 :     return count;
     696             : }
     697             : 
     698          55 : static bool fo_server_match(struct fo_server *server,
     699             :                            const char *name,
     700             :                            int port,
     701             :                            void *user_data)
     702             : {
     703          55 :     if (server->port != port) {
     704          27 :         return false;
     705             :     }
     706             : 
     707             :     /* Compare user data only if user_data_cmp and both arguments
     708             :      * are not NULL.
     709             :      */
     710          28 :     if (server->service->user_data_cmp && server->user_data && user_data) {
     711          21 :         if (server->service->user_data_cmp(server->user_data, user_data)) {
     712          12 :             return false;
     713             :         }
     714             :     }
     715             : 
     716          16 :     if (name == NULL && server->common == NULL) {
     717           0 :         return true;
     718             :     }
     719             : 
     720          32 :     if (name != NULL &&
     721          32 :         server->common != NULL && server->common->name != NULL) {
     722          16 :         if (!strcasecmp(name, server->common->name))
     723          11 :             return true;
     724             :     }
     725             : 
     726           5 :     return false;
     727             : }
     728             : 
     729          18 : static bool fo_server_cmp(struct fo_server *s1, struct fo_server *s2)
     730             : {
     731          18 :     char *name = NULL;
     732             : 
     733          18 :     if (s2->common != NULL) {
     734          18 :         name = s2->common->name;
     735             :     }
     736             : 
     737          18 :     return fo_server_match(s1, name, s2->port, s2->user_data);
     738             : }
     739             : 
     740          27 : static bool fo_server_exists(struct fo_server *list,
     741             :                              const char *name,
     742             :                              int port,
     743             :                              void *user_data)
     744             : {
     745          27 :     struct fo_server *server = NULL;
     746             : 
     747          62 :     DLIST_FOR_EACH(server, list) {
     748          37 :         if (fo_server_match(server, name, port, user_data)) {
     749           2 :             return true;
     750             :         }
     751             :     }
     752             : 
     753          25 :     return false;
     754             : }
     755             : 
     756          27 : static errno_t fo_add_server_to_list(struct fo_server **to_list,
     757             :                                      struct fo_server *check_list,
     758             :                                      struct fo_server *server,
     759             :                                      const char *service_name)
     760             : {
     761          27 :     const char *debug_name = NULL;
     762          27 :     const char *name = NULL;
     763             :     bool exists;
     764             : 
     765          27 :     if (server->common == NULL || server->common->name == NULL) {
     766           1 :         debug_name = "(no name)";
     767           1 :         name = NULL;
     768             :     } else {
     769          26 :         debug_name = server->common->name;
     770          26 :         name = server->common->name;
     771             :     }
     772             : 
     773          27 :     exists = fo_server_exists(check_list, name, server->port,
     774             :                               server->user_data);
     775             : 
     776          27 :     if (exists) {
     777           2 :         DEBUG(SSSDBG_TRACE_FUNC, "Server '%s:%d' for service '%s' "
     778             :               "is already present\n", debug_name, server->port, service_name);
     779           2 :         return EEXIST;
     780             :     }
     781             : 
     782          25 :     DLIST_ADD_END(*to_list, server, struct fo_server *);
     783             : 
     784          25 :     DEBUG(SSSDBG_TRACE_FUNC, "Inserted %s server '%s:%d' to service "
     785             :           "'%s'\n", (server->primary ? "primary" : "backup"),
     786             :           debug_name, server->port, service_name);
     787             : 
     788          25 :     return EOK;
     789             : }
     790             : 
     791           8 : static errno_t fo_add_server_list(struct fo_service *service,
     792             :                                   struct fo_server *after_server,
     793             :                                   struct fo_server_info *servers,
     794             :                                   size_t num_servers,
     795             :                                   struct srv_data *srv_data,
     796             :                                   void *user_data,
     797             :                                   bool primary,
     798             :                                   struct fo_server **_last_server)
     799             : {
     800           8 :     struct fo_server *server = NULL;
     801           8 :     struct fo_server *last_server = NULL;
     802           8 :     struct fo_server *srv_list = NULL;
     803             :     size_t i;
     804             :     errno_t ret;
     805             : 
     806          24 :     for (i = 0; i < num_servers; i++) {
     807          16 :         server = create_fo_server(service, servers[i].host, servers[i].port,
     808             :                                   user_data, primary);
     809          16 :         if (server == NULL) {
     810           0 :             return ENOMEM;
     811             :         }
     812             : 
     813          16 :         server->srv_data = srv_data;
     814             : 
     815          16 :         ret = fo_add_server_to_list(&srv_list, service->server_list,
     816          16 :                                     server, service->name);
     817          16 :         if (ret != EOK) {
     818           0 :             fo_server_free(server);
     819           0 :             continue;
     820             :         }
     821             : 
     822          16 :         last_server = server;
     823             :     }
     824             : 
     825           8 :     if (srv_list != NULL) {
     826           8 :         DLIST_ADD_LIST_AFTER(service->server_list, after_server,
     827             :                              srv_list, struct fo_server *);
     828             :     }
     829             : 
     830           8 :     if (_last_server != NULL) {
     831           8 :         *_last_server = last_server == NULL ? after_server : last_server;
     832             :     }
     833             : 
     834           8 :     return EOK;
     835             : }
     836             : 
     837             : int
     838          11 : fo_add_server(struct fo_service *service, const char *name, int port,
     839             :               void *user_data, bool primary)
     840             : {
     841             :     struct fo_server *server;
     842             :     errno_t ret;
     843             : 
     844          11 :     server = create_fo_server(service, name, port, user_data, primary);
     845          11 :     if (!server) {
     846           0 :         return ENOMEM;
     847             :     }
     848             : 
     849          11 :     ret = fo_add_server_to_list(&service->server_list, service->server_list,
     850          11 :                                 server, service->name);
     851          11 :     if (ret != EOK) {
     852           2 :         fo_server_free(server);
     853             :     }
     854             : 
     855          11 :     return ret;
     856             : }
     857             : 
     858          26 : void fo_ref_server(TALLOC_CTX *ref_ctx,
     859             :                    struct fo_server *server)
     860             : {
     861          26 :     if (server) {
     862          22 :         server = rc_reference(ref_ctx, struct fo_server, server);
     863             :     }
     864          26 : }
     865             : 
     866             : static int
     867          26 : get_first_server_entity(struct fo_service *service, struct fo_server **_server)
     868             : {
     869             :     struct fo_server *server;
     870             : 
     871             :     /* If we already have a working server, use that one. */
     872          26 :     server = service->active_server;
     873          26 :     if (server != NULL) {
     874           6 :         if (service_works(server) && fo_is_server_primary(server)) {
     875           3 :             goto done;
     876             :         }
     877           3 :         service->active_server = NULL;
     878             :     }
     879             : 
     880             :     /*
     881             :      * Otherwise iterate through the server list.
     882             :      */
     883             : 
     884             :     /* First, try primary servers after the last one we tried.
     885             :      * (only if the last one was primary as well)
     886             :      */
     887          37 :     if (service->last_tried_server != NULL &&
     888          14 :         service->last_tried_server->primary) {
     889          22 :         if (service->last_tried_server->port_status == PORT_NEUTRAL &&
     890          10 :             server_works(service->last_tried_server)) {
     891           6 :             server = service->last_tried_server;
     892           6 :             goto done;
     893             :         }
     894             : 
     895           7 :         DLIST_FOR_EACH(server, service->last_tried_server->next) {
     896             :             /* Go only through primary servers */
     897           3 :             if (!server->primary) continue;
     898             : 
     899           2 :             if (service_works(server)) {
     900           2 :                 goto done;
     901             :             }
     902             :         }
     903             :     }
     904             : 
     905             :     /* If none were found, try at the start, primary first */
     906          23 :     DLIST_FOR_EACH(server, service->server_list) {
     907             :         /* First iterate only over primary servers */
     908          21 :         if (!server->primary) continue;
     909             : 
     910          17 :         if (service_works(server)) {
     911           9 :             goto done;
     912             :         }
     913           8 :         if (server == service->last_tried_server) {
     914           4 :             break;
     915             :         }
     916             :     }
     917             : 
     918          16 :     DLIST_FOR_EACH(server, service->server_list) {
     919             :         /* Now iterate only over backup servers */
     920          12 :         if (server->primary) continue;
     921             : 
     922           4 :         if (service_works(server)) {
     923           2 :             goto done;
     924             :         }
     925             :     }
     926             : 
     927           4 :     service->last_tried_server = NULL;
     928           4 :     return ENOENT;
     929             : 
     930             : done:
     931          22 :     service->last_tried_server = server;
     932          22 :     *_server = server;
     933          22 :     return EOK;
     934             : }
     935             : 
     936             : static int
     937          12 : resolve_service_request_destructor(struct resolve_service_request *request)
     938             : {
     939          12 :     DLIST_REMOVE(request->server_common->request_list, request);
     940          12 :     return 0;
     941             : }
     942             : 
     943             : static int
     944          12 : set_lookup_hook(struct tevent_context *ev,
     945             :                 struct fo_server *server,
     946             :                 struct tevent_req *req)
     947             : {
     948             :     struct resolve_service_request *request;
     949             : 
     950          12 :     request = talloc(req, struct resolve_service_request);
     951          12 :     if (request == NULL) {
     952           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "No memory\n");
     953           0 :         talloc_free(request);
     954           0 :         return ENOMEM;
     955             :     }
     956          12 :     request->server_common = rc_reference(request, struct server_common,
     957             :                                           server->common);
     958          12 :     if (request->server_common == NULL) {
     959           0 :         talloc_free(request);
     960           0 :         return ENOMEM;
     961             :     }
     962          12 :     request->ev = ev;
     963          12 :     request->req = req;
     964          12 :     DLIST_ADD(server->common->request_list, request);
     965          12 :     talloc_set_destructor(request, resolve_service_request_destructor);
     966             : 
     967          12 :     return EOK;
     968             : }
     969             : 
     970             : 
     971             : 
     972             : /*******************************************************************
     973             :  * Get server to connect to.                                       *
     974             :  *******************************************************************/
     975             : 
     976             : struct resolve_service_state {
     977             :     struct fo_server *server;
     978             : 
     979             :     struct resolv_ctx *resolv;
     980             :     struct tevent_context *ev;
     981             :     struct tevent_timer *timeout_handler;
     982             :     struct fo_ctx *fo_ctx;
     983             : };
     984             : 
     985             : static errno_t fo_resolve_service_activate_timeout(struct tevent_req *req,
     986             :             struct tevent_context *ev, const unsigned long timeout_seconds);
     987             : static void fo_resolve_service_cont(struct tevent_req *subreq);
     988             : static void fo_resolve_service_done(struct tevent_req *subreq);
     989             : static bool fo_resolve_service_server(struct tevent_req *req);
     990             : 
     991             : /* Forward declarations for SRV resolving */
     992             : static struct tevent_req *
     993             : resolve_srv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
     994             :                     struct resolv_ctx *resolv, struct fo_ctx *ctx,
     995             :                     struct fo_server *server);
     996             : static int
     997             : resolve_srv_recv(struct tevent_req *req, struct fo_server **server);
     998             : 
     999             : struct tevent_req *
    1000          26 : fo_resolve_service_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
    1001             :                         struct resolv_ctx *resolv, struct fo_ctx *ctx,
    1002             :                         struct fo_service *service)
    1003             : {
    1004             :     int ret;
    1005             :     struct fo_server *server;
    1006             :     struct tevent_req *req;
    1007             :     struct tevent_req *subreq;
    1008             :     struct resolve_service_state *state;
    1009             : 
    1010          26 :     DEBUG(SSSDBG_CONF_SETTINGS,
    1011             :           "Trying to resolve service '%s'\n", service->name);
    1012          26 :     req = tevent_req_create(mem_ctx, &state, struct resolve_service_state);
    1013          26 :     if (req == NULL)
    1014           0 :         return NULL;
    1015             : 
    1016          26 :     state->resolv = resolv;
    1017          26 :     state->ev = ev;
    1018          26 :     state->fo_ctx = ctx;
    1019             : 
    1020          26 :     ret = get_first_server_entity(service, &server);
    1021          26 :     if (ret != EOK) {
    1022           4 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1023             :               "No available servers for service '%s'\n", service->name);
    1024           4 :         goto done;
    1025             :     }
    1026             : 
    1027             :     /* Activate per-service timeout handler */
    1028          22 :     ret = fo_resolve_service_activate_timeout(req, ev,
    1029          22 :                                         ctx->opts->service_resolv_timeout);
    1030          22 :     if (ret != EOK) {
    1031           0 :         DEBUG(SSSDBG_OP_FAILURE, "Could not set service timeout\n");
    1032           0 :         goto done;
    1033             :     }
    1034             : 
    1035          22 :     if (fo_is_srv_lookup(server)) {
    1036             :         /* Don't know the server yet, must do a SRV lookup */
    1037          10 :         subreq = resolve_srv_send(state, ev, resolv,
    1038             :                                   ctx, server);
    1039          10 :         if (subreq == NULL) {
    1040           0 :             ret = ENOMEM;
    1041           0 :             goto done;
    1042             :         }
    1043             : 
    1044          10 :         tevent_req_set_callback(subreq,
    1045             :                                 fo_resolve_service_cont,
    1046             :                                 req);
    1047          10 :         return req;
    1048             :     }
    1049             : 
    1050             :     /* This is a regular server, just do hostname lookup */
    1051          12 :     state->server = server;
    1052          12 :     if (fo_resolve_service_server(req)) {
    1053           7 :         tevent_req_post(req, ev);
    1054             :     }
    1055             : 
    1056          12 :     ret = EOK;
    1057             : done:
    1058          16 :     if (ret != EOK) {
    1059           4 :         tevent_req_error(req, ret);
    1060           4 :         tevent_req_post(req, ev);
    1061             :     }
    1062          16 :     return req;
    1063             : }
    1064             : 
    1065             : static void set_server_common_status(struct server_common *common,
    1066             :                                      enum server_status status);
    1067             : 
    1068             : static void
    1069           0 : fo_resolve_service_timeout(struct tevent_context *ev,
    1070             :                            struct tevent_timer *te,
    1071             :                            struct timeval tv, void *pvt)
    1072             : {
    1073           0 :     struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
    1074             : 
    1075           0 :     DEBUG(SSSDBG_MINOR_FAILURE, "Service resolving timeout reached\n");
    1076           0 :     tevent_req_error(req, ETIMEDOUT);
    1077           0 : }
    1078             : 
    1079             : static errno_t
    1080          22 : fo_resolve_service_activate_timeout(struct tevent_req *req,
    1081             :                                     struct tevent_context *ev,
    1082             :                                     const unsigned long timeout_seconds)
    1083             : {
    1084             :     struct timeval tv;
    1085          22 :     struct resolve_service_state *state = tevent_req_data(req,
    1086             :                                         struct resolve_service_state);
    1087             : 
    1088          22 :     tv = tevent_timeval_current();
    1089          22 :     tv = tevent_timeval_add(&tv, timeout_seconds, 0);
    1090          22 :     state->timeout_handler = tevent_add_timer(ev, state, tv,
    1091             :                                               fo_resolve_service_timeout, req);
    1092          22 :     if (state->timeout_handler == NULL) {
    1093           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_timer failed.\n");
    1094           0 :         return ENOMEM;
    1095             :     }
    1096             : 
    1097          22 :     DEBUG(SSSDBG_TRACE_INTERNAL, "Resolve timeout set to %lu seconds\n",
    1098             :           timeout_seconds);
    1099          22 :     return EOK;
    1100             : }
    1101             : 
    1102             : /* SRV resolving finished, see if we got server to work with */
    1103             : static void
    1104          10 : fo_resolve_service_cont(struct tevent_req *subreq)
    1105             : {
    1106          10 :     struct tevent_req *req = tevent_req_callback_data(subreq,
    1107             :                                                       struct tevent_req);
    1108          10 :     struct resolve_service_state *state = tevent_req_data(req,
    1109             :                                         struct resolve_service_state);
    1110             :     int ret;
    1111             : 
    1112          10 :     ret = resolve_srv_recv(subreq, &state->server);
    1113          10 :     talloc_zfree(subreq);
    1114             : 
    1115          10 :     if (ret) {
    1116           0 :         tevent_req_error(req, ret);
    1117          10 :         return;
    1118             :     }
    1119             : 
    1120          10 :     fo_resolve_service_server(req);
    1121             : }
    1122             : 
    1123             : static bool
    1124          22 : fo_resolve_service_server(struct tevent_req *req)
    1125             : {
    1126          22 :     struct resolve_service_state *state = tevent_req_data(req,
    1127             :                                         struct resolve_service_state);
    1128             :     struct tevent_req *subreq;
    1129             :     int ret;
    1130             : 
    1131          22 :     switch (get_server_status(state->server)) {
    1132             :     case SERVER_NAME_NOT_RESOLVED: /* Request name resolution. */
    1133          24 :         subreq = resolv_gethostbyname_send(state->server->common,
    1134             :                                            state->ev, state->resolv,
    1135          12 :                                            state->server->common->name,
    1136          12 :                                            state->fo_ctx->opts->family_order,
    1137             :                                            default_host_dbs);
    1138          12 :         if (subreq == NULL) {
    1139           0 :             tevent_req_error(req, ENOMEM);
    1140           0 :             return true;
    1141             :         }
    1142          12 :         tevent_req_set_callback(subreq, fo_resolve_service_done,
    1143          12 :                                 state->server->common);
    1144          12 :         fo_set_server_status(state->server, SERVER_RESOLVING_NAME);
    1145             :         /* FALLTHROUGH */
    1146             :     case SERVER_RESOLVING_NAME:
    1147             :         /* Name resolution is already under way. Just add ourselves into the
    1148             :          * waiting queue so we get notified after the operation is finished. */
    1149          12 :         ret = set_lookup_hook(state->ev, state->server, req);
    1150          12 :         if (ret != EOK) {
    1151           0 :             tevent_req_error(req, ret);
    1152           0 :             return true;
    1153             :         }
    1154          12 :         break;
    1155             :     default: /* The name is already resolved. Return immediately. */
    1156          10 :         tevent_req_done(req);
    1157          10 :         return true;
    1158             :     }
    1159             : 
    1160          12 :     return false;
    1161             : }
    1162             : 
    1163             : static void
    1164          12 : fo_resolve_service_done(struct tevent_req *subreq)
    1165             : {
    1166          12 :     struct server_common *common = tevent_req_callback_data(subreq,
    1167             :                                                         struct server_common);
    1168             :     int resolv_status;
    1169             :     struct resolve_service_request *request;
    1170             :     int ret;
    1171             : 
    1172          12 :     if (common->rhostent != NULL) {
    1173           0 :         talloc_zfree(common->rhostent);
    1174             :     }
    1175             : 
    1176          12 :     ret = resolv_gethostbyname_recv(subreq, common,
    1177             :                                     &resolv_status, NULL,
    1178             :                                     &common->rhostent);
    1179          12 :     talloc_zfree(subreq);
    1180          12 :     if (ret != EOK) {
    1181           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to resolve server '%s': %s\n",
    1182             :                   common->name,
    1183             :                   resolv_strerror(resolv_status));
    1184             :         /* If the resolver failed to resolve a hostname but did not
    1185             :          * encounter an error, tell the caller to retry another server.
    1186             :          *
    1187             :          * If there are no more servers to try, the next request would
    1188             :          * just shortcut with ENOENT.
    1189             :          */
    1190           0 :         if (ret == ENOENT) {
    1191           0 :             ret = EAGAIN;
    1192             :         }
    1193           0 :         set_server_common_status(common, SERVER_NOT_WORKING);
    1194             :     } else {
    1195          12 :         set_server_common_status(common, SERVER_NAME_RESOLVED);
    1196             :     }
    1197             : 
    1198             :     /* Take care of all requests for this server. */
    1199          36 :     while ((request = common->request_list) != NULL) {
    1200          12 :         DLIST_REMOVE(common->request_list, request);
    1201             : 
    1202             :         /* If the request callback decresed refcount on the returned
    1203             :          * server, we would have crashed as common would not be valid
    1204             :          * anymore. Rather schedule the notify for next tev iteration
    1205             :          */
    1206          12 :         tevent_req_defer_callback(request->req, request->ev);
    1207             : 
    1208          12 :         if (ret) {
    1209           0 :             tevent_req_error(request->req, ret);
    1210             :         } else {
    1211          12 :             tevent_req_done(request->req);
    1212             :         }
    1213             :     }
    1214          12 : }
    1215             : 
    1216             : int
    1217          26 : fo_resolve_service_recv(struct tevent_req *req,
    1218             :                         TALLOC_CTX *ref_ctx,
    1219             :                         struct fo_server **server)
    1220             : {
    1221             :     struct resolve_service_state *state;
    1222             : 
    1223          26 :     state = tevent_req_data(req, struct resolve_service_state);
    1224             : 
    1225             :     /* always return the server if asked for, otherwise the caller
    1226             :      * cannot mark it as faulty in case we return an error */
    1227          26 :     if (server != NULL) {
    1228          26 :         fo_ref_server(ref_ctx, state->server);
    1229          26 :         *server = state->server;
    1230             :     }
    1231             : 
    1232          30 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    1233             : 
    1234          22 :     return EOK;
    1235             : }
    1236             : 
    1237             : /*******************************************************************
    1238             :  * Resolve the server to connect to using a SRV query.             *
    1239             :  *******************************************************************/
    1240             : 
    1241             : static void resolve_srv_done(struct tevent_req *subreq);
    1242             : 
    1243             : struct resolve_srv_state {
    1244             :     struct fo_server *meta;
    1245             :     struct fo_service *service;
    1246             : 
    1247             :     struct fo_server *out;
    1248             : 
    1249             :     struct resolv_ctx *resolv;
    1250             :     struct tevent_context *ev;
    1251             :     struct fo_ctx *fo_ctx;
    1252             : };
    1253             : 
    1254             : static struct tevent_req *
    1255          10 : resolve_srv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
    1256             :                  struct resolv_ctx *resolv, struct fo_ctx *ctx,
    1257             :                  struct fo_server *server)
    1258             : {
    1259             :     int ret;
    1260             :     struct tevent_req *req;
    1261             :     struct tevent_req *subreq;
    1262             :     struct resolve_srv_state *state;
    1263             :     int status;
    1264             : 
    1265          10 :     req = tevent_req_create(mem_ctx, &state, struct resolve_srv_state);
    1266          10 :     if (req == NULL)
    1267           0 :         return NULL;
    1268             : 
    1269          10 :     state->service = server->service;
    1270          10 :     state->ev = ev;
    1271          10 :     state->resolv = resolv;
    1272          10 :     state->fo_ctx = ctx;
    1273          10 :     state->meta = server->srv_data->meta;
    1274             : 
    1275          10 :     status = get_srv_data_status(server->srv_data);
    1276          10 :     DEBUG(SSSDBG_FUNC_DATA, "The status of SRV lookup is %s\n",
    1277             :           str_srv_data_status(status));
    1278          10 :     switch(status) {
    1279             :     case SRV_EXPIRED: /* Need a refresh */
    1280           4 :         state->meta = collapse_srv_lookup(&server);
    1281             :         /* FALLTHROUGH.
    1282             :          * "server" might be invalid now if the SRV
    1283             :          * query collapsed
    1284             :          * */
    1285             :     case SRV_NEUTRAL: /* Request SRV lookup */
    1286           8 :         if (server != NULL && server != state->meta) {
    1287             :             /* A server created by expansion of meta server was marked as
    1288             :              * neutral. We have to collapse the servers and issue new
    1289             :              * SRV resolution. */
    1290           1 :             state->meta = collapse_srv_lookup(&server);
    1291             :         }
    1292             : 
    1293           8 :         if (ctx->srv_send_fn == NULL || ctx->srv_recv_fn == NULL) {
    1294           0 :             DEBUG(SSSDBG_OP_FAILURE, "No SRV lookup plugin is set\n");
    1295           0 :             ret = ENOTSUP;
    1296           0 :             goto done;
    1297             :         }
    1298             : 
    1299          16 :         subreq = ctx->srv_send_fn(state, ev,
    1300           8 :                                   state->meta->srv_data->srv,
    1301           8 :                                   state->meta->srv_data->proto,
    1302           8 :                                   state->meta->srv_data->discovery_domain,
    1303             :                                   ctx->srv_pvt);
    1304           8 :         if (subreq == NULL) {
    1305           0 :             ret = ENOMEM;
    1306           0 :             goto done;
    1307             :         }
    1308             : 
    1309           8 :         tevent_req_set_callback(subreq, resolve_srv_done, req);
    1310           8 :         break;
    1311             :     case SRV_RESOLVE_ERROR: /* query could not be resolved but don't retry yet */
    1312           0 :         ret = EIO;
    1313           0 :         state->out = server;
    1314             : 
    1315             :         /* The port status was reseted to neutral but we still haven't reached
    1316             :          * timeout to try to resolve SRV record again. We will set the port
    1317             :          * status back to not working. */
    1318           0 :         fo_set_port_status(state->meta, PORT_NOT_WORKING);
    1319           0 :         goto done;
    1320             :     case SRV_RESOLVED:  /* The query is resolved and valid. Return. */
    1321           2 :         state->out = server;
    1322           2 :         tevent_req_done(req);
    1323           2 :         tevent_req_post(req, state->ev);
    1324           2 :         return req;
    1325             :     default:
    1326           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1327             :               "Unexpected status %d for a SRV server\n", status);
    1328           0 :         ret = EIO;
    1329           0 :         goto done;
    1330             :     }
    1331             : 
    1332           8 :     ret = EOK;
    1333             : done:
    1334           8 :     if (ret != EOK) {
    1335           0 :         tevent_req_error(req, ret);
    1336           0 :         tevent_req_post(req, ev);
    1337             :     }
    1338           8 :     return req;
    1339             : }
    1340             : 
    1341             : static void
    1342           8 : resolve_srv_done(struct tevent_req *subreq)
    1343             : {
    1344           8 :     struct tevent_req *req = tevent_req_callback_data(subreq,
    1345             :                                                       struct tevent_req);
    1346           8 :     struct resolve_srv_state *state = tevent_req_data(req,
    1347             :                                                 struct resolve_srv_state);
    1348           8 :     struct fo_server *last_server = NULL;
    1349           8 :     struct fo_server_info *primary_servers = NULL;
    1350           8 :     struct fo_server_info *backup_servers = NULL;
    1351           8 :     size_t num_primary_servers = 0;
    1352           8 :     size_t num_backup_servers = 0;
    1353           8 :     char *dns_domain = NULL;
    1354             :     int ret;
    1355             :     uint32_t ttl;
    1356             : 
    1357           8 :     ret = state->fo_ctx->srv_recv_fn(state, subreq, &dns_domain, &ttl,
    1358             :                                      &primary_servers, &num_primary_servers,
    1359             :                                      &backup_servers, &num_backup_servers);
    1360           8 :     talloc_free(subreq);
    1361           8 :     switch (ret) {
    1362             :     case EOK:
    1363           8 :         if ((num_primary_servers == 0 || primary_servers == NULL)
    1364           0 :                 && (num_backup_servers == 0 || backup_servers == NULL)) {
    1365           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "SRV lookup plugin returned EOK but "
    1366             :                                         "no servers\n");
    1367           0 :             ret = EFAULT;
    1368           0 :             goto done;
    1369             :         }
    1370             : 
    1371           8 :         state->meta->srv_data->ttl = ttl;
    1372           8 :         talloc_zfree(state->meta->srv_data->dns_domain);
    1373           8 :         state->meta->srv_data->dns_domain = talloc_steal(state->meta->srv_data,
    1374             :                                                          dns_domain);
    1375             : 
    1376           8 :         last_server = state->meta;
    1377             : 
    1378           8 :         if (primary_servers != NULL) {
    1379          16 :             ret = fo_add_server_list(state->service, last_server,
    1380             :                                      primary_servers, num_primary_servers,
    1381           8 :                                      state->meta->srv_data,
    1382           8 :                                      state->meta->user_data,
    1383             :                                      true, &last_server);
    1384           8 :             if (ret != EOK) {
    1385           0 :                 goto done;
    1386             :             }
    1387             :         }
    1388             : 
    1389           8 :         if (backup_servers != NULL) {
    1390           0 :             ret = fo_add_server_list(state->service, last_server,
    1391             :                                      backup_servers, num_backup_servers,
    1392           0 :                                      state->meta->srv_data,
    1393           0 :                                      state->meta->user_data,
    1394             :                                      false, &last_server);
    1395           0 :             if (ret != EOK) {
    1396           0 :                 goto done;
    1397             :             }
    1398             :         }
    1399             : 
    1400           8 :         if (last_server == state->meta) {
    1401             :             /* SRV lookup returned only those servers
    1402             :              * that are already present. */
    1403           0 :             DEBUG(SSSDBG_TRACE_FUNC, "SRV lookup did not return "
    1404             :                                       "any new server.\n");
    1405           0 :             ret = ERR_SRV_DUPLICATES;
    1406           0 :             goto done;
    1407             :         }
    1408             : 
    1409             :         /* At least one new server was inserted.
    1410             :          * We will return the first new server. */
    1411           8 :         if (state->meta->next == NULL) {
    1412           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
    1413             :                  "BUG: state->meta->next is NULL\n");
    1414           0 :             ret = ERR_INTERNAL;
    1415           0 :             goto done;
    1416             :         }
    1417             : 
    1418           8 :         state->out = state->meta->next;
    1419             : 
    1420             :         /* And remove meta server from the server list. It will be
    1421             :          * inserted again during srv collapse. */
    1422           8 :         DLIST_REMOVE(state->service->server_list, state->meta);
    1423           8 :         if (state->service->last_tried_server == state->meta) {
    1424           8 :             state->service->last_tried_server = state->out;
    1425             :         }
    1426             : 
    1427           8 :         set_srv_data_status(state->meta->srv_data, SRV_RESOLVED);
    1428           8 :         ret = EOK;
    1429           8 :         break;
    1430             :     case ERR_SRV_NOT_FOUND:
    1431             :         /* fall through */
    1432             :     case ERR_SRV_LOOKUP_ERROR:
    1433           0 :         fo_set_port_status(state->meta, PORT_NOT_WORKING);
    1434             :         /* fall through */
    1435             :     default:
    1436           0 :         DEBUG(SSSDBG_OP_FAILURE, "Unable to resolve SRV [%d]: %s\n",
    1437             :                                   ret, sss_strerror(ret));
    1438             :     }
    1439             : 
    1440             : done:
    1441           8 :     if (ret != EOK) {
    1442           0 :         state->out = state->meta;
    1443           0 :         set_srv_data_status(state->meta->srv_data, SRV_RESOLVE_ERROR);
    1444           0 :         tevent_req_error(req, ret);
    1445           8 :         return;
    1446             :     }
    1447             : 
    1448           8 :     tevent_req_done(req);
    1449             : }
    1450             : 
    1451             : static int
    1452          10 : resolve_srv_recv(struct tevent_req *req, struct fo_server **server)
    1453             : {
    1454          10 :     struct resolve_srv_state *state = tevent_req_data(req,
    1455             :                                                 struct resolve_srv_state);
    1456             : 
    1457             :     /* always return the server if asked for, otherwise the caller
    1458             :      * cannot mark it as faulty in case we return an error */
    1459          10 :     if (server) {
    1460          10 :         *server = state->out;
    1461             :     }
    1462             : 
    1463          10 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    1464             : 
    1465          10 :     return EOK;
    1466             : }
    1467             : 
    1468             : /*******************************************************************
    1469             :  *     Get Fully Qualified Domain Name of the host machine         *
    1470             :  *******************************************************************/
    1471             : static void
    1472          45 : set_server_common_status(struct server_common *common,
    1473             :                          enum server_status status)
    1474             : {
    1475          45 :     DEBUG(SSSDBG_CONF_SETTINGS, "Marking server '%s' as '%s'\n", common->name,
    1476             :               str_server_status(status));
    1477             : 
    1478          45 :     common->server_status = status;
    1479          45 :     gettimeofday(&common->last_status_change, NULL);
    1480          45 : }
    1481             : 
    1482             : void
    1483          33 : fo_set_server_status(struct fo_server *server, enum server_status status)
    1484             : {
    1485          33 :     if (server->common == NULL) {
    1486           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1487             :               "Bug: Trying to set server status of a name-less server\n");
    1488          33 :         return;
    1489             :     }
    1490             : 
    1491          33 :     set_server_common_status(server->common, status);
    1492             : }
    1493             : 
    1494             : void
    1495           9 : fo_set_port_status(struct fo_server *server, enum port_status status)
    1496             : {
    1497             :     struct fo_server *siter;
    1498             : 
    1499           9 :     DEBUG(SSSDBG_CONF_SETTINGS,
    1500             :           "Marking port %d of server '%s' as '%s'\n", server->port,
    1501             :               SERVER_NAME(server), str_port_status(status));
    1502             : 
    1503           9 :     server->port_status = status;
    1504           9 :     gettimeofday(&server->last_status_change, NULL);
    1505           9 :     if (status == PORT_WORKING) {
    1506           4 :         fo_set_server_status(server, SERVER_WORKING);
    1507           4 :         server->service->active_server = server;
    1508             :     }
    1509             : 
    1510          18 :     if (!server->common || !server->common->name) return;
    1511             : 
    1512             :     /* It is possible to introduce duplicates when expanding SRV results
    1513             :      * into fo_server structures. Find the duplicates and set the same
    1514             :      * status */
    1515          27 :     DLIST_FOR_EACH(siter, server->service->server_list) {
    1516          18 :         if (fo_server_cmp(siter, server)) {
    1517           9 :             DEBUG(SSSDBG_TRACE_FUNC,
    1518             :                   "Marking port %d of duplicate server '%s' as '%s'\n",
    1519             :                    siter->port, SERVER_NAME(siter),
    1520             :                    str_port_status(status));
    1521           9 :             siter->port_status = status;
    1522           9 :             gettimeofday(&siter->last_status_change, NULL);
    1523             :         }
    1524             :     }
    1525             : }
    1526             : 
    1527           1 : struct fo_server *fo_get_active_server(struct fo_service *service)
    1528             : {
    1529           1 :     return service->active_server;
    1530             : }
    1531             : 
    1532           0 : void fo_try_next_server(struct fo_service *service)
    1533             : {
    1534             :     struct fo_server *server;
    1535             : 
    1536           0 :     if (!service) {
    1537           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Bug: No service supplied\n");
    1538           0 :         return;
    1539             :     }
    1540             : 
    1541           0 :     server = service->active_server;
    1542           0 :     if (!server) {
    1543           0 :         return;
    1544             :     }
    1545             : 
    1546           0 :     service->active_server = 0;
    1547             : 
    1548           0 :     if (server->port_status == PORT_WORKING) {
    1549           0 :         server->port_status = PORT_NEUTRAL;
    1550             :     }
    1551             : }
    1552             : 
    1553             : void *
    1554           0 : fo_get_server_user_data(struct fo_server *server)
    1555             : {
    1556           0 :     return server->user_data;
    1557             : }
    1558             : 
    1559             : int
    1560          22 : fo_get_server_port(struct fo_server *server)
    1561             : {
    1562          22 :     return server->port;
    1563             : }
    1564             : 
    1565             : const char *
    1566          22 : fo_get_server_name(struct fo_server *server)
    1567             : {
    1568          22 :     if (!server->common) {
    1569           1 :         return NULL;
    1570             :     }
    1571          21 :     return server->common->name;
    1572             : }
    1573             : 
    1574           0 : const char *fo_get_server_str_name(struct fo_server *server)
    1575             : {
    1576           0 :     if (!server->common) {
    1577           0 :         if (fo_is_srv_lookup(server)) {
    1578           0 :             return "SRV lookup meta-server";
    1579             :         }
    1580           0 :         return "unknown name";
    1581             :     }
    1582             : 
    1583           0 :     return server->common->name;
    1584             : }
    1585             : 
    1586             : struct resolv_hostent *
    1587           7 : fo_get_server_hostent(struct fo_server *server)
    1588             : {
    1589           7 :     if (server->common == NULL) {
    1590           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1591             :               "Bug: Trying to get hostent from a name-less server\n");
    1592           0 :         return NULL;
    1593             :     }
    1594             : 
    1595           7 :     return server->common->rhostent;
    1596             : }
    1597             : 
    1598             : bool
    1599           4 : fo_is_server_primary(struct fo_server *server)
    1600             : {
    1601           4 :     return server->primary;
    1602             : }
    1603             : 
    1604             : time_t
    1605           0 : fo_get_server_hostname_last_change(struct fo_server *server)
    1606             : {
    1607           0 :     if (server->common == NULL) {
    1608           0 :         return 0;
    1609             :     }
    1610           0 :     return server->common->last_status_change.tv_sec;
    1611             : }
    1612             : 
    1613           0 : time_t fo_get_service_retry_timeout(struct fo_service *svc)
    1614             : {
    1615           0 :     if (svc == NULL || svc->ctx == NULL || svc->ctx->opts == NULL) {
    1616           0 :         return 0;
    1617             :     }
    1618             : 
    1619           0 :     return svc->ctx->opts->retry_timeout;
    1620             : }
    1621             : 
    1622           2 : void fo_reset_servers(struct fo_service *service)
    1623             : {
    1624             :     struct fo_server *server;
    1625             : 
    1626           6 :     DLIST_FOR_EACH(server, service->server_list) {
    1627           4 :         if (server->srv_data != NULL) {
    1628           2 :             set_srv_data_status(server->srv_data, SRV_NEUTRAL);
    1629             :         }
    1630             : 
    1631           4 :         if (server->common) {
    1632           4 :             fo_set_server_status(server, SERVER_NAME_NOT_RESOLVED);
    1633             :         }
    1634             : 
    1635           4 :         fo_set_port_status(server, PORT_NEUTRAL);
    1636             :     }
    1637           2 : }
    1638             : 
    1639             : 
    1640           0 : void fo_reset_services(struct fo_ctx *fo_ctx)
    1641             : {
    1642             :     struct fo_service *service;
    1643             : 
    1644           0 :     DEBUG(SSSDBG_TRACE_LIBS,
    1645             :           "Resetting all servers in all services\n");
    1646             : 
    1647           0 :     DLIST_FOR_EACH(service, fo_ctx->service_list) {
    1648           0 :         fo_reset_servers(service);
    1649             :     }
    1650           0 : }
    1651             : 
    1652           0 : bool fo_svc_has_server(struct fo_service *service, struct fo_server *server)
    1653             : {
    1654             :     struct fo_server *srv;
    1655             : 
    1656           0 :     DLIST_FOR_EACH(srv, service->server_list) {
    1657           0 :         if (srv == server) return true;
    1658             :     }
    1659             : 
    1660           0 :     return false;
    1661             : }
    1662             : 
    1663           3 : bool fo_set_srv_lookup_plugin(struct fo_ctx *ctx,
    1664             :                               fo_srv_lookup_plugin_send_t send_fn,
    1665             :                               fo_srv_lookup_plugin_recv_t recv_fn,
    1666             :                               void *pvt)
    1667             : {
    1668           3 :     if (ctx == NULL || send_fn == NULL || recv_fn == NULL) {
    1669           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Invalid parameters\n");
    1670           0 :         return false;
    1671             :     }
    1672             : 
    1673           3 :     if (ctx->srv_send_fn != NULL || ctx->srv_recv_fn != NULL) {
    1674           0 :         DEBUG(SSSDBG_MINOR_FAILURE, "SRV lookup plugin is already set\n");
    1675           0 :         return false;
    1676             :     }
    1677             : 
    1678           3 :     ctx->srv_send_fn = send_fn;
    1679           3 :     ctx->srv_recv_fn = recv_fn;
    1680           3 :     ctx->srv_pvt = talloc_steal(ctx, pvt);
    1681             : 
    1682           3 :     return true;
    1683             : }

Generated by: LCOV version 1.10