1  /* Copyright (C) 2000-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <errno.h>
      19  #include <netdb.h>
      20  #include "nsswitch.h"
      21  #include <resolv/resolv_context.h>
      22  
      23  /* Set up NIP to run through the services.  If ALL is zero, use NIP's
      24     current location if it's not nil.  Return nonzero if there are no
      25     services (left).  */
      26  static int
      27  setup (const char *func_name, db_lookup_function lookup_fct,
      28         void **fctp, nss_action_list *nip, nss_action_list *startp, int all)
      29  {
      30    int no_more;
      31    if (*startp == NULL || all)
      32      {
      33        no_more = lookup_fct (nip, func_name, NULL, fctp);
      34        *startp = no_more ? (nss_action_list) -1l : *nip;
      35      }
      36    else if (*startp == (nss_action_list) -1l)
      37      /* No services at all.  */
      38      return 1;
      39    else
      40      {
      41        if (!*nip)
      42  	/* Reset to the beginning of the service list.  */
      43  	*nip = *startp;
      44        /* Look up the first function.  */
      45        no_more = __nss_lookup (nip, func_name, NULL, fctp);
      46      }
      47    return no_more;
      48  }
      49  
      50  void
      51  __nss_setent (const char *func_name, db_lookup_function lookup_fct,
      52  	      nss_action_list *nip, nss_action_list *startp,
      53  	      nss_action_list *last_nip, int stayopen, int *stayopen_tmp,
      54  	      int res)
      55  {
      56    union
      57    {
      58      setent_function f;
      59      void *ptr;
      60    } fct;
      61    int no_more;
      62  
      63    struct resolv_context *res_ctx = NULL;
      64    if (res)
      65      {
      66        res_ctx = __resolv_context_get ();
      67        if (res_ctx == NULL)
      68  	{
      69  	  __set_h_errno (NETDB_INTERNAL);
      70  	  return;
      71  	}
      72      }
      73  
      74    /* Cycle through the services and run their `setXXent' functions until
      75       we find an available service.  */
      76    no_more = setup (func_name, lookup_fct, &fct.ptr, nip,
      77  		   startp, 1);
      78    while (! no_more)
      79      {
      80        int is_last_nip = *nip == *last_nip;
      81        enum nss_status status;
      82  
      83        if (stayopen_tmp)
      84  	status = DL_CALL_FCT (fct.f, (*stayopen_tmp));
      85        else
      86  	status = DL_CALL_FCT (fct.f, (0));
      87  
      88  
      89        /* This is a special-case.  When [SUCCESS=merge] is in play,
      90           _nss_next2() will skip to the next database.  Due to the
      91           implementation of that function, we can't know whether we're
      92           in an enumeration or an individual lookup, which behaves
      93           differently with regards to merging.  We'll treat SUCCESS as
      94           an indication to start the enumeration at this database. */
      95        if (nss_next_action (*nip, status) == NSS_ACTION_MERGE)
      96  	no_more = 1;
      97        else
      98  	no_more = __nss_next2 (nip, func_name, NULL, &fct.ptr, status, 0);
      99  
     100        if (is_last_nip)
     101  	*last_nip = *nip;
     102      }
     103  
     104    __resolv_context_put (res_ctx);
     105  
     106    if (stayopen_tmp)
     107      *stayopen_tmp = stayopen;
     108  }
     109  
     110  
     111  void
     112  __nss_endent (const char *func_name, db_lookup_function lookup_fct,
     113  	      nss_action_list *nip, nss_action_list *startp,
     114  	      nss_action_list *last_nip, int res)
     115  {
     116    union
     117    {
     118      endent_function f;
     119      void *ptr;
     120    } fct;
     121    int no_more;
     122  
     123    struct resolv_context *res_ctx = NULL;
     124    if (res)
     125      {
     126        res_ctx = __resolv_context_get ();
     127        if (res_ctx == NULL)
     128  	{
     129  	  __set_h_errno (NETDB_INTERNAL);
     130  	  return;
     131  	}
     132      }
     133  
     134    /* Cycle through all the services and run their endXXent functions.  */
     135    no_more = setup (func_name, lookup_fct, &fct.ptr, nip, startp, 1);
     136    while (! no_more)
     137      {
     138        /* Ignore status, we force check in __NSS_NEXT.  */
     139        DL_CALL_FCT (fct.f, ());
     140  
     141        if (*nip == *last_nip)
     142  	/* We have processed all services which were used.  */
     143  	break;
     144  
     145        no_more = __nss_next2 (nip, func_name, NULL, &fct.ptr, 0, 1);
     146      }
     147    *last_nip = *nip = NULL;
     148  
     149    __resolv_context_put (res_ctx);
     150  }
     151  
     152  
     153  int
     154  __nss_getent_r (const char *getent_func_name,
     155  		const char *setent_func_name,
     156  		db_lookup_function lookup_fct,
     157  		nss_action_list *nip, nss_action_list *startp,
     158  		nss_action_list *last_nip, int *stayopen_tmp, int res,
     159  		void *resbuf, char *buffer, size_t buflen,
     160  		void **result, int *h_errnop)
     161  {
     162    union
     163    {
     164      getent_function f;
     165      void *ptr;
     166    } fct;
     167    int no_more;
     168    enum nss_status status;
     169  
     170    struct resolv_context *res_ctx = NULL;
     171    if (res)
     172      {
     173        res_ctx = __resolv_context_get ();
     174        if (res_ctx == NULL)
     175  	{
     176  	  *h_errnop = NETDB_INTERNAL;
     177  	  *result = NULL;
     178  	  return errno;
     179  	}
     180      }
     181  
     182    /* Initialize status to return if no more functions are found.  */
     183    status = NSS_STATUS_NOTFOUND;
     184  
     185    /* Run through available functions, starting with the same function last
     186       run.  We will repeat each function as long as it succeeds, and then go
     187       on to the next service action.  */
     188    no_more = setup (getent_func_name, lookup_fct, &fct.ptr, nip,
     189  		   startp, 0);
     190    while (! no_more)
     191      {
     192        int is_last_nip = *nip == *last_nip;
     193  
     194        status = DL_CALL_FCT (fct.f,
     195  			    (resbuf, buffer, buflen, &errno, &h_errno));
     196  
     197        /* The status is NSS_STATUS_TRYAGAIN and errno is ERANGE the
     198  	 provided buffer is too small.  In this case we should give
     199  	 the user the possibility to enlarge the buffer and we should
     200  	 not simply go on with the next service (even if the TRYAGAIN
     201  	 action tells us so).  */
     202        if (status == NSS_STATUS_TRYAGAIN
     203  	  && (h_errnop == NULL || *h_errnop == NETDB_INTERNAL)
     204  	  && errno == ERANGE)
     205  	break;
     206  
     207        do
     208  	{
     209          /* This is a special-case.  When [SUCCESS=merge] is in play,
     210             _nss_next2() will skip to the next database.  Due to the
     211             implementation of that function, we can't know whether we're
     212             in an enumeration or an individual lookup, which behaves
     213             differently with regards to merging.  We'll treat SUCCESS as
     214             an indication to return the results here. */
     215  	  if (status == NSS_STATUS_SUCCESS
     216  	      && nss_next_action (*nip, status) == NSS_ACTION_MERGE)
     217  	    no_more = 1;
     218  	  else
     219  	    no_more = __nss_next2 (nip, getent_func_name, NULL, &fct.ptr,
     220  				   status, 0);
     221  
     222  	  if (is_last_nip)
     223  	    *last_nip = *nip;
     224  
     225  	  if (! no_more)
     226  	    {
     227  	      /* Call the `setXXent' function.  This wasn't done before.  */
     228  	      union
     229  	      {
     230  		setent_function f;
     231  		void *ptr;
     232  	      } sfct;
     233  
     234  	      no_more = __nss_lookup (nip, setent_func_name, NULL, &sfct.ptr);
     235  
     236  	      if (! no_more)
     237  	        {
     238  		  if (stayopen_tmp)
     239  		    status = DL_CALL_FCT (sfct.f, (*stayopen_tmp));
     240  		  else
     241  		    status = DL_CALL_FCT (sfct.f, (0));
     242  		}
     243  	      else
     244  		status = NSS_STATUS_NOTFOUND;
     245  	    }
     246  	}
     247        while (! no_more && status != NSS_STATUS_SUCCESS);
     248      }
     249  
     250    __resolv_context_put (res_ctx);
     251  
     252    *result = status == NSS_STATUS_SUCCESS ? resbuf : NULL;
     253    return (status == NSS_STATUS_SUCCESS ? 0
     254  	  : status != NSS_STATUS_TRYAGAIN ? ENOENT
     255  	  /* h_errno functions only set errno if h_errno is NETDB_INTERNAL.  */
     256  	  : (h_errnop == NULL || *h_errnop == NETDB_INTERNAL) ? errno
     257  	  : EAGAIN);
     258  }