LCOV - code coverage report
Current view: top level - providers - fail_over.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 477 670 71.2 %
Date: 2015-10-19 Functions: 44 56 78.6 %

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

Generated by: LCOV version 1.10