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

Generated by: LCOV version 1.10