LCOV - code coverage report
Current view: top level - responder/nss - nsssrv_services.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 722 0.0 %
Date: 2016-06-29 Functions: 0 26 0.0 %

          Line data    Source code
       1             : /*
       2             :     SSSD
       3             : 
       4             :     Authors:
       5             :         Stephen Gallagher <sgallagh@redhat.com>
       6             : 
       7             :     Copyright (C) 2012 Red Hat
       8             : 
       9             :     This program is free software; you can redistribute it and/or modify
      10             :     it under the terms of the GNU General Public License as published by
      11             :     the Free Software Foundation; either version 3 of the License, or
      12             :     (at your option) any later version.
      13             : 
      14             :     This program is distributed in the hope that it will be useful,
      15             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :     GNU General Public License for more details.
      18             : 
      19             :     You should have received a copy of the GNU General Public License
      20             :     along with this program.  If not, see <http://www.gnu.org/licenses/>.
      21             : */
      22             : 
      23             : 
      24             : #include <arpa/inet.h>
      25             : #include "util/util.h"
      26             : #include "responder/nss/nsssrv.h"
      27             : #include "responder/nss/nsssrv_private.h"
      28             : #include "responder/nss/nsssrv_services.h"
      29             : #include "responder/common/negcache.h"
      30             : #include "confdb/confdb.h"
      31             : #include "db/sysdb.h"
      32             : #include "db/sysdb_services.h"
      33             : 
      34             : struct getserv_ctx {
      35             :     uint16_t port;
      36             :     struct tevent_context *ev;
      37             :     struct nss_dom_ctx *dctx;
      38             : 
      39             :     struct sss_domain_info **domains;
      40             :     size_t dom_idx;
      41             : 
      42             :     char *name;
      43             :     char *cased_name;
      44             : 
      45             :     char *proto;
      46             :     char *cased_proto;
      47             :     struct ldb_result *res;
      48             : };
      49             : 
      50             : static errno_t lookup_service_step(struct tevent_req *req);
      51             : static void lookup_service_done(struct tevent_req *req);
      52             : 
      53             : #define SVC_NAME_CASED (dom->case_sensitive ? state->name \
      54             :                                            : state->cased_name)
      55             : #define SVC_PROTO_CASED (dom->case_sensitive ? state->proto \
      56             :                                             : state->cased_proto)
      57             : 
      58             : /* Provider Lookup Logic:
      59             :  * Iterate through the available caches. If the cached entry is
      60             :  * present and not expired, return it immediately(*). If it is
      61             :  * present and expired, add it to a list of domains eligible to
      62             :  * be checked. If it is in the negative cache, skip over it and
      63             :  * do not add it to the eligible domain list.
      64             :  *
      65             :  * Once we have searched all of the caches, if the entry has not
      66             :  * been determined to be available, search all domains in order
      67             :  * to see if any of them contain the requested entry.
      68             :  *
      69             :  * (*) Optionally perform a midpoint cache refresh if appropriate.
      70             :  */
      71             : 
      72             : static struct tevent_req *
      73           0 : getserv_send(TALLOC_CTX *mem_ctx,
      74             :              struct tevent_context *ev,
      75             :              uint16_t port,
      76             :              const char *service_name,
      77             :              const char *service_protocol,
      78             :              struct nss_dom_ctx *dctx)
      79             : {
      80             :     errno_t ret;
      81             :     struct tevent_req *req;
      82             :     struct tevent_req *subreq;
      83             :     struct getserv_ctx *state;
      84           0 :     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
      85           0 :     struct cli_ctx *cctx = cmdctx->cctx;
      86             :     struct sss_domain_info *dom;
      87           0 :     size_t num_domains = 0;
      88           0 :     size_t dom_idx = 0;
      89           0 :     struct nss_ctx *nctx =
      90           0 :             talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
      91           0 :     time_t now = time(NULL);
      92             :     uint64_t lastUpdate;
      93             :     uint64_t cacheExpire;
      94             :     uint64_t midpoint_refresh;
      95             : 
      96           0 :     req = tevent_req_create(mem_ctx, &state, struct getserv_ctx);
      97           0 :     if (!req) return NULL;
      98           0 :     state->dctx = dctx;
      99             : 
     100           0 :     for (dom = cctx->rctx->domains; dom; dom = get_next_domain(dom, 0)) {
     101           0 :         num_domains++;
     102             :     }
     103             : 
     104             :     /* Create an array of domains to check. To save resizes, we'll
     105             :      * assume that all will be checked
     106             :      */
     107           0 :     state->domains = talloc_zero_array(state,
     108             :                                        struct sss_domain_info *,
     109             :                                        num_domains + 1);
     110           0 :     if (!state->domains) {
     111           0 :         ret = ENOMEM;
     112           0 :         goto immediate;
     113             :     }
     114             : 
     115           0 :     state->port = port;
     116             : 
     117             :     /* Store both the case-sensitive and lowercased names
     118             :      * in the state object, to avoid recalculating the
     119             :      * lowercase in multiple domains.
     120             :      */
     121           0 :     if (service_protocol) {
     122           0 :         state->proto = talloc_strdup(state, service_protocol);
     123           0 :         if (!state->proto) {
     124           0 :             ret = ENOMEM;
     125           0 :             goto immediate;
     126             :         }
     127           0 :         state->cased_proto = sss_get_cased_name(state, service_protocol,
     128             :                                                 false);
     129           0 :         if (!state->cased_proto) {
     130           0 :             ret = ENOMEM;
     131           0 :             goto immediate;
     132             :         }
     133             :     } else {
     134           0 :         state->proto = NULL;
     135           0 :         state->cased_proto = NULL;
     136             :     }
     137             : 
     138             :     /* If we're looking up by name */
     139           0 :     if (service_name) {
     140             :         /* Store both the case-sensitive and lowercased names
     141             :          * in the state object, to avoid recalculating the
     142             :          * lowercase in multiple domains.
     143             :          */
     144           0 :         state->name = talloc_strdup(state, service_name);
     145           0 :         if (!state->name) {
     146           0 :             ret = ENOMEM;
     147           0 :             goto immediate;
     148             :         }
     149             : 
     150           0 :         state->cased_name = sss_get_cased_name(state, service_name,
     151             :                                                false);
     152           0 :         if (!state->cased_name) {
     153           0 :             ret = ENOMEM;
     154           0 :             goto immediate;
     155             :         }
     156             :     }
     157             : 
     158           0 :     dom = cctx->rctx->domains;
     159           0 :     while(dom) {
     160             :         /* if it is a domainless search, skip domains that require fully
     161             :           * qualified names instead */
     162           0 :          while (dom && cmdctx->check_next && dom->fqnames) {
     163           0 :              dom = get_next_domain(dom, 0);
     164             :          }
     165           0 :          if (!dom) break;
     166             : 
     167           0 :          if (dom->sysdb == NULL) {
     168           0 :              DEBUG(SSSDBG_CRIT_FAILURE,
     169             :                    "Critical: Sysdb CTX not found for [%s]!\n", dom->name);
     170           0 :              ret = EINVAL;
     171           0 :              goto immediate;
     172             :          }
     173             : 
     174             :          /* If we're looking up by name */
     175           0 :          if (service_name) {
     176             :              /* Check the negative cache */
     177           0 :              ret = sss_ncache_check_service(nctx->rctx->ncache, dom,
     178           0 :                                             SVC_NAME_CASED, SVC_PROTO_CASED);
     179             :              /* If negatively cached, return we didn't find it */
     180           0 :              if (ret == EEXIST) {
     181           0 :                  DEBUG(SSSDBG_TRACE_FUNC,
     182             :                        "Service [%s:%s] does not exist in [%s]! "
     183             :                         "(negative cache)\n",
     184             :                         SVC_NAME_CASED,
     185             :                         SVC_PROTO_CASED ? SVC_PROTO_CASED : "<ANY>",
     186             :                         dom->name);
     187             : 
     188             :                  /* If this is a multi-domain search, try the next one */
     189           0 :                  if (cmdctx->check_next) {
     190           0 :                      dom = get_next_domain(dom, 0);
     191             :                  } else {
     192             :                      /* This was a single-domain search.
     193             :                       * exit the loop. Since it was negatively-
     194             :                       * cached, don't add it to the eligible
     195             :                       * domains list.
     196             :                       */
     197           0 :                      dom = NULL;
     198             :                  }
     199             : 
     200           0 :                  continue;
     201             :              }
     202             : 
     203             :              /* Check the cache */
     204           0 :              DEBUG(SSSDBG_TRACE_FUNC,
     205             :                    "Checking cache for [%s:%s@%s]\n",
     206             :                     SVC_NAME_CASED,
     207             :                     SVC_PROTO_CASED ? SVC_PROTO_CASED : "<ANY>",
     208             :                     dom->name);
     209             : 
     210           0 :              ret = sysdb_getservbyname(state, dom,
     211           0 :                                        SVC_NAME_CASED,
     212           0 :                                        SVC_PROTO_CASED,
     213           0 :                                        &state->res);
     214             :          } else { /* Looking up by port */
     215             :              /* Check the negative cache */
     216           0 :              ret = sss_ncache_check_service_port(nctx->rctx->ncache, dom, port,
     217           0 :                                                  SVC_PROTO_CASED);
     218             :              /* If negatively cached, return we didn't find it */
     219           0 :              if (ret == EEXIST) {
     220           0 :                  DEBUG(SSSDBG_TRACE_FUNC,
     221             :                        "Service [%"PRIu16":%s] does not exist in [%s]! "
     222             :                         "(negative cache)\n",
     223             :                         port,
     224             :                         SVC_PROTO_CASED ? SVC_PROTO_CASED : "<ANY>",
     225             :                         dom->name);
     226             : 
     227             :                  /* If this is a multi-domain search, try the next one */
     228           0 :                  if (cmdctx->check_next) {
     229           0 :                      dom = get_next_domain(dom, 0);
     230             :                  } else {
     231             :                      /* This was a single-domain search.
     232             :                       * exit the loop. Since it was negatively-
     233             :                       * cached, don't add it to the eligible
     234             :                       * domains list.
     235             :                       */
     236           0 :                      dom = NULL;
     237             :                  }
     238             : 
     239           0 :                  continue;
     240             :              }
     241             : 
     242             :              /* Check the cache */
     243           0 :              DEBUG(SSSDBG_TRACE_FUNC,
     244             :                    "Checking cache for [%"PRIu16":%s@%s]\n",
     245             :                     port,
     246             :                     SVC_PROTO_CASED ? SVC_PROTO_CASED : "<ANY>",
     247             :                     dom->name);
     248             : 
     249           0 :              ret = sysdb_getservbyport(state, dom, port,
     250           0 :                                        SVC_PROTO_CASED,
     251           0 :                                        &state->res);
     252             :          }
     253           0 :          if (ret != EOK && ret != ENOENT) goto immediate;
     254             : 
     255           0 :          if (ret == ENOENT) {
     256             :              /* Not found in the cache. Add this domain to the
     257             :               * list of eligible domains to check the provider.
     258             :               */
     259           0 :              if (NEED_CHECK_PROVIDER(dom->provider)) {
     260           0 :                  state->domains[dom_idx] = dom;
     261           0 :                  dom_idx++;
     262             :              } else {
     263             :                  /* No provider to check. Set the negative cache here */
     264           0 :                  if (state->name) {
     265           0 :                      ret = sss_ncache_set_service_name(nctx->rctx->ncache, false,
     266             :                                                        dom,
     267           0 :                                                        SVC_NAME_CASED,
     268           0 :                                                        SVC_PROTO_CASED);
     269           0 :                      if (ret != EOK) {
     270             :                          /* Failure to set the negative cache is non-fatal.
     271             :                           * We'll log an error and continue.
     272             :                           */
     273           0 :                          DEBUG(SSSDBG_MINOR_FAILURE,
     274             :                                "Could not set negative cache for [%s][%s]\n",
     275             :                                 SVC_NAME_CASED, SVC_PROTO_CASED);
     276             :                      }
     277             :                  } else {
     278           0 :                      ret = sss_ncache_set_service_port(nctx->rctx->ncache, false,
     279             :                                                        dom,
     280           0 :                                                        state->port,
     281           0 :                                                        SVC_PROTO_CASED);
     282           0 :                      if (ret != EOK) {
     283             :                          /* Failure to set the negative cache is non-fatal.
     284             :                           * We'll log an error and continue.
     285             :                           */
     286           0 :                          DEBUG(SSSDBG_MINOR_FAILURE,
     287             :                                "Could not set negative cache for "
     288             :                                 "[%"PRIu16"][%s]\n",
     289             :                                 state->port, SVC_PROTO_CASED);
     290             :                      }
     291             :                  }
     292             :              }
     293             : 
     294             :              /* If this is a multi-domain search, try the next one */
     295           0 :              if (cmdctx->check_next) {
     296           0 :                  dom = get_next_domain(dom, 0);
     297             :              } else {
     298             :                  /* This was a single-domain search.
     299             :                   * exit the loop.
     300             :                   */
     301           0 :                  dom = NULL;
     302             :              }
     303           0 :              continue;
     304             :          }
     305             : 
     306             :          /* Found a result. Check its validity */
     307           0 :          if (state->res->count > 1) {
     308           0 :              DEBUG(SSSDBG_OP_FAILURE,
     309             :                    "getservby* returned more than one result!\n");
     310           0 :              ret = ENOENT;
     311           0 :              goto immediate;
     312             :          }
     313             : 
     314           0 :          lastUpdate = ldb_msg_find_attr_as_uint64(state->res->msgs[0],
     315             :                                                   SYSDB_LAST_UPDATE, 0);
     316             : 
     317           0 :          cacheExpire = ldb_msg_find_attr_as_uint64(state->res->msgs[0],
     318             :                                                    SYSDB_CACHE_EXPIRE, 0);
     319             : 
     320           0 :          midpoint_refresh = 0;
     321           0 :          if(nctx->cache_refresh_percent) {
     322           0 :              midpoint_refresh = lastUpdate +
     323           0 :                (cacheExpire - lastUpdate)*nctx->cache_refresh_percent/100.0;
     324           0 :              if (midpoint_refresh - lastUpdate < 10) {
     325             :                  /* If the percentage results in an expiration
     326             :                   * less than ten seconds after the lastUpdate time,
     327             :                   * that's too often we will simply set it to 10s
     328             :                   */
     329           0 :                  midpoint_refresh = lastUpdate+10;
     330             :              }
     331             :          }
     332             : 
     333           0 :          if (cacheExpire > now) {
     334             :              /* cache still valid */
     335             : 
     336           0 :              if (NEED_CHECK_PROVIDER(dom->provider)
     337           0 :                      && midpoint_refresh
     338           0 :                      && midpoint_refresh < now) {
     339             :                  /* We're past the cache refresh timeout
     340             :                   * We'll return the value from the cache, but we'll also
     341             :                   * queue the cache entry for update out-of-band.
     342             :                   */
     343           0 :                  DEBUG(SSSDBG_TRACE_FUNC,
     344             :                        "Performing midpoint cache update\n");
     345             : 
     346             :                  /* Update the cache */
     347           0 :                  subreq = sss_dp_get_account_send(cctx, cctx->rctx,
     348             :                                                   dom, true,
     349             :                                                   SSS_DP_SERVICES,
     350           0 :                                                   SVC_NAME_CASED,
     351             :                                                   port, NULL);
     352           0 :                  if (!subreq) {
     353           0 :                      DEBUG(SSSDBG_CRIT_FAILURE,
     354             :                            "Out of memory sending out-of-band data provider "
     355             :                             "request\n");
     356             :                      /* This is non-fatal, so we'll continue here */
     357             :                  }
     358             :                  /* We don't need to listen for a reply, so we will free the
     359             :                   * request here.
     360             :                   */
     361           0 :                  talloc_zfree(subreq);
     362             :              }
     363             : 
     364             :              /* The cache is valid. Return it */
     365           0 :              ret = EOK;
     366           0 :              goto immediate;
     367             :          } else {
     368             :              /* Cache is expired. Add this domain to the
     369             :               * list of eligible domains to check the provider.
     370             :               */
     371           0 :              if (NEED_CHECK_PROVIDER(dom->provider)) {
     372           0 :                  state->domains[dom_idx] = dom;
     373           0 :                  dom_idx++;
     374             :              }
     375             : 
     376             :              /* If this is a multi-domain search, try the next one */
     377           0 :              if (cmdctx->check_next) {
     378           0 :                  dom = get_next_domain(dom, 0);
     379             :              } else {
     380             :                  /* This was a single-domain search.
     381             :                   * exit the loop.
     382             :                   */
     383           0 :                  dom = NULL;
     384             :              }
     385             :          }
     386             :     }
     387             : 
     388             :     /* No valid cached entries found and
     389             :      * not found in negative caches.
     390             :      * Iterate through the domains and try
     391             :      * to look the data up.
     392             :      */
     393             : 
     394           0 :     state->dom_idx = 0;
     395           0 :     if (!state->domains[state->dom_idx]) {
     396             :         /* No domains to search. Return ENOENT */
     397           0 :         ret = ENOENT;
     398           0 :         goto immediate;
     399             :     }
     400             : 
     401           0 :     ret = lookup_service_step(req);
     402           0 :     if (ret != EOK) goto immediate;
     403             : 
     404           0 :     return req;
     405             : 
     406             : immediate:
     407           0 :     if (ret == EOK) {
     408           0 :         tevent_req_done(req);
     409             :     } else {
     410           0 :         tevent_req_error(req, ret);
     411             :     }
     412           0 :     tevent_req_post(req, ev);
     413           0 :     return req;
     414             : }
     415             : 
     416           0 : static errno_t lookup_service_step(struct tevent_req *req)
     417             : {
     418           0 :     struct getserv_ctx *state =
     419           0 :             tevent_req_data(req, struct getserv_ctx);
     420             :     struct tevent_req *subreq;
     421           0 :     struct cli_ctx *cctx = state->dctx->cmdctx->cctx;
     422           0 :     struct sss_domain_info *dom =
     423           0 :             state->domains[state->dom_idx];
     424             : 
     425             :     /* Update the cache */
     426           0 :     subreq = sss_dp_get_account_send(req,
     427             :                                      cctx->rctx,
     428             :                                      dom,
     429             :                                      true,
     430             :                                      SSS_DP_SERVICES,
     431           0 :                                      SVC_NAME_CASED,
     432           0 :                                      state->port,
     433           0 :                                      SVC_PROTO_CASED);
     434           0 :     if (!subreq) return ENOMEM;
     435           0 :     tevent_req_set_callback(subreq, lookup_service_done, req);
     436             : 
     437           0 :     return EOK;
     438             : }
     439             : 
     440           0 : static void lookup_service_done(struct tevent_req *subreq)
     441             : {
     442             :     errno_t ret;
     443             :     dbus_uint16_t err_maj;
     444             :     dbus_uint32_t err_min;
     445             :     char *err_msg;
     446             : 
     447           0 :     struct tevent_req *req =
     448           0 :             tevent_req_callback_data(subreq, struct tevent_req);
     449           0 :     struct getserv_ctx *state =
     450           0 :             tevent_req_data(req, struct getserv_ctx);
     451           0 :     struct cli_ctx *cctx = state->dctx->cmdctx->cctx;
     452           0 :     struct nss_ctx *nctx =
     453           0 :             talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
     454           0 :     struct sss_domain_info *dom = state->domains[state->dom_idx];
     455             : 
     456           0 :     ret = sss_dp_get_account_recv(state, subreq,
     457             :                                   &err_maj, &err_min,
     458             :                                   &err_msg);
     459           0 :     talloc_zfree(subreq);
     460           0 :     if (ret != EOK) {
     461           0 :         DEBUG(SSSDBG_OP_FAILURE,
     462             :               "Unable to get information from Data Provider\n"
     463             :                "dp_error: [%u], errno: [%u], error_msg: [%s]\n"
     464             :                "Will try to return what we have in cache\n",
     465             :                (unsigned int)err_maj, (unsigned int)err_min,
     466             :                err_msg ? err_msg : "none");
     467             :     }
     468             : 
     469             :     /* Recheck the cache after the lookup.
     470             :      * We can ignore the expiration values here, because
     471             :      * either we have just updated it or the provider is
     472             :      * offline. Either way, whatever is in the cache should
     473             :      * be returned, if it exists. Otherwise, move to the
     474             :      * next provider.
     475             :      */
     476           0 :     if (dom->sysdb == NULL) {
     477           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     478             :               "Critical: Sysdb CTX not found for [%s]!\n",
     479             :                 dom->name);
     480           0 :         ret = EINVAL;
     481           0 :         goto done;
     482             :     }
     483             : 
     484           0 :     if (state->name) {
     485           0 :         DEBUG(SSSDBG_TRACE_FUNC,
     486             :               "Re-checking cache for [%s:%s@%s]\n",
     487             :                SVC_NAME_CASED,
     488             :                SVC_PROTO_CASED ? SVC_PROTO_CASED : "<ANY>",
     489             :                dom->name);
     490             : 
     491           0 :         ret = sysdb_getservbyname(state, dom,
     492           0 :                                   SVC_NAME_CASED,
     493           0 :                                   SVC_PROTO_CASED,
     494             :                                   &state->res);
     495             :     } else {
     496           0 :         DEBUG(SSSDBG_TRACE_FUNC,
     497             :               "Re-checking cache for [%"PRIu16":%s@%s]\n",
     498             :                state->port,
     499             :                SVC_PROTO_CASED ? SVC_PROTO_CASED : "<ANY>",
     500             :                dom->name);
     501             : 
     502           0 :         ret = sysdb_getservbyport(state, dom,
     503           0 :                                   state->port,
     504           0 :                                   SVC_PROTO_CASED,
     505             :                                   &state->res);
     506             :     }
     507             : 
     508           0 :     if (ret == ENOENT) {
     509             :         /* Nothing in the cache.
     510             :          * Set the negative cache
     511             :          */
     512           0 :         if (state->name) {
     513           0 :             ret = sss_ncache_set_service_name(nctx->rctx->ncache, false,
     514             :                                               dom,
     515           0 :                                               SVC_NAME_CASED,
     516           0 :                                               SVC_PROTO_CASED);
     517           0 :             if (ret != EOK) {
     518             :                 /* Failure to set the negative cache is non-fatal.
     519             :                  * We'll log an error and continue.
     520             :                  */
     521           0 :                 DEBUG(SSSDBG_MINOR_FAILURE,
     522             :                       "Could not set negative cache for [%s][%s]\n",
     523             :                        SVC_NAME_CASED, SVC_PROTO_CASED);
     524             :             }
     525             :         } else {
     526           0 :             ret = sss_ncache_set_service_port(nctx->rctx->ncache, false,
     527             :                                               dom,
     528           0 :                                               state->port,
     529           0 :                                               SVC_PROTO_CASED);
     530           0 :             if (ret != EOK) {
     531             :                 /* Failure to set the negative cache is non-fatal.
     532             :                  * We'll log an error and continue.
     533             :                  */
     534           0 :                 DEBUG(SSSDBG_MINOR_FAILURE,
     535             :                       "Could not set negative cache for [%"PRIu16"][%s]\n",
     536             :                        state->port, SVC_PROTO_CASED);
     537             :             }
     538             :         }
     539             : 
     540             :         /* Need to check other domains */
     541           0 :         state->dom_idx++;
     542           0 :         if (!state->domains[state->dom_idx]) {
     543             :             /* No more domains to search. Return ENOENT */
     544           0 :             ret = ENOENT;
     545           0 :             goto done;
     546             :         }
     547           0 :         ret = lookup_service_step(req);
     548           0 :         if (ret != EOK) goto done;
     549             : 
     550             :         /* Set EAGAIN so we will re-enter the mainloop */
     551           0 :         ret = EAGAIN;
     552             :     }
     553             : 
     554             : done:
     555           0 :     if (ret == EOK) {
     556             :         /* Cache contained results. Return them */
     557           0 :         tevent_req_done(req);
     558           0 :     } else if (ret != EAGAIN) {
     559             :         /* An error occurred, fail the request */
     560           0 :         tevent_req_error(req, ret);
     561             :     }
     562             : 
     563             :     /* ret == EAGAIN: Reenter mainloop */
     564           0 :     return;
     565             : }
     566             : 
     567             : static errno_t
     568           0 : getserv_recv(TALLOC_CTX *mem_ctx,
     569             :              struct tevent_req *req,
     570             :              struct ldb_result **_res)
     571             : {
     572           0 :     struct getserv_ctx *state =
     573           0 :             tevent_req_data(req, struct getserv_ctx);
     574             : 
     575           0 :     TEVENT_REQ_RETURN_ON_ERROR(req);
     576             : 
     577           0 :     *_res = talloc_steal(mem_ctx, state->res);
     578             : 
     579           0 :     return EOK;
     580             : }
     581             : 
     582             : static errno_t
     583           0 : fill_service(struct sss_packet *packet,
     584             :              struct sss_domain_info *dom,
     585             :              const char *protocol,
     586             :              struct ldb_message **msgs,
     587             :              unsigned int *count)
     588             : {
     589             :     errno_t ret;
     590           0 :     unsigned int msg_count = *count;
     591             :     size_t rzero, rsize, aptr;
     592           0 :     unsigned int num = 0;
     593             :     unsigned int i, j;
     594             :     uint32_t num_aliases, written_aliases;
     595             :     struct ldb_message *msg;
     596             :     struct ldb_message_element *el;
     597           0 :     TALLOC_CTX *tmp_ctx = NULL;
     598             :     const char *orig_name;
     599             :     const char *orig_proto;
     600             :     struct sized_string cased_name;
     601             :     struct sized_string cased_proto;
     602             :     uint16_t port;
     603             :     char *tmpstr;
     604             :     uint8_t *body;
     605             :     size_t blen;
     606             :     struct sized_string alias;
     607             : 
     608             :     /* FIXME: Should we account for fully-qualified
     609             :      * service names?
     610             :      */
     611             : 
     612             :     /* first 2 fields (len and reserved), filled up later */
     613           0 :     ret = sss_packet_grow(packet, 2 * sizeof(uint32_t));
     614           0 :     if (ret != EOK) goto done;
     615             : 
     616           0 :     rzero = 2 * sizeof(uint32_t);
     617           0 :     rsize = 0;
     618             : 
     619           0 :     for (i = 0; i < msg_count; i++) {
     620           0 :         talloc_zfree(tmp_ctx);
     621           0 :         tmp_ctx = talloc_new(NULL);
     622           0 :         if (!tmp_ctx) return ENOMEM;
     623             : 
     624           0 :         msg = msgs[i];
     625             : 
     626             :         /* new service */
     627           0 :         if (!ldb_msg_check_string_attribute(msg, "objectClass",
     628             :                                             SYSDB_SVC_CLASS)) {
     629           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Wrong object (%s) found on stack!\n",
     630             :                       ldb_dn_get_linearized(msg->dn));
     631           0 :             continue;
     632             :         }
     633             : 
     634             :         /* new result starts at end of previous result */
     635           0 :         rzero += rsize;
     636           0 :         rsize = 0;
     637             : 
     638             :         /* Get the service name */
     639           0 :         orig_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
     640           0 :         tmpstr = sss_get_cased_name(tmp_ctx, orig_name, dom->case_preserve);
     641           0 :         if (tmpstr == NULL) {
     642           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     643             :                   "Could not identify service name, skipping\n");
     644           0 :             continue;
     645             :         }
     646           0 :         to_sized_string(&cased_name, tmpstr);
     647             : 
     648             :         /* Get the port */
     649           0 :         port = (uint16_t) ldb_msg_find_attr_as_uint(msg, SYSDB_SVC_PORT, 0);
     650           0 :         if (!port) {
     651           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     652             :                   "No port for service [%s]. Skipping\n", tmpstr);
     653           0 :             continue;
     654             :         }
     655             : 
     656             :         /* Get the service protocol.
     657             :          * Use the requested protocol if present,
     658             :          * otherwise take the first protocol returned
     659             :          * by the sysdb.
     660             :          * If more than one is available, select the
     661             :          * first in the message.
     662             :          */
     663           0 :         if (protocol) {
     664           0 :             orig_proto = protocol;
     665             :         } else {
     666           0 :             el = ldb_msg_find_element(msg, SYSDB_SVC_PROTO);
     667           0 :             if (el->num_values == 0) {
     668           0 :                 ret = EINVAL;
     669           0 :                 num = 0;
     670           0 :                 goto done;
     671             :             }
     672           0 :             orig_proto = (const char *)el->values[0].data;
     673             :         }
     674             : 
     675           0 :         tmpstr = sss_get_cased_name(tmp_ctx, orig_proto, dom->case_preserve);
     676           0 :         if (tmpstr == NULL) {
     677           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     678             :                   "sss_get_cased_name failed, skipping\n");
     679           0 :             continue;
     680             :         }
     681           0 :         to_sized_string(&cased_proto, tmpstr);
     682             : 
     683           0 :         ret = sss_packet_grow(packet, 2 * sizeof(uint16_t)
     684             :                               + sizeof(uint32_t)
     685           0 :                               + cased_name.len
     686           0 :                               + cased_proto.len);
     687           0 :         if (ret != EOK) {
     688           0 :             num = 0;
     689           0 :             goto done;
     690             :         }
     691           0 :         sss_packet_get_body(packet, &body, &blen);
     692             : 
     693             :         /* Store the port number */
     694           0 :         SAFEALIGN_SETMEM_UINT32(&body[rzero + rsize], (uint32_t)htons(port), &rsize);
     695             : 
     696             :         /* Get the aliases */
     697           0 :         el = ldb_msg_find_element(msg, SYSDB_NAME_ALIAS);
     698           0 :         if (!el) {
     699             :             /* No aliases for this user */
     700           0 :             num_aliases = 0;
     701             :         } else {
     702           0 :             num_aliases = el->num_values;
     703             :         }
     704             : 
     705             :         /* We'll store the alias count here */
     706           0 :         aptr = rzero+rsize;
     707           0 :         rsize += sizeof(uint32_t);
     708             : 
     709             :         /* Store the primary name */
     710           0 :         safealign_memcpy(&body[rzero + rsize],
     711           0 :                          cased_name.str,
     712             :                          cased_name.len,
     713             :                          &rsize);
     714             : 
     715             :         /* Store the protocol */
     716           0 :         safealign_memcpy(&body[rzero + rsize],
     717           0 :                          cased_proto.str,
     718             :                          cased_proto.len,
     719             :                          &rsize);
     720             : 
     721           0 :         written_aliases = 0;
     722           0 :         for (j = 0; j < num_aliases; j++) {
     723           0 :             if (sss_string_equal(dom->case_sensitive,
     724           0 :                              (const char *)el->values[j].data,
     725             :                              cased_name.str)) {
     726           0 :                 continue;
     727             :             }
     728           0 :             to_sized_string(&alias, (const char *)el->values[j].data);
     729             : 
     730           0 :             ret = sss_packet_grow(packet, alias.len);
     731           0 :             if (ret != EOK) {
     732           0 :                 num = 0;
     733           0 :                 goto done;
     734             :             }
     735           0 :             sss_packet_get_body(packet, &body, &blen);
     736             : 
     737             :             /* Store the alias */
     738           0 :             safealign_memcpy(&body[rzero + rsize],
     739           0 :                              alias.str,
     740             :                              alias.len,
     741             :                              &rsize);
     742             : 
     743           0 :             written_aliases++;
     744           0 :             talloc_zfree(tmpstr);
     745             :         }
     746             : 
     747             :         /* We must not advance rsize here, the data has already been
     748             :          * allocated and skipped earlier when aptr was assigned to.
     749             :          */
     750           0 :         SAFEALIGN_SETMEM_UINT32(&body[aptr], written_aliases, NULL);
     751             : 
     752           0 :         num++;
     753             :     }
     754             : 
     755           0 :     ret = EOK;
     756             : 
     757             : done:
     758           0 :     talloc_free(tmp_ctx);
     759             : 
     760           0 :     if (ret != EOK ||num == 0) {
     761             :         /* if num is 0 most probably something went wrong,
     762             :          * reset packet and return ENOENT */
     763           0 :         sss_packet_set_size(packet, 0);
     764           0 :         return ENOENT;
     765             :     }
     766             : 
     767             :     /* num results */
     768           0 :     SAFEALIGN_COPY_UINT32(body, &num, NULL);
     769             : 
     770             :     /* reserved */
     771           0 :     SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL);
     772             : 
     773           0 :     return ret;
     774             : }
     775             : /*****************
     776             :  * getservbyname *
     777             :  *****************/
     778             : 
     779             : errno_t parse_getservbyname(TALLOC_CTX *mem_ctx,
     780             :                             uint8_t *body, size_t blen,
     781             :                             struct sss_domain_info *domains,
     782             :                             char *default_domain,
     783             :                             char **domain_name,
     784             :                             char **service_name,
     785             :                             char **service_protocol);
     786             : 
     787             : static void
     788             : nss_cmd_getserv_done(struct tevent_req *req);
     789             : 
     790           0 : int nss_cmd_getservbyname(struct cli_ctx *cctx)
     791             : {
     792             :     errno_t ret;
     793             :     struct nss_cmd_ctx *cmdctx;
     794             :     struct nss_dom_ctx *dctx;
     795             :     char *domname;
     796             :     char *service_name;
     797             :     char *service_protocol;
     798             :     uint8_t *body;
     799             :     size_t blen;
     800             :     struct tevent_req *req;
     801             : 
     802           0 :     cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
     803           0 :     if (!cmdctx) return ENOMEM;
     804             : 
     805           0 :     cmdctx->cctx = cctx;
     806             : 
     807           0 :     dctx = talloc_zero(cmdctx, struct nss_dom_ctx);
     808           0 :     if (!dctx) {
     809           0 :         ret = ENOMEM;
     810           0 :         goto done;
     811             :     }
     812           0 :     dctx->cmdctx = cmdctx;
     813             : 
     814             :     /* get service name and protocol */
     815           0 :     sss_packet_get_body(cctx->creq->in, &body, &blen);
     816             :     /* if not terminated fail */
     817           0 :     if (body[blen -1] != '\0') {
     818           0 :         ret = EINVAL;
     819           0 :         goto done;
     820             :     }
     821             : 
     822           0 :     ret = parse_getservbyname(cmdctx, body, blen,
     823           0 :                               cctx->rctx->domains,
     824           0 :                               cctx->rctx->default_domain,
     825             :                               &domname,
     826             :                               &service_name,
     827             :                               &service_protocol);
     828           0 :     if (ret != EOK) {
     829           0 :         DEBUG(SSSDBG_OP_FAILURE,
     830             :               "Could not parse request\n");
     831           0 :         goto done;
     832             :     }
     833             : 
     834           0 :     dctx->protocol = service_protocol;
     835             : 
     836           0 :     DEBUG(SSSDBG_TRACE_FUNC,
     837             :           "Requesting info for service [%s:%s] from [%s]\n",
     838             :            service_name,
     839             :            service_protocol ? service_protocol : "<ANY>",
     840             :            domname ? domname : "<ALL>");
     841             : 
     842           0 :     if (domname) {
     843           0 :         dctx->domain = responder_get_domain(cctx->rctx, domname);
     844           0 :         if (!dctx->domain) {
     845           0 :             ret = ENOENT;
     846           0 :             goto done;
     847             :         }
     848             :     } else {
     849             :         /* this is a multidomain search */
     850           0 :         dctx->domain = cctx->rctx->domains;
     851           0 :         cmdctx->check_next = true;
     852             :     }
     853             : 
     854             :     /* Identify if this backend requires a provider check */
     855           0 :     dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
     856             : 
     857             :     /* Ok, find it! */
     858           0 :     req = getserv_send(cmdctx, cctx->ev, 0,
     859             :                        service_name,
     860             :                        service_protocol,
     861             :                        dctx);
     862           0 :     if (!req) {
     863           0 :         ret = ENOMEM;
     864           0 :         goto done;
     865             :     }
     866           0 :     tevent_req_set_callback(req, nss_cmd_getserv_done, dctx);
     867             : 
     868             : done:
     869           0 :     return nss_cmd_done(cmdctx, ret);
     870             : }
     871             : 
     872           0 : errno_t parse_getservbyname(TALLOC_CTX *mem_ctx,
     873             :                             uint8_t *body, size_t blen,
     874             :                             struct sss_domain_info *domains,
     875             :                             char *default_domain,
     876             :                             char **domain_name,
     877             :                             char **service_name,
     878             :                             char **service_protocol)
     879             : {
     880             :     errno_t ret;
     881             :     size_t i, j, namelen;
     882             :     char *rawname;
     883             :     char *domname;
     884             :     char *svc_name;
     885             :     char *protocol;
     886             :     TALLOC_CTX *tmp_ctx;
     887             : 
     888           0 :     tmp_ctx = talloc_new(NULL);
     889           0 :     if (!tmp_ctx) return ENOMEM;
     890             : 
     891             :     /* The raw name is at most one character shorter
     892             :      * than the body length (if the protocol wasn't
     893             :      * specified). Since this is a common case, we'll
     894             :      * just assume the maximum memory size for the
     895             :      * rawname.
     896             :      */
     897           0 :     rawname = talloc_array(tmp_ctx, char, blen - 1);
     898           0 :     if (!rawname) {
     899           0 :         ret = ENOMEM;
     900           0 :         goto done;
     901             :     }
     902             : 
     903           0 :     i = j = 0;
     904             : 
     905             :     /* Copy in the service name */
     906           0 :     while (i < (blen - 1) && body[i]) {
     907           0 :         rawname[j] = body[i];
     908           0 :         i++;
     909           0 :         j++;
     910             :     }
     911           0 :     if (body[i] != '\0') {
     912             :         /* blen - 1 was reached without hitting
     913             :          * a NULL-terminator. No protocol field
     914             :          * is possible.
     915             :          */
     916           0 :         ret = EINVAL;
     917           0 :         goto done;
     918             :     }
     919           0 :     rawname[j] = '\0';
     920             : 
     921           0 :     i++;
     922           0 :     namelen = i;
     923           0 :     j = 0;
     924             : 
     925             :     /* Copy in the protocol */
     926           0 :     if (body[i] == '\0') {
     927             :         /* Zero-length protocol
     928             :          * Just set the protocol to NULL
     929             :          */
     930           0 :         protocol = NULL;
     931             :     } else {
     932             :         /* The protocol must be no longer than the remaining
     933             :          * body space, after the name was copied.
     934             :          */
     935           0 :         protocol = talloc_array(tmp_ctx, char, blen - i);
     936           0 :         if (!protocol) {
     937           0 :             ret = ENOMEM;
     938           0 :             goto done;
     939             :         }
     940             : 
     941           0 :         while (i < blen && body[i]) {
     942           0 :             protocol[j] = body[i];
     943           0 :             i++;
     944           0 :             j++;
     945             :         }
     946           0 :         if (body[i] != '\0') {
     947             :             /* blen was reached without hitting
     948             :              * a NULL-terminator.
     949             :              */
     950           0 :             ret = EINVAL;
     951           0 :             goto done;
     952             :         }
     953             : 
     954           0 :         protocol[j] = '\0';
     955             : 
     956           0 :         if (j != blen - namelen - 1) {
     957           0 :             DEBUG(SSSDBG_MINOR_FAILURE,
     958             :                   "Body longer than the name and protocol\n");
     959           0 :             ret = EINVAL;
     960           0 :             goto done;
     961             :         }
     962             :     }
     963             : 
     964           0 :     ret = sss_parse_name_for_domains(tmp_ctx, domains, default_domain, rawname,
     965             :                                      &domname, &svc_name);
     966           0 :     if (ret != EOK) {
     967           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
     968             :               "Could not split name and domain of [%s]\n",
     969             :                rawname);
     970           0 :         goto done;
     971             :     }
     972             : 
     973           0 :     *domain_name = talloc_steal(mem_ctx, domname);
     974           0 :     *service_name = talloc_steal(mem_ctx, svc_name);
     975           0 :     *service_protocol = talloc_steal(mem_ctx, protocol);
     976             : 
     977           0 :     ret = EOK;
     978             : 
     979             : done:
     980           0 :     talloc_free(tmp_ctx);
     981           0 :     return ret;
     982             : }
     983             : 
     984             : static void
     985           0 : nss_cmd_getserv_done(struct tevent_req *req)
     986             : {
     987             :     errno_t ret, reqret;
     988             :     unsigned int i;
     989             : 
     990           0 :     struct nss_dom_ctx *dctx =
     991           0 :             tevent_req_callback_data(req, struct nss_dom_ctx);
     992           0 :     struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
     993             : 
     994           0 :     reqret = getserv_recv(dctx, req, &dctx->res);
     995           0 :     talloc_zfree(req);
     996           0 :     if (reqret != EOK && reqret != ENOENT) {
     997           0 :         DEBUG(SSSDBG_OP_FAILURE,
     998             :               "getservbyname failed\n");
     999           0 :         nss_cmd_done(cmdctx, reqret);
    1000           0 :         return;
    1001             :     }
    1002             : 
    1003             :     /* Either we succeeded or no domains were eligible */
    1004           0 :     ret = sss_packet_new(cmdctx->cctx->creq, 0,
    1005           0 :                          sss_packet_get_cmd(cmdctx->cctx->creq->in),
    1006           0 :                          &cmdctx->cctx->creq->out);
    1007           0 :     if (ret == EOK) {
    1008           0 :         if (reqret == ENOENT) {
    1009             :             /* Notify the caller that this entry wasn't found */
    1010           0 :             ret = sss_cmd_empty_packet(cmdctx->cctx->creq->out);
    1011             :         } else {
    1012           0 :             i = dctx->res->count;
    1013           0 :             ret = fill_service(cmdctx->cctx->creq->out,
    1014             :                                dctx->domain,
    1015             :                                dctx->protocol,
    1016           0 :                                dctx->res->msgs,
    1017             :                                &i);
    1018             :         }
    1019           0 :         if (ret != EOK) {
    1020           0 :             DEBUG(SSSDBG_OP_FAILURE,
    1021             :                   "Could not create response packet: [%s]\n",
    1022             :                    strerror(ret));
    1023             :         }
    1024             : 
    1025           0 :         sss_cmd_done(cmdctx->cctx, cmdctx);
    1026           0 :         return;
    1027             :     }
    1028             : 
    1029           0 :     DEBUG(SSSDBG_OP_FAILURE, "Error creating packet\n");
    1030             : }
    1031             : 
    1032           0 : errno_t parse_getservbyport(TALLOC_CTX *mem_ctx,
    1033             :                             uint8_t *body, size_t blen,
    1034             :                             uint16_t *service_port,
    1035             :                             char **service_protocol)
    1036             : {
    1037             :     errno_t ret;
    1038             :     size_t i, j;
    1039             :     size_t port_and_padding_len;
    1040             :     uint16_t c, port;
    1041             :     char *protocol;
    1042           0 :     TALLOC_CTX *tmp_ctx = talloc_new(NULL);
    1043           0 :     if (!tmp_ctx) return ENOMEM;
    1044             : 
    1045             :     /* Copy in the port */
    1046           0 :     SAFEALIGN_COPY_UINT16(&c, body, NULL);
    1047           0 :     port = ntohs(c);
    1048             : 
    1049           0 :     port_and_padding_len = 2 * sizeof(uint16_t) + sizeof(uint32_t);
    1050           0 :     i = port_and_padding_len;
    1051           0 :     j = 0;
    1052             : 
    1053             :     /* Copy in the protocol */
    1054           0 :     if (body[i] == '\0') {
    1055             :         /* Zero-length protocol
    1056             :          * Just set the protocol to NULL
    1057             :          */
    1058           0 :         protocol = NULL;
    1059             :     } else {
    1060             :         /* The protocol must be no longer than the remaining
    1061             :          * body space.
    1062             :          */
    1063           0 :         protocol = talloc_array(tmp_ctx, char, blen - i);
    1064           0 :         if (!protocol) {
    1065           0 :             ret = ENOMEM;
    1066           0 :             goto done;
    1067             :         }
    1068             : 
    1069           0 :         while (i < blen && body[i]) {
    1070           0 :             protocol[j] = body[i];
    1071           0 :             i++;
    1072           0 :             j++;
    1073             :         }
    1074           0 :         if (body[i] != '\0') {
    1075             :             /* blen was reached without hitting
    1076             :              * a NULL-terminator.
    1077             :              */
    1078           0 :             ret = EINVAL;
    1079           0 :             goto done;
    1080             :         }
    1081             : 
    1082           0 :         protocol[j] = '\0';
    1083             : 
    1084           0 :         if (j != blen - port_and_padding_len - 1) {
    1085           0 :             DEBUG(SSSDBG_MINOR_FAILURE,
    1086             :                   "Body longer than the name and protocol\n");
    1087           0 :             ret = EINVAL;
    1088           0 :             goto done;
    1089             :         }
    1090             :     }
    1091             : 
    1092           0 :     *service_port = port;
    1093           0 :     *service_protocol = talloc_steal(mem_ctx, protocol);
    1094             : 
    1095           0 :     ret = EOK;
    1096             : 
    1097             : done:
    1098           0 :     talloc_free(tmp_ctx);
    1099           0 :     return ret;
    1100             : }
    1101             : 
    1102             : /*****************
    1103             :  * getservbyport *
    1104             :  *****************/
    1105           0 : int nss_cmd_getservbyport(struct cli_ctx *cctx)
    1106             : {
    1107             :     errno_t ret;
    1108             :     struct nss_cmd_ctx *cmdctx;
    1109             :     struct nss_dom_ctx *dctx;
    1110             :     uint16_t port;
    1111             :     char *service_protocol;
    1112             :     uint8_t *body;
    1113             :     size_t blen;
    1114             :     struct tevent_req *req;
    1115             : 
    1116           0 :     cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
    1117           0 :     if (!cmdctx) return ENOMEM;
    1118             : 
    1119           0 :     cmdctx->cctx = cctx;
    1120             : 
    1121           0 :     dctx = talloc_zero(cmdctx, struct nss_dom_ctx);
    1122           0 :     if (!dctx) {
    1123           0 :         ret = ENOMEM;
    1124           0 :         goto done;
    1125             :     }
    1126           0 :     dctx->cmdctx = cmdctx;
    1127             : 
    1128             :     /* get service port and protocol */
    1129           0 :     sss_packet_get_body(cctx->creq->in, &body, &blen);
    1130             :     /* if not terminated fail */
    1131           0 :     if (body[blen -1] != '\0') {
    1132           0 :         ret = EINVAL;
    1133           0 :         goto done;
    1134             :     }
    1135             : 
    1136           0 :     ret = parse_getservbyport(cmdctx, body, blen,
    1137             :                               &port,
    1138             :                               &service_protocol);
    1139           0 :     if (ret != EOK) {
    1140           0 :         DEBUG(SSSDBG_OP_FAILURE,
    1141             :               "Could not parse request\n");
    1142           0 :         goto done;
    1143             :     }
    1144             : 
    1145           0 :     dctx->protocol = service_protocol;
    1146             : 
    1147           0 :     DEBUG(SSSDBG_TRACE_FUNC,
    1148             :           "Requesting info for service on port [%"PRIu16"/%s]\n",
    1149             :            port, service_protocol ? service_protocol : "<ANY>");
    1150             : 
    1151             :     /* All port lookups are multidomain searches */
    1152           0 :     dctx->domain = cctx->rctx->domains;
    1153           0 :     cmdctx->check_next = true;
    1154             : 
    1155             :     /* Identify if this backend requires a provider check */
    1156           0 :     dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
    1157             : 
    1158             :     /* Ok, find it! */
    1159           0 :     req = getserv_send(cmdctx, cctx->ev, port,
    1160             :                        NULL, service_protocol, dctx);
    1161           0 :     if (!req) {
    1162           0 :         ret = ENOMEM;
    1163           0 :         goto done;
    1164             :     }
    1165           0 :     tevent_req_set_callback(req, nss_cmd_getserv_done, dctx);
    1166             : 
    1167             : done:
    1168           0 :     return nss_cmd_done(cmdctx, ret);
    1169             : }
    1170             : 
    1171             : struct setservent_ctx {
    1172             :     struct cli_ctx *cctx;
    1173             :     struct nss_ctx *nctx;
    1174             :     struct nss_dom_ctx *dctx;
    1175             :     struct getent_ctx *getent_ctx;
    1176             : };
    1177             : 
    1178             : static errno_t
    1179             : setservent_step(struct setent_step_ctx *step_ctx);
    1180             : static void
    1181             : setservent_step_done(struct tevent_req *req);
    1182             : 
    1183             : static struct tevent_req *
    1184             : lookup_servent_send(TALLOC_CTX *mem_ctx,
    1185             :                     struct resp_ctx *rctx,
    1186             :                     struct sss_domain_info *dom);
    1187             : 
    1188             : static struct tevent_req *
    1189           0 : setservent_send(TALLOC_CTX *mem_ctx, struct cli_ctx *cctx)
    1190             : {
    1191             :     errno_t ret;
    1192             :     unsigned int num_domains;
    1193             :     struct tevent_req *req;
    1194             :     struct setservent_ctx *state;
    1195             :     struct sss_domain_info *dom;
    1196             :     struct setent_step_ctx *step_ctx;
    1197           0 :     struct nss_ctx *nctx =
    1198           0 :             talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    1199             : 
    1200           0 :     DEBUG(SSSDBG_TRACE_FUNC, "Received setservent request\n");
    1201             : 
    1202             :     /* Reset the read pointers */
    1203           0 :     cctx->svc_dom_idx = 0;
    1204           0 :     cctx->svcent_cur = 0;
    1205             : 
    1206           0 :     req = tevent_req_create(mem_ctx, &state, struct setservent_ctx);
    1207           0 :     if (!req) return NULL;
    1208             : 
    1209           0 :     state->nctx = nctx;
    1210           0 :     state->cctx = cctx;
    1211           0 :     state->dctx = talloc_zero(state, struct nss_dom_ctx);
    1212           0 :     if (!state->dctx) {
    1213           0 :         ret = ENOMEM;
    1214           0 :         goto immediate;
    1215             :     }
    1216           0 :     state->dctx->domain = cctx->rctx->domains;
    1217             : 
    1218             :     /* Is the result context already available */
    1219           0 :     if (state->nctx->svcctx) {
    1220           0 :         if (state->nctx->svcctx->ready) {
    1221             :             /* All of the necessary data is in place
    1222             :              * We can return now, getservent requests will work at this point
    1223             :              */
    1224           0 :             ret = EOK;
    1225           0 :             goto immediate;
    1226             :         }
    1227             :         else {
    1228             :             /* Object is still being constructed
    1229             :              * Register for notification when it's
    1230             :              * ready.
    1231             :              */
    1232           0 :             ret = nss_setent_add_ref(state, state->nctx->svcctx, req);
    1233           0 :             if (ret != EOK) goto immediate;
    1234             :         }
    1235           0 :         return req;
    1236             :     }
    1237             : 
    1238             :     /* Create a new result context
    1239             :      * We are creating it on the nss_ctx so that it doesn't
    1240             :      * go away if the original request does. We will delete
    1241             :      * it when the refcount goes to zero;
    1242             :      */
    1243           0 :     state->nctx->svcctx = talloc_zero(nctx, struct getent_ctx);
    1244           0 :     if (!state->nctx->svcctx) {
    1245           0 :         ret = ENOMEM;
    1246           0 :         goto immediate;
    1247             :     }
    1248           0 :     state->getent_ctx = nctx->svcctx;
    1249             : 
    1250             :     /* Assume that all domains will have results (to avoid having
    1251             :      * to reallocate later
    1252             :      */
    1253           0 :     num_domains = 0;
    1254           0 :     for (dom = state->cctx->rctx->domains;
    1255             :          dom;
    1256           0 :          dom = get_next_domain(dom, 0)) {
    1257           0 :         num_domains++;
    1258             :     }
    1259             : 
    1260           0 :     state->nctx->svcctx->doms = talloc_zero_array(state->nctx->svcctx,
    1261             :                                                   struct dom_ctx,
    1262             :                                                   num_domains);
    1263           0 :     if (!state->nctx->svcctx->doms) {
    1264           0 :         ret = ENOMEM;
    1265           0 :         goto immediate;
    1266             :     }
    1267             : 
    1268             :     /* Add a callback reference for ourselves */
    1269           0 :     ret = nss_setent_add_ref(state, state->nctx->svcctx, req);
    1270           0 :     if (ret != EOK) {
    1271           0 :         goto immediate;
    1272             :     }
    1273             : 
    1274             :     /* ok, start the searches */
    1275           0 :     step_ctx = talloc_zero(state->getent_ctx, struct setent_step_ctx);
    1276           0 :     if (!step_ctx) {
    1277           0 :         ret = ENOMEM;
    1278           0 :         goto immediate;
    1279             :     }
    1280             : 
    1281             :     /* Steal the dom_ctx onto the step_ctx so it doesn't go out of scope if
    1282             :      * this request is canceled while other requests are in-progress.
    1283             :      */
    1284           0 :     step_ctx->dctx = talloc_steal(step_ctx, state->dctx);
    1285           0 :     step_ctx->nctx = state->nctx;
    1286           0 :     step_ctx->getent_ctx = state->getent_ctx;
    1287           0 :     step_ctx->rctx = cctx->rctx;
    1288           0 :     step_ctx->cctx = cctx;
    1289           0 :     step_ctx->returned_to_mainloop = false;
    1290             : 
    1291           0 :     while (step_ctx->dctx->domain) {
    1292             :         /* There are more domains to check */
    1293           0 :         ret = setservent_step(step_ctx);
    1294           0 :         if (ret == EOK) {
    1295             :             /* Re-enter the mainloop */
    1296           0 :             return req;
    1297             :         }
    1298             : 
    1299           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1300             :               "Error [%s] requesting info from domain [%s]. Skipping.\n",
    1301             :                strerror(ret), step_ctx->dctx->domain->name);
    1302             : 
    1303           0 :         step_ctx->dctx->domain = get_next_domain(step_ctx->dctx->domain, 0);
    1304             :     }
    1305             : 
    1306             :     /* All domains failed */
    1307           0 :     ret = EIO;
    1308             : 
    1309             : immediate:
    1310           0 :     if (ret == EOK) {
    1311           0 :         tevent_req_done(req);
    1312             :     } else {
    1313           0 :         tevent_req_error(req, ret);
    1314             :     }
    1315           0 :     tevent_req_post(req, cctx->rctx->ev);
    1316           0 :     return req;
    1317             : }
    1318             : 
    1319             : static errno_t
    1320           0 : setservent_step(struct setent_step_ctx *step_ctx)
    1321             : {
    1322             :     struct tevent_req *req;
    1323             : 
    1324           0 :     req = lookup_servent_send(step_ctx,
    1325             :                               step_ctx->rctx,
    1326           0 :                               step_ctx->dctx->domain);
    1327           0 :     if (!req) {
    1328           0 :         return ENOMEM;
    1329             :     }
    1330           0 :     tevent_req_set_callback(req, setservent_step_done, step_ctx);
    1331             : 
    1332           0 :     return EOK;
    1333             : }
    1334             : 
    1335             : struct lookup_servent_ctx {
    1336             :     struct resp_ctx *rctx;
    1337             :     struct sss_domain_info *dom;
    1338             :     struct ldb_result *res;
    1339             : };
    1340             : 
    1341             : static void
    1342             : lookup_servent_done(struct tevent_req *subreq);
    1343             : 
    1344             : static void
    1345             : setservent_finalize(struct setent_step_ctx *step_ctx);
    1346             : 
    1347             : static struct tevent_req *
    1348           0 : lookup_servent_send(TALLOC_CTX *mem_ctx,
    1349             :                     struct resp_ctx *rctx,
    1350             :                     struct sss_domain_info *dom)
    1351             : {
    1352             :     errno_t ret;
    1353             :     struct tevent_req *req;
    1354             :     struct tevent_req *subreq;
    1355             :     struct lookup_servent_ctx *state;
    1356             :     struct sysdb_ctx *sysdb;
    1357             : 
    1358           0 :     req = tevent_req_create(mem_ctx, &state, struct lookup_servent_ctx);
    1359           0 :     if (!req) return NULL;
    1360             : 
    1361           0 :     state->rctx = rctx;
    1362           0 :     state->dom = dom;
    1363             : 
    1364           0 :     if (!dom->enumerate) {
    1365           0 :         ret = ENOENT;
    1366           0 :         goto immediate;
    1367             :     }
    1368             : 
    1369           0 :     if (!(NEED_CHECK_PROVIDER(dom->name))) {
    1370             :         /* No provider check required. Just ask the
    1371             :          * sysdb.
    1372             :          */
    1373           0 :         sysdb = dom->sysdb;
    1374           0 :         if (sysdb == NULL) {
    1375           0 :             DEBUG(SSSDBG_FATAL_FAILURE,
    1376             :                   "Sysdb CTX not found for [%s]!\n", dom->name);
    1377           0 :             ret = EINVAL;
    1378           0 :             goto immediate;
    1379             :         }
    1380             : 
    1381           0 :         ret = sysdb_enumservent(state, dom, &state->res);
    1382             :         /* Whatever the result, we're done, so report it */
    1383           0 :         goto immediate;
    1384             :     }
    1385             : 
    1386             :     /* We need to ask the provider for an enumeration */
    1387             :     /* Update the cache */
    1388           0 :     subreq = sss_dp_get_account_send(req,  rctx, state->dom,
    1389             :                                      true, SSS_DP_SERVICES,
    1390             :                                      NULL, 0, NULL);
    1391           0 :     if (!subreq) {
    1392           0 :         ret = ENOMEM;
    1393           0 :         goto immediate;
    1394             :     }
    1395           0 :     tevent_req_set_callback(subreq, lookup_servent_done, req);
    1396             : 
    1397           0 :     return req;
    1398             : 
    1399             : immediate:
    1400           0 :     if (ret == EOK) {
    1401           0 :         tevent_req_done(req);
    1402             :     } else {
    1403           0 :         tevent_req_error(req, ENOENT);
    1404             :     }
    1405           0 :     tevent_req_post(req, rctx->ev);
    1406           0 :     return req;
    1407             : }
    1408             : 
    1409             : static void
    1410           0 : lookup_servent_done(struct tevent_req *subreq)
    1411             : {
    1412             :     errno_t ret;
    1413             :     dbus_uint16_t dp_err;
    1414             :     dbus_uint32_t dp_ret;
    1415             :     char *err_msg;
    1416             :     struct sysdb_ctx *sysdb;
    1417           0 :     struct tevent_req *req =
    1418           0 :             tevent_req_callback_data(subreq, struct tevent_req);
    1419           0 :     struct lookup_servent_ctx *state =
    1420           0 :             tevent_req_data(req, struct lookup_servent_ctx);
    1421             : 
    1422           0 :     ret = sss_dp_get_account_recv(state, subreq,
    1423             :                                   &dp_err, &dp_ret, &err_msg);
    1424           0 :     talloc_zfree(subreq);
    1425           0 :     if (ret != EOK) {
    1426           0 :         DEBUG(SSSDBG_OP_FAILURE,
    1427             :               "Unable to get information from Data Provider\n"
    1428             :                "dp_error: [%u], errno: [%u], error_msg: [%s]\n"
    1429             :                "Will try to return what we have in cache\n",
    1430             :                (unsigned int)dp_err, (unsigned int)dp_ret,
    1431             :                err_msg ? err_msg : "none");
    1432             :     }
    1433             : 
    1434             :     /* Check the cache now */
    1435           0 :     sysdb = state->dom->sysdb;
    1436           0 :     if (sysdb == NULL) {
    1437           0 :         DEBUG(SSSDBG_FATAL_FAILURE,
    1438             :               "Sysdb CTX not found for [%s]!\n", state->dom->name);
    1439           0 :         ret = EINVAL;
    1440           0 :         goto done;
    1441             :     }
    1442             : 
    1443           0 :     ret = sysdb_enumservent(state, state->dom, &state->res);
    1444             :     /* Whatever the result, we're done, so report it */
    1445             : 
    1446             : done:
    1447           0 :     if (ret == EOK) {
    1448           0 :         tevent_req_done(req);
    1449             :     } else {
    1450           0 :         tevent_req_error(req, ret);
    1451             :     }
    1452           0 : }
    1453             : 
    1454             : static errno_t
    1455           0 : lookup_servent_recv(TALLOC_CTX *mem_ctx,
    1456             :                     struct tevent_req *req,
    1457             :                     struct ldb_result **res)
    1458             : {
    1459           0 :     struct lookup_servent_ctx *state =
    1460           0 :             tevent_req_data(req, struct lookup_servent_ctx);
    1461             : 
    1462           0 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    1463             : 
    1464           0 :     *res = talloc_steal(mem_ctx, state->res);
    1465           0 :     return EOK;
    1466             : }
    1467             : 
    1468             : static void
    1469           0 : setservent_step_done(struct tevent_req *req)
    1470             : {
    1471             :     errno_t ret;
    1472           0 :     struct ldb_result *res = NULL;
    1473           0 :     struct setent_step_ctx *step_ctx =
    1474           0 :             tevent_req_callback_data(req, struct setent_step_ctx);
    1475           0 :     struct nss_dom_ctx *dctx = step_ctx->dctx;
    1476           0 :     struct getent_ctx *svcctx = step_ctx->getent_ctx;
    1477             : 
    1478             : 
    1479           0 :     ret = lookup_servent_recv(step_ctx, req, &res);
    1480           0 :     talloc_zfree(req);
    1481           0 :     if (ret == ENOENT) {
    1482           0 :         DEBUG(SSSDBG_TRACE_FUNC,
    1483             :               "Domain [%s] returned no results\n", dctx->domain->name);
    1484           0 :     } else if (ret != EOK) {
    1485           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1486             :               "Error [%s] while retrieving info from domain [%s]. "
    1487             :                "Skipping.\n", strerror(ret), dctx->domain->name);
    1488             :         /* Continue on */
    1489             :     } else {
    1490             :         /* Got some results
    1491             :          * Add the retrieved results to the list
    1492             :          */
    1493           0 :         svcctx->doms[svcctx->num].domain = dctx->domain;
    1494           0 :         svcctx->doms[svcctx->num].res = talloc_steal(svcctx->doms, res);
    1495           0 :         svcctx->num++;
    1496             :     }
    1497             : 
    1498           0 :     step_ctx->dctx->domain = get_next_domain(step_ctx->dctx->domain, 0);
    1499             : 
    1500           0 :     while (step_ctx->dctx->domain) {
    1501             :         /* There are more domains to check */
    1502           0 :         ret = setservent_step(step_ctx);
    1503           0 :         if (ret == EOK) {
    1504             :             /* Re-enter the mainloop */
    1505           0 :             return;
    1506             :         }
    1507             : 
    1508           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1509             :               "Error [%s] requesting info from domain [%s]. Skipping.\n",
    1510             :                strerror(ret), step_ctx->dctx->domain->name);
    1511             : 
    1512           0 :         step_ctx->dctx->domain = get_next_domain(step_ctx->dctx->domain, 0);
    1513             :     }
    1514             : 
    1515             :     /* All domains have been checked */
    1516           0 :     setservent_finalize(step_ctx);
    1517             : }
    1518             : 
    1519             : static void
    1520             : setservent_result_timeout(struct tevent_context *ev,
    1521             :                           struct tevent_timer *te,
    1522             :                           struct timeval current_time,
    1523             :                           void *pvt);
    1524             : 
    1525             : static void
    1526           0 : setservent_finalize(struct setent_step_ctx *step_ctx)
    1527             : {
    1528           0 :     struct nss_ctx *nctx = step_ctx->nctx;
    1529           0 :     struct resp_ctx *rctx = step_ctx->rctx;
    1530             :     struct timeval tv;
    1531             :     struct tevent_timer *te;
    1532             : 
    1533             :     /* We've finished all our lookups
    1534             :      * The result object is now safe to read.
    1535             :      */
    1536           0 :     nctx->svcctx->ready = true;
    1537             : 
    1538             :     /* Set up a lifetime timer for this result object
    1539             :      * We don't want this result object to outlive the
    1540             :      * enum cache refresh timeout
    1541             :      */
    1542           0 :     tv = tevent_timeval_current_ofs(nctx->enum_cache_timeout, 0);
    1543           0 :     te = tevent_add_timer(rctx->ev, nctx->svcctx, tv,
    1544             :                           setservent_result_timeout, nctx);
    1545           0 :     if (!te) {
    1546           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1547             :               "Could not set up life timer for setservent result object. "
    1548             :                "Entries may become stale.\n");
    1549             :     }
    1550             : 
    1551           0 :     nss_setent_notify_done(nctx->svcctx);
    1552           0 : }
    1553             : 
    1554             : static void
    1555           0 : setservent_result_timeout(struct tevent_context *ev,
    1556             :                           struct tevent_timer *te,
    1557             :                           struct timeval current_time,
    1558             :                           void *pvt)
    1559             : {
    1560           0 :     struct nss_ctx *nctx = talloc_get_type(pvt, struct nss_ctx);
    1561             : 
    1562           0 :     DEBUG(SSSDBG_TRACE_FUNC,
    1563             :           "setservent result object has expired. Cleaning up.\n");
    1564             : 
    1565             :     /* Free the service enumeration context.
    1566             :      * If additional getservent requests come in, they will invoke
    1567             :      * an implicit setservent and refresh the result object.
    1568             :      */
    1569           0 :     talloc_zfree(nctx->svcctx);
    1570           0 : }
    1571             : 
    1572             : static errno_t
    1573           0 : setservent_recv(struct tevent_req *req)
    1574             : {
    1575           0 :     TEVENT_REQ_RETURN_ON_ERROR(req);
    1576           0 :     return EOK;
    1577             : }
    1578             : 
    1579             : static void
    1580             : nss_cmd_setservent_done(struct tevent_req *req);
    1581             : 
    1582             : int
    1583           0 : nss_cmd_setservent(struct cli_ctx *cctx)
    1584             : {
    1585             :     struct nss_cmd_ctx *cmdctx;
    1586             :     struct tevent_req *req;
    1587           0 :     errno_t ret = EOK;
    1588             : 
    1589           0 :     cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
    1590           0 :     if (!cmdctx) {
    1591           0 :         return ENOMEM;
    1592             :     }
    1593           0 :     cmdctx->cctx = cctx;
    1594             : 
    1595           0 :     req = setservent_send(cmdctx, cctx);
    1596           0 :     if (!req) {
    1597           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1598             :               "Fatal error calling nss_cmd_setservent_send\n");
    1599           0 :         ret = EIO;
    1600           0 :         goto done;
    1601             :     }
    1602           0 :     tevent_req_set_callback(req, nss_cmd_setservent_done, cmdctx);
    1603             : 
    1604             : done:
    1605           0 :     return nss_cmd_done(cmdctx, ret);
    1606             : }
    1607             : 
    1608             : static void
    1609           0 : nss_cmd_setservent_done(struct tevent_req *req)
    1610             : {
    1611             :     errno_t ret;
    1612           0 :     struct nss_cmd_ctx *cmdctx =
    1613           0 :             tevent_req_callback_data(req, struct nss_cmd_ctx);
    1614             : 
    1615           0 :     ret = setservent_recv(req);
    1616           0 :     talloc_zfree(req);
    1617           0 :     if (ret == EOK || ret == ENOENT) {
    1618             :         /* Either we succeeded or no domains
    1619             :          * were eligible.
    1620             :          * Return an acknowledgment
    1621             :          */
    1622           0 :         ret = sss_packet_new(cmdctx->cctx->creq, 0,
    1623           0 :                              sss_packet_get_cmd(cmdctx->cctx->creq->in),
    1624           0 :                              &cmdctx->cctx->creq->out);
    1625           0 :         if (ret == EOK) {
    1626           0 :             sss_cmd_done(cmdctx->cctx, cmdctx);
    1627           0 :             return;
    1628             :         }
    1629             :     }
    1630             : 
    1631             :     /* Something bad happened.
    1632             :      * Return an error
    1633             :      */
    1634           0 :     nss_cmd_done(cmdctx, ret);
    1635             : }
    1636             : 
    1637             : static void
    1638             : nss_cmd_implicit_setservent_done(struct tevent_req *req);
    1639             : 
    1640             : static errno_t
    1641             : nss_cmd_getservent_immediate(struct nss_cmd_ctx *cmdctx);
    1642             : 
    1643             : static errno_t
    1644             : retservent(struct cli_ctx *cctx, int num);
    1645             : 
    1646           0 : int nss_cmd_getservent(struct cli_ctx *cctx)
    1647             : {
    1648             :     struct nss_ctx *nctx;
    1649             :     struct nss_cmd_ctx *cmdctx;
    1650             :     struct tevent_req *req;
    1651             : 
    1652           0 :     DEBUG(SSSDBG_TRACE_FUNC,
    1653             :           "Requesting info for all services\n");
    1654             : 
    1655           0 :     cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
    1656           0 :     if (!cmdctx) {
    1657           0 :         return ENOMEM;
    1658             :     }
    1659           0 :     cmdctx->cctx = cctx;
    1660             : 
    1661             :     /* Save the current index and cursor locations
    1662             :      * If we end up calling setservent implicitly, because the response object
    1663             :      * expired and has to be recreated, we want to resume from the same
    1664             :      * location.
    1665             :      */
    1666           0 :     cmdctx->saved_dom_idx = cctx->svc_dom_idx;
    1667           0 :     cmdctx->saved_cur = cctx->svcent_cur;
    1668             : 
    1669           0 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    1670           0 :     if(!nctx->svcctx || !nctx->svcctx->ready) {
    1671             :         /* Make sure we invoke setservent if it hasn't been run or is still
    1672             :          * processing from another client
    1673             :          */
    1674           0 :         req = setservent_send(cmdctx, cctx);
    1675           0 :         if (!req) {
    1676           0 :             return EIO;
    1677             :         }
    1678           0 :         tevent_req_set_callback(req,
    1679             :                                 nss_cmd_implicit_setservent_done,
    1680             :                                 cmdctx);
    1681           0 :         return EOK;
    1682             :     }
    1683             : 
    1684           0 :     return nss_cmd_getservent_immediate(cmdctx);
    1685             : }
    1686             : 
    1687             : static void
    1688           0 : nss_cmd_implicit_setservent_done(struct tevent_req *req)
    1689             : {
    1690             :     errno_t ret;
    1691           0 :     struct nss_cmd_ctx *cmdctx =
    1692           0 :             tevent_req_callback_data(req, struct nss_cmd_ctx);
    1693             : 
    1694           0 :     ret = setservent_recv(req);
    1695           0 :     talloc_zfree(req);
    1696             : 
    1697             :     /* ENOENT is acceptable, as it just means that there were no entries
    1698             :      * to be returned. This will be handled gracefully in retservent
    1699             :      * later.
    1700             :      */
    1701           0 :     if (ret != EOK && ret != ENOENT) {
    1702           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1703             :               "Implicit setservent failed with unexpected error [%d][%s]\n",
    1704             :                ret, strerror(ret));
    1705           0 :         NSS_CMD_FATAL_ERROR(cmdctx);
    1706             :     }
    1707             : 
    1708             :     /* Restore the saved index and cursor locations */
    1709           0 :     cmdctx->cctx->svc_dom_idx = cmdctx->saved_dom_idx;
    1710           0 :     cmdctx->cctx->svcent_cur = cmdctx->saved_cur;
    1711             : 
    1712           0 :     ret = nss_cmd_getservent_immediate(cmdctx);
    1713           0 :     if (ret != EOK) {
    1714           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
    1715             :               "Immediate retrieval failed with unexpected error "
    1716             :                "[%d][%s]\n", ret, strerror(ret));
    1717           0 :         NSS_CMD_FATAL_ERROR(cmdctx);
    1718             :     }
    1719             : }
    1720             : 
    1721             : static errno_t
    1722           0 : nss_cmd_getservent_immediate(struct nss_cmd_ctx *cmdctx)
    1723             : {
    1724           0 :     struct cli_ctx *cctx = cmdctx->cctx;
    1725             :     uint8_t *body;
    1726             :     size_t blen;
    1727             :     uint32_t num;
    1728             :     int ret;
    1729             : 
    1730             :     /* get max num of entries to return in one call */
    1731           0 :     sss_packet_get_body(cctx->creq->in, &body, &blen);
    1732           0 :     if (blen != sizeof(uint32_t)) {
    1733           0 :         return EINVAL;
    1734             :     }
    1735           0 :     SAFEALIGN_COPY_UINT32(&num, body, NULL);
    1736             : 
    1737             :     /* create response packet */
    1738           0 :     ret = sss_packet_new(cctx->creq, 0,
    1739           0 :                          sss_packet_get_cmd(cctx->creq->in),
    1740           0 :                          &cctx->creq->out);
    1741           0 :     if (ret != EOK) {
    1742           0 :         return ret;
    1743             :     }
    1744             : 
    1745           0 :     ret = retservent(cctx, num);
    1746             : 
    1747           0 :     sss_packet_set_error(cctx->creq->out, ret);
    1748           0 :     sss_cmd_done(cctx, cmdctx);
    1749             : 
    1750           0 :     return EOK;
    1751             : }
    1752             : 
    1753             : static errno_t
    1754           0 : retservent(struct cli_ctx *cctx, int num)
    1755             : {
    1756             :     struct nss_ctx *nctx;
    1757             :     struct getent_ctx *svcctx;
    1758           0 :     struct ldb_message **msgs = NULL;
    1759           0 :     struct dom_ctx *pdom = NULL;
    1760           0 :     unsigned int n = 0;
    1761           0 :     int ret = ENOENT;
    1762             : 
    1763           0 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    1764           0 :     if (!nctx->svcctx) goto none;
    1765             : 
    1766           0 :     svcctx = nctx->svcctx;
    1767             : 
    1768           0 :     while (ret == ENOENT) {
    1769           0 :         if (cctx->svc_dom_idx >= svcctx->num) break;
    1770             : 
    1771           0 :         pdom = &svcctx->doms[cctx->svc_dom_idx];
    1772             : 
    1773           0 :         n = pdom->res->count - cctx->svcent_cur;
    1774           0 :         if (n <= 0 && (cctx->svc_dom_idx+1 < svcctx->num)) {
    1775           0 :             cctx->svc_dom_idx++;
    1776           0 :             pdom = &svcctx->doms[cctx->svc_dom_idx];
    1777           0 :             n = pdom->res->count;
    1778           0 :             cctx->svcent_cur = 0;
    1779             :         }
    1780             : 
    1781           0 :         if (!n) break;
    1782             : 
    1783           0 :         if (n > num) n = num;
    1784             : 
    1785           0 :         msgs = &(pdom->res->msgs[cctx->svcent_cur]);
    1786             : 
    1787           0 :         ret = fill_service(cctx->creq->out,
    1788             :                            pdom->domain,
    1789             :                            NULL, msgs,
    1790             :                            &n);
    1791             : 
    1792           0 :         cctx->svcent_cur += n;
    1793             :     }
    1794             : 
    1795             : none:
    1796           0 :     if (ret == ENOENT) {
    1797           0 :         ret = sss_cmd_empty_packet(cctx->creq->out);
    1798             :     }
    1799           0 :     return ret;
    1800             : }
    1801             : 
    1802           0 : int nss_cmd_endservent(struct cli_ctx *cctx)
    1803             : {
    1804             :     struct nss_ctx *nctx;
    1805             :     int ret;
    1806             : 
    1807           0 :     DEBUG(SSSDBG_TRACE_FUNC,
    1808             :           "Terminating request info for all accounts\n");
    1809             : 
    1810           0 :     nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
    1811             : 
    1812             :     /* create response packet */
    1813           0 :     ret = sss_packet_new(cctx->creq, 0,
    1814           0 :                          sss_packet_get_cmd(cctx->creq->in),
    1815           0 :                          &cctx->creq->out);
    1816             : 
    1817           0 :     if (ret != EOK) {
    1818           0 :         return ret;
    1819             :     }
    1820           0 :     if (nctx->svcctx == NULL) goto done;
    1821             : 
    1822             :     /* Reset the indices so that subsequent requests start at zero */
    1823           0 :     cctx->svc_dom_idx = 0;
    1824           0 :     cctx->svcent_cur = 0;
    1825             : 
    1826             : done:
    1827           0 :     sss_cmd_done(cctx, NULL);
    1828           0 :     return EOK;
    1829             : }

Generated by: LCOV version 1.10