1/* Copyright (C) 1996-2024 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
132static 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
145static 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 if (err) \
161 { \
162 __set_errno (err); \
163 if (err == ERANGE) \
164 status = NSS_STATUS_TRYAGAIN; \
165 else \
166 status = NSS_STATUS_UNAVAIL; \
167 break; \
168 } \
169 })
170
171/* Type of the lookup function we need here. */
172typedef enum nss_status (*lookup_function) (ADD_PARAMS, LOOKUP_TYPE *, char *,
173 size_t, int * H_ERRNO_PARM
174 EXTRA_PARAMS);
175
176/* The lookup function for the first entry of this service. */
177extern int DB_LOOKUP_FCT (nss_action_list *nip, const char *name,
178 const char *name2, void **fctp);
179libc_hidden_proto (DB_LOOKUP_FCT)
180
181
182int
183INTERNAL (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer,
184 size_t buflen, LOOKUP_TYPE **result H_ERRNO_PARM
185 EXTRA_PARAMS)
186{
187 nss_action_list nip;
188 int do_merge = 0;
189 LOOKUP_TYPE mergegrp;
190 char *mergebuf = NULL;
191 char *endptr = NULL;
192 union
193 {
194 lookup_function l;
195 void *ptr;
196 } fct;
197 int no_more, err;
198 enum nss_status status = NSS_STATUS_UNAVAIL;
199#ifdef USE_NSCD
200 int nscd_status;
201#endif
202#ifdef NEED_H_ERRNO
203 bool any_service = false;
204#endif
205
206#ifdef NEED__RES
207 /* The HANDLE_DIGITS_DOTS case below already needs the resolver
208 configuration, so this has to happen early. */
209 struct resolv_context *res_ctx = __resolv_context_get ();
210 if (res_ctx == NULL)
211 {
212 *h_errnop = NETDB_INTERNAL;
213 *result = NULL;
214 return errno;
215 }
216#endif /* NEED__RES */
217
218#ifdef PREPROCESS
219 PREPROCESS;
220#endif
221
222
223#ifdef HANDLE_DIGITS_DOTS
224 switch (__nss_hostname_digits_dots (name, resbuf, &buffer, NULL,
225 buflen, result, &status, AF_VAL,
226 H_ERRNO_VAR_P))
227 {
228 case -1:
229# ifdef NEED__RES
230 __resolv_context_put (res_ctx);
231# endif
232 return errno;
233 case 1:
234#ifdef NEED_H_ERRNO
235 any_service = true;
236#endif
237 goto done;
238 }
239#endif
240
241#ifdef USE_NSCD
242 if (NOT_USENSCD_NAME > 0 && ++NOT_USENSCD_NAME > NSS_NSCD_RETRY)
243 NOT_USENSCD_NAME = 0;
244
245 if (!NOT_USENSCD_NAME
246 && !__nss_database_custom[CONCAT2 (NSS_DBSIDX_, DATABASE_NAME)])
247 {
248 nscd_status = NSCD_NAME (ADD_VARIABLES, resbuf, buffer, buflen, result
249 H_ERRNO_VAR);
250 if (nscd_status >= 0)
251 {
252# ifdef NEED__RES
253 __resolv_context_put (res_ctx);
254# endif
255 return nscd_status;
256 }
257 }
258#endif
259
260 no_more = DB_LOOKUP_FCT (nip: &nip, REENTRANT_NAME_STRING,
261 REENTRANT2_NAME_STRING, fctp: &fct.ptr);
262
263 while (no_more == 0)
264 {
265#ifdef NEED_H_ERRNO
266 any_service = true;
267#endif
268
269 status = DL_CALL_FCT (fct.l, (ADD_VARIABLES, resbuf, buffer, buflen,
270 &errno H_ERRNO_VAR EXTRA_VARIABLES));
271
272 /* The status is NSS_STATUS_TRYAGAIN and errno is ERANGE the
273 provided buffer is too small. In this case we should give
274 the user the possibility to enlarge the buffer and we should
275 not simply go on with the next service (even if the TRYAGAIN
276 action tells us so). */
277 if (status == NSS_STATUS_TRYAGAIN
278#ifdef NEED_H_ERRNO
279 && *h_errnop == NETDB_INTERNAL
280#endif
281 && errno == ERANGE)
282 break;
283
284 if (do_merge)
285 {
286
287 if (status == NSS_STATUS_SUCCESS)
288 {
289 /* The previous loop saved a buffer for merging.
290 Perform the merge now. */
291 err = MERGE_FN (savedgrp: &mergegrp, savedbuf: mergebuf, savedend: endptr, buflen, mergegrp: resbuf,
292 mergebuf: buffer);
293 CHECK_MERGE (err,status);
294 do_merge = 0;
295 }
296 else
297 {
298 /* If the result wasn't SUCCESS, copy the saved buffer back
299 into the result buffer and set the status back to
300 NSS_STATUS_SUCCESS to match the previous pass through the
301 loop.
302 * If the next action is CONTINUE, it will overwrite the value
303 currently in the buffer and return the new value.
304 * If the next action is RETURN, we'll return the previously-
305 acquired values.
306 * If the next action is MERGE, then it will be added to the
307 buffer saved from the previous source. */
308 err = DEEPCOPY_FN (srcgrp: mergegrp, buflen, destgrp: resbuf, destbuf: buffer, NULL);
309 CHECK_MERGE (err, status);
310 status = NSS_STATUS_SUCCESS;
311 }
312 }
313
314 /* If we were are configured to merge this value with the next one,
315 save the current value of the group struct. */
316 if (nss_next_action (nip, status) == NSS_ACTION_MERGE
317 && status == NSS_STATUS_SUCCESS)
318 {
319 /* Copy the current values into a buffer to be merged with the next
320 set of retrieved values. */
321 if (mergebuf == NULL)
322 {
323 /* Only allocate once and reuse it for as many merges as we need
324 to perform. */
325 mergebuf = malloc (size: buflen);
326 if (mergebuf == NULL)
327 {
328 __set_errno (ENOMEM);
329 status = NSS_STATUS_UNAVAIL;
330 break;
331 }
332 }
333
334 err = DEEPCOPY_FN (srcgrp: *resbuf, buflen, destgrp: &mergegrp, destbuf: mergebuf, endptr: &endptr);
335 CHECK_MERGE (err, status);
336 do_merge = 1;
337 }
338
339 no_more = __nss_next2 (ni: &nip, REENTRANT_NAME_STRING,
340 REENTRANT2_NAME_STRING, fctp: &fct.ptr, status, all_values: 0);
341 }
342 free (ptr: mergebuf);
343 mergebuf = NULL;
344
345#ifdef HANDLE_DIGITS_DOTS
346done:
347#endif
348 *result = status == NSS_STATUS_SUCCESS ? resbuf : NULL;
349#ifdef NEED_H_ERRNO
350 if (status == NSS_STATUS_UNAVAIL && !any_service && errno != ENOENT)
351 /* This happens when we weren't able to use a service for reasons other
352 than the module not being found. In such a case, we'd want to tell the
353 caller that errno has the real reason for failure. */
354 *h_errnop = NETDB_INTERNAL;
355 else if (status != NSS_STATUS_SUCCESS && !any_service)
356 /* We were not able to use any service. */
357 *h_errnop = NO_RECOVERY;
358#endif
359#ifdef POSTPROCESS
360 POSTPROCESS;
361#endif
362
363#ifdef NEED__RES
364 /* This has to happen late because the POSTPROCESS stage above might
365 need the resolver context. */
366 __resolv_context_put (res_ctx);
367#endif /* NEED__RES */
368
369 int res;
370 if (status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND)
371 res = 0;
372 /* Don't pass back ERANGE if this is not for a too-small buffer. */
373 else if (errno == ERANGE && status != NSS_STATUS_TRYAGAIN)
374 res = EINVAL;
375#ifdef NEED_H_ERRNO
376 /* These functions only set errno if h_errno is NETDB_INTERNAL. */
377 else if (status == NSS_STATUS_TRYAGAIN && *h_errnop != NETDB_INTERNAL)
378 res = EAGAIN;
379#endif
380 else
381 return errno;
382
383 __set_errno (res);
384 return res;
385}
386
387
388#ifdef NO_COMPAT_NEEDED
389strong_alias (INTERNAL (REENTRANT_NAME), REENTRANT_NAME);
390#elif !defined FUNCTION2_NAME
391# include <shlib-compat.h>
392# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1_2)
393# define OLD(name) OLD1 (name)
394# define OLD1(name) __old_##name
395
396int
397attribute_compat_text_section
398OLD (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer,
399 size_t buflen, LOOKUP_TYPE **result H_ERRNO_PARM)
400{
401 int ret = INTERNAL (REENTRANT_NAME) (ADD_VARIABLES, resbuf, buffer,
402 buflen, result H_ERRNO_VAR);
403
404 if (ret != 0 || result == NULL)
405 ret = -1;
406
407 return ret;
408}
409
410# define do_symbol_version(real, name, version) \
411 compat_symbol (libc, real, name, version)
412do_symbol_version (OLD (REENTRANT_NAME), REENTRANT_NAME, GLIBC_2_0);
413# endif
414
415/* As INTERNAL (REENTRANT_NAME) may be hidden, we need an alias
416 in between so that the REENTRANT_NAME@@GLIBC_2.1.2 is not
417 hidden too. */
418strong_alias (INTERNAL (REENTRANT_NAME), NEW (REENTRANT_NAME));
419
420# define do_default_symbol_version(real, name, version) \
421 versioned_symbol (libc, real, name, version)
422do_default_symbol_version (NEW (REENTRANT_NAME),
423 REENTRANT_NAME, GLIBC_2_1_2);
424#endif
425
426nss_interface_function (REENTRANT_NAME)
427

source code of glibc/nss/getXXbyYY_r.c