(root)/
glibc-2.38/
nss/
getXXbyYY_r.c
       1  /* Copyright (C) 1996-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 <assert.h>
      19  #include <atomic.h>
      20  #include <errno.h>
      21  #include <stdbool.h>
      22  #include "nsswitch.h"
      23  #include "sysdep.h"
      24  #ifdef USE_NSCD
      25  # include <nscd/nscd_proto.h>
      26  #endif
      27  #ifdef NEED__RES
      28  # include <resolv/resolv_context.h>
      29  #endif
      30  /*******************************************************************\
      31  |* Here we assume several symbols to be defined:		   *|
      32  |*								   *|
      33  |* LOOKUP_TYPE   - the return type of the function		   *|
      34  |*								   *|
      35  |* FUNCTION_NAME - name of the non-reentrant function		   *|
      36  |*								   *|
      37  |* DATABASE_NAME - name of the database the function accesses	   *|
      38  |*		   (e.g., host, services, ...)			   *|
      39  |*								   *|
      40  |* ADD_PARAMS    - additional parameters, can vary in number	   *|
      41  |*								   *|
      42  |* ADD_VARIABLES - names of additional parameters		   *|
      43  |*								   *|
      44  |* Optionally the following vars can be defined:		   *|
      45  |*								   *|
      46  |* EXTRA_PARAMS  - optional parameters, can vary in number	   *|
      47  |*								   *|
      48  |* EXTRA_VARIABLES - names of optional parameter		   *|
      49  |*								   *|
      50  |* FUNCTION2_NAME - alternative name of the non-reentrant function *|
      51  |*								   *|
      52  |* NEED_H_ERRNO  - an extra parameter will be passed to point to   *|
      53  |*		   the global `h_errno' variable.		   *|
      54  |*								   *|
      55  |* NEED__RES     - obtain a struct resolv_context resolver context *|
      56  |*								   *|
      57  |* PREPROCESS    - code run before anything else		   *|
      58  |*								   *|
      59  |* POSTPROCESS   - code run after the lookup			   *|
      60  |*								   *|
      61  \*******************************************************************/
      62  
      63  /* To make the real sources a bit prettier.  */
      64  #define REENTRANT_NAME APPEND_R (FUNCTION_NAME)
      65  #ifdef FUNCTION2_NAME
      66  # define REENTRANT2_NAME APPEND_R (FUNCTION2_NAME)
      67  #else
      68  # define REENTRANT2_NAME NULL
      69  #endif
      70  #define APPEND_R(name) APPEND_R1 (name)
      71  #define APPEND_R1(name) name##_r
      72  #define INTERNAL(name) INTERNAL1 (name)
      73  #define INTERNAL1(name) __##name
      74  #define NEW(name) NEW1 (name)
      75  #define NEW1(name) __new_##name
      76  
      77  #ifdef USE_NSCD
      78  # define NSCD_NAME ADD_NSCD (REENTRANT_NAME)
      79  # define ADD_NSCD(name) ADD_NSCD1 (name)
      80  # define ADD_NSCD1(name) __nscd_##name
      81  # define NOT_USENSCD_NAME ADD_NOT_NSCDUSE (DATABASE_NAME)
      82  # define ADD_NOT_NSCDUSE(name) ADD_NOT_NSCDUSE1 (name)
      83  # define ADD_NOT_NSCDUSE1(name) __nss_not_use_nscd_##name
      84  # define CONCAT2(arg1, arg2) CONCAT2_2 (arg1, arg2)
      85  # define CONCAT2_2(arg1, arg2) arg1##arg2
      86  #endif
      87  
      88  #define FUNCTION_NAME_STRING STRINGIZE (FUNCTION_NAME)
      89  #define REENTRANT_NAME_STRING STRINGIZE (REENTRANT_NAME)
      90  #ifdef FUNCTION2_NAME
      91  # define REENTRANT2_NAME_STRING STRINGIZE (REENTRANT2_NAME)
      92  #else
      93  # define REENTRANT2_NAME_STRING NULL
      94  #endif
      95  #define DATABASE_NAME_STRING STRINGIZE (DATABASE_NAME)
      96  #define STRINGIZE(name) STRINGIZE1 (name)
      97  #define STRINGIZE1(name) #name
      98  
      99  #ifndef DB_LOOKUP_FCT
     100  # define DB_LOOKUP_FCT CONCAT3_1 (__nss_, DATABASE_NAME, _lookup2)
     101  # define CONCAT3_1(Pre, Name, Post) CONCAT3_2 (Pre, Name, Post)
     102  # define CONCAT3_2(Pre, Name, Post) Pre##Name##Post
     103  #endif
     104  
     105  /* Sometimes we need to store error codes in the `h_errno' variable.  */
     106  #ifdef NEED_H_ERRNO
     107  # define H_ERRNO_PARM , int *h_errnop
     108  # define H_ERRNO_VAR , h_errnop
     109  # define H_ERRNO_VAR_P h_errnop
     110  #else
     111  # define H_ERRNO_PARM
     112  # define H_ERRNO_VAR
     113  # define H_ERRNO_VAR_P NULL
     114  #endif
     115  
     116  #ifndef EXTRA_PARAMS
     117  # define EXTRA_PARAMS
     118  #endif
     119  #ifndef EXTRA_VARIABLES
     120  # define EXTRA_VARIABLES
     121  #endif
     122  
     123  #ifdef HAVE_AF
     124  # define AF_VAL af
     125  #else
     126  # define AF_VAL AF_INET
     127  #endif
     128  
     129  
     130  /* Set defaults for merge functions that haven't been defined.  */
     131  #ifndef DEEPCOPY_FN
     132  static inline int
     133  __copy_einval (LOOKUP_TYPE a,
     134  	       const size_t b,
     135  	       LOOKUP_TYPE *c,
     136  	       char *d,
     137  	       char **e)
     138  {
     139    return EINVAL;
     140  }
     141  # define DEEPCOPY_FN __copy_einval
     142  #endif
     143  
     144  #ifndef MERGE_FN
     145  static inline int
     146  __merge_einval (LOOKUP_TYPE *a,
     147  		char *b,
     148  		char *c,
     149  		size_t d,
     150  		LOOKUP_TYPE *e,
     151  		char *f)
     152  {
     153    return EINVAL;
     154  }
     155  # define MERGE_FN __merge_einval
     156  #endif
     157  
     158  #define CHECK_MERGE(err, status)		\
     159    ({						\
     160      do						\
     161        {						\
     162  	if (err)				\
     163  	  {					\
     164  	    __set_errno (err);			\
     165  	    if (err == ERANGE)			\
     166  	      status = NSS_STATUS_TRYAGAIN;	\
     167  	    else				\
     168  	      status = NSS_STATUS_UNAVAIL;	\
     169  	    break;				\
     170  	  }					\
     171        }						\
     172      while (0);					\
     173    })
     174  
     175  /* Type of the lookup function we need here.  */
     176  typedef enum nss_status (*lookup_function) (ADD_PARAMS, LOOKUP_TYPE *, char *,
     177  					    size_t, int * H_ERRNO_PARM
     178  					    EXTRA_PARAMS);
     179  
     180  /* The lookup function for the first entry of this service.  */
     181  extern int DB_LOOKUP_FCT (nss_action_list *nip, const char *name,
     182  			  const char *name2, void **fctp);
     183  libc_hidden_proto (DB_LOOKUP_FCT)
     184  
     185  
     186  int
     187  INTERNAL (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer,
     188  			   size_t buflen, LOOKUP_TYPE **result H_ERRNO_PARM
     189  			   EXTRA_PARAMS)
     190  {
     191    nss_action_list nip;
     192    int do_merge = 0;
     193    LOOKUP_TYPE mergegrp;
     194    char *mergebuf = NULL;
     195    char *endptr = NULL;
     196    union
     197    {
     198      lookup_function l;
     199      void *ptr;
     200    } fct;
     201    int no_more, err;
     202    enum nss_status status = NSS_STATUS_UNAVAIL;
     203  #ifdef USE_NSCD
     204    int nscd_status;
     205  #endif
     206  #ifdef NEED_H_ERRNO
     207    bool any_service = false;
     208  #endif
     209  
     210  #ifdef NEED__RES
     211    /* The HANDLE_DIGITS_DOTS case below already needs the resolver
     212       configuration, so this has to happen early.  */
     213    struct resolv_context *res_ctx = __resolv_context_get ();
     214    if (res_ctx == NULL)
     215      {
     216        *h_errnop = NETDB_INTERNAL;
     217        *result = NULL;
     218        return errno;
     219      }
     220  #endif /* NEED__RES */
     221  
     222  #ifdef PREPROCESS
     223    PREPROCESS;
     224  #endif
     225  
     226  
     227  #ifdef HANDLE_DIGITS_DOTS
     228    switch (__nss_hostname_digits_dots (name, resbuf, &buffer, NULL,
     229  				      buflen, result, &status, AF_VAL,
     230  				      H_ERRNO_VAR_P))
     231      {
     232      case -1:
     233  # ifdef NEED__RES
     234        __resolv_context_put (res_ctx);
     235  # endif
     236        return errno;
     237      case 1:
     238  #ifdef NEED_H_ERRNO
     239        any_service = true;
     240  #endif
     241        goto done;
     242      }
     243  #endif
     244  
     245  #ifdef USE_NSCD
     246    if (NOT_USENSCD_NAME > 0 && ++NOT_USENSCD_NAME > NSS_NSCD_RETRY)
     247      NOT_USENSCD_NAME = 0;
     248  
     249    if (!NOT_USENSCD_NAME
     250        && !__nss_database_custom[CONCAT2 (NSS_DBSIDX_, DATABASE_NAME)])
     251      {
     252        nscd_status = NSCD_NAME (ADD_VARIABLES, resbuf, buffer, buflen, result
     253  			       H_ERRNO_VAR);
     254        if (nscd_status >= 0)
     255  	{
     256  # ifdef NEED__RES
     257  	  __resolv_context_put (res_ctx);
     258  # endif
     259  	  return nscd_status;
     260  	}
     261      }
     262  #endif
     263  
     264    no_more = DB_LOOKUP_FCT (&nip, REENTRANT_NAME_STRING,
     265  			   REENTRANT2_NAME_STRING, &fct.ptr);
     266  
     267    while (no_more == 0)
     268      {
     269  #ifdef NEED_H_ERRNO
     270        any_service = true;
     271  #endif
     272  
     273        status = DL_CALL_FCT (fct.l, (ADD_VARIABLES, resbuf, buffer, buflen,
     274  				    &errno H_ERRNO_VAR EXTRA_VARIABLES));
     275  
     276        /* The status is NSS_STATUS_TRYAGAIN and errno is ERANGE the
     277  	 provided buffer is too small.  In this case we should give
     278  	 the user the possibility to enlarge the buffer and we should
     279  	 not simply go on with the next service (even if the TRYAGAIN
     280  	 action tells us so).  */
     281        if (status == NSS_STATUS_TRYAGAIN
     282  #ifdef NEED_H_ERRNO
     283  	  && *h_errnop == NETDB_INTERNAL
     284  #endif
     285  	  && errno == ERANGE)
     286  	break;
     287  
     288        if (do_merge)
     289  	{
     290  
     291  	  if (status == NSS_STATUS_SUCCESS)
     292  	    {
     293  	      /* The previous loop saved a buffer for merging.
     294  		 Perform the merge now.  */
     295  	      err = MERGE_FN (&mergegrp, mergebuf, endptr, buflen, resbuf,
     296  			      buffer);
     297  	      CHECK_MERGE (err,status);
     298  	      do_merge = 0;
     299  	    }
     300  	  else
     301  	    {
     302  	      /* If the result wasn't SUCCESS, copy the saved buffer back
     303  	         into the result buffer and set the status back to
     304  	         NSS_STATUS_SUCCESS to match the previous pass through the
     305  	         loop.
     306  	          * If the next action is CONTINUE, it will overwrite the value
     307  	            currently in the buffer and return the new value.
     308  	          * If the next action is RETURN, we'll return the previously-
     309  	            acquired values.
     310  	          * If the next action is MERGE, then it will be added to the
     311  	            buffer saved from the previous source.  */
     312  	      err = DEEPCOPY_FN (mergegrp, buflen, resbuf, buffer, NULL);
     313  	      CHECK_MERGE (err, status);
     314  	      status = NSS_STATUS_SUCCESS;
     315  	    }
     316  	}
     317  
     318        /* If we were are configured to merge this value with the next one,
     319           save the current value of the group struct.  */
     320        if (nss_next_action (nip, status) == NSS_ACTION_MERGE
     321  	  && status == NSS_STATUS_SUCCESS)
     322  	{
     323  	  /* Copy the current values into a buffer to be merged with the next
     324  	     set of retrieved values.  */
     325  	  if (mergebuf == NULL)
     326  	    {
     327  	      /* Only allocate once and reuse it for as many merges as we need
     328  	         to perform.  */
     329  	      mergebuf = malloc (buflen);
     330  	      if (mergebuf == NULL)
     331  		{
     332  		  __set_errno (ENOMEM);
     333  		  status = NSS_STATUS_UNAVAIL;
     334  		  break;
     335  		}
     336  	    }
     337  
     338  	  err = DEEPCOPY_FN (*resbuf, buflen, &mergegrp, mergebuf, &endptr);
     339  	  CHECK_MERGE (err, status);
     340  	  do_merge = 1;
     341  	}
     342  
     343        no_more = __nss_next2 (&nip, REENTRANT_NAME_STRING,
     344  			     REENTRANT2_NAME_STRING, &fct.ptr, status, 0);
     345      }
     346    free (mergebuf);
     347    mergebuf = NULL;
     348  
     349  #ifdef HANDLE_DIGITS_DOTS
     350  done:
     351  #endif
     352    *result = status == NSS_STATUS_SUCCESS ? resbuf : NULL;
     353  #ifdef NEED_H_ERRNO
     354    if (status == NSS_STATUS_UNAVAIL && !any_service && errno != ENOENT)
     355      /* This happens when we weren't able to use a service for reasons other
     356         than the module not being found.  In such a case, we'd want to tell the
     357         caller that errno has the real reason for failure.  */
     358      *h_errnop = NETDB_INTERNAL;
     359    else if (status != NSS_STATUS_SUCCESS && !any_service)
     360      /* We were not able to use any service.  */
     361      *h_errnop = NO_RECOVERY;
     362  #endif
     363  #ifdef POSTPROCESS
     364    POSTPROCESS;
     365  #endif
     366  
     367  #ifdef NEED__RES
     368    /* This has to happen late because the POSTPROCESS stage above might
     369       need the resolver context.  */
     370    __resolv_context_put (res_ctx);
     371  #endif /* NEED__RES */
     372  
     373    int res;
     374    if (status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND)
     375      res = 0;
     376    /* Don't pass back ERANGE if this is not for a too-small buffer.  */
     377    else if (errno == ERANGE && status != NSS_STATUS_TRYAGAIN)
     378      res = EINVAL;
     379  #ifdef NEED_H_ERRNO
     380    /* These functions only set errno if h_errno is NETDB_INTERNAL.  */
     381    else if (status == NSS_STATUS_TRYAGAIN && *h_errnop != NETDB_INTERNAL)
     382      res = EAGAIN;
     383  #endif
     384    else
     385      return errno;
     386  
     387    __set_errno (res);
     388    return res;
     389  }
     390  
     391  
     392  #ifdef NO_COMPAT_NEEDED
     393  strong_alias (INTERNAL (REENTRANT_NAME), REENTRANT_NAME);
     394  #elif !defined FUNCTION2_NAME
     395  # include <shlib-compat.h>
     396  # if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1_2)
     397  #  define OLD(name) OLD1 (name)
     398  #  define OLD1(name) __old_##name
     399  
     400  int
     401  attribute_compat_text_section
     402  OLD (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer,
     403  		      size_t buflen, LOOKUP_TYPE **result H_ERRNO_PARM)
     404  {
     405    int ret = INTERNAL (REENTRANT_NAME) (ADD_VARIABLES, resbuf, buffer,
     406  				       buflen, result H_ERRNO_VAR);
     407  
     408    if (ret != 0 || result == NULL)
     409      ret = -1;
     410  
     411    return ret;
     412  }
     413  
     414  #  define do_symbol_version(real, name, version) \
     415    compat_symbol (libc, real, name, version)
     416  do_symbol_version (OLD (REENTRANT_NAME), REENTRANT_NAME, GLIBC_2_0);
     417  # endif
     418  
     419  /* As INTERNAL (REENTRANT_NAME) may be hidden, we need an alias
     420     in between so that the REENTRANT_NAME@@GLIBC_2.1.2 is not
     421     hidden too.  */
     422  strong_alias (INTERNAL (REENTRANT_NAME), NEW (REENTRANT_NAME));
     423  
     424  # define do_default_symbol_version(real, name, version) \
     425    versioned_symbol (libc, real, name, version)
     426  do_default_symbol_version (NEW (REENTRANT_NAME),
     427  			   REENTRANT_NAME, GLIBC_2_1_2);
     428  #endif
     429  
     430  nss_interface_function (REENTRANT_NAME)