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