1 | /* Copyright (C) 1996-2017 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. |
3 | Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <http://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <nss.h> |
20 | #include <netdb.h> |
21 | #include <ctype.h> |
22 | #include <errno.h> |
23 | #include <string.h> |
24 | #include <libc-lock.h> |
25 | #include <rpcsvc/yp.h> |
26 | #include <rpcsvc/ypclnt.h> |
27 | |
28 | #include "nss-nis.h" |
29 | #include <libnsl.h> |
30 | |
31 | |
32 | /* Get the declaration of the parser function. */ |
33 | #define ENTNAME servent |
34 | #define EXTERN_PARSER |
35 | #include <nss/nss_files/files-parse.c> |
36 | |
37 | __libc_lock_define_initialized (static, lock) |
38 | |
39 | static intern_t intern; |
40 | |
41 | struct search_t |
42 | { |
43 | const char *name; |
44 | const char *proto; |
45 | int port; |
46 | enum nss_status status; |
47 | struct servent *serv; |
48 | char *buffer; |
49 | size_t buflen; |
50 | int *errnop; |
51 | }; |
52 | |
53 | static int |
54 | dosearch (int instatus, char *inkey, int inkeylen, char *inval, |
55 | int invallen, char *indata) |
56 | { |
57 | struct search_t *req = (struct search_t *) indata; |
58 | |
59 | if (__glibc_unlikely (instatus != YP_TRUE)) |
60 | return 1; |
61 | |
62 | if (inkey && inkeylen > 0 && inval && invallen > 0) |
63 | { |
64 | if (__glibc_unlikely ((size_t) (invallen + 1) > req->buflen)) |
65 | { |
66 | *req->errnop = ERANGE; |
67 | req->status = NSS_STATUS_TRYAGAIN; |
68 | return 1; |
69 | } |
70 | |
71 | char *p = strncpy (req->buffer, inval, invallen); |
72 | req->buffer[invallen] = '\0'; |
73 | while (isspace (*p)) |
74 | ++p; |
75 | |
76 | int parse_res = _nss_files_parse_servent (p, req->serv, |
77 | (void *) req->buffer, |
78 | req->buflen, req->errnop); |
79 | if (parse_res == -1) |
80 | { |
81 | req->status = NSS_STATUS_TRYAGAIN; |
82 | return 1; |
83 | } |
84 | |
85 | if (!parse_res) |
86 | return 0; |
87 | |
88 | if (req->proto != NULL && strcmp (req->serv->s_proto, req->proto) != 0) |
89 | return 0; |
90 | |
91 | if (req->port != -1 && req->serv->s_port != req->port) |
92 | return 0; |
93 | |
94 | if (req->name != NULL && strcmp (req->serv->s_name, req->name) != 0) |
95 | { |
96 | char **cp; |
97 | for (cp = req->serv->s_aliases; *cp; cp++) |
98 | if (strcmp (req->name, *cp) == 0) |
99 | break; |
100 | |
101 | if (*cp == NULL) |
102 | return 0; |
103 | } |
104 | |
105 | req->status = NSS_STATUS_SUCCESS; |
106 | return 1; |
107 | } |
108 | |
109 | return 0; |
110 | } |
111 | |
112 | static void |
113 | internal_nis_endservent (void) |
114 | { |
115 | struct response_t *curr = intern.next; |
116 | |
117 | while (curr != NULL) |
118 | { |
119 | struct response_t *last = curr; |
120 | curr = curr->next; |
121 | free (last); |
122 | } |
123 | |
124 | intern.next = intern.start = NULL; |
125 | } |
126 | |
127 | enum nss_status |
128 | _nss_nis_endservent (void) |
129 | { |
130 | __libc_lock_lock (lock); |
131 | |
132 | internal_nis_endservent (); |
133 | |
134 | __libc_lock_unlock (lock); |
135 | |
136 | return NSS_STATUS_SUCCESS; |
137 | } |
138 | |
139 | static enum nss_status |
140 | internal_nis_setservent (void) |
141 | { |
142 | char *domainname; |
143 | struct ypall_callback ypcb; |
144 | enum nss_status status; |
145 | |
146 | if (yp_get_default_domain (&domainname)) |
147 | return NSS_STATUS_UNAVAIL; |
148 | |
149 | internal_nis_endservent (); |
150 | |
151 | ypcb.foreach = _nis_saveit; |
152 | ypcb.data = (char *) &intern; |
153 | status = yperr2nss (yp_all (domainname, "services.byname" , &ypcb)); |
154 | |
155 | /* Mark the last buffer as full. */ |
156 | if (intern.next != NULL) |
157 | intern.next->size = intern.offset; |
158 | |
159 | intern.next = intern.start; |
160 | intern.offset = 0; |
161 | |
162 | return status; |
163 | } |
164 | |
165 | enum nss_status |
166 | _nss_nis_setservent (int stayopen) |
167 | { |
168 | enum nss_status status; |
169 | |
170 | __libc_lock_lock (lock); |
171 | |
172 | status = internal_nis_setservent (); |
173 | |
174 | __libc_lock_unlock (lock); |
175 | |
176 | return status; |
177 | } |
178 | |
179 | static enum nss_status |
180 | internal_nis_getservent_r (struct servent *serv, char *buffer, |
181 | size_t buflen, int *errnop) |
182 | { |
183 | struct parser_data *pdata = (void *) buffer; |
184 | int parse_res; |
185 | char *p; |
186 | |
187 | if (intern.start == NULL) |
188 | internal_nis_setservent (); |
189 | |
190 | if (intern.next == NULL) |
191 | /* Not one entry in the map. */ |
192 | return NSS_STATUS_NOTFOUND; |
193 | |
194 | /* Get the next entry until we found a correct one. */ |
195 | do |
196 | { |
197 | struct response_t *bucket = intern.next; |
198 | |
199 | if (__glibc_unlikely (intern.offset >= bucket->size)) |
200 | { |
201 | if (bucket->next == NULL) |
202 | return NSS_STATUS_NOTFOUND; |
203 | |
204 | /* We look at all the content in the current bucket. Go on |
205 | to the next. */ |
206 | bucket = intern.next = bucket->next; |
207 | intern.offset = 0; |
208 | } |
209 | |
210 | for (p = &bucket->mem[intern.offset]; isspace (*p); ++p) |
211 | ++intern.offset; |
212 | |
213 | size_t len = strlen (p) + 1; |
214 | if (__glibc_unlikely (len > buflen)) |
215 | { |
216 | *errnop = ERANGE; |
217 | return NSS_STATUS_TRYAGAIN; |
218 | } |
219 | |
220 | /* We unfortunately have to copy the data in the user-provided |
221 | buffer because that buffer might be around for a very long |
222 | time and the servent structure must remain valid. If we would |
223 | rely on the BUCKET memory the next 'setservent' or 'endservent' |
224 | call would destroy it. |
225 | |
226 | The important thing is that it is a single NUL-terminated |
227 | string. This is what the parsing routine expects. */ |
228 | p = memcpy (buffer, &bucket->mem[intern.offset], len); |
229 | |
230 | parse_res = _nss_files_parse_servent (p, serv, pdata, buflen, errnop); |
231 | if (__glibc_unlikely (parse_res == -1)) |
232 | return NSS_STATUS_TRYAGAIN; |
233 | |
234 | intern.offset += len; |
235 | } |
236 | while (!parse_res); |
237 | |
238 | return NSS_STATUS_SUCCESS; |
239 | } |
240 | |
241 | enum nss_status |
242 | _nss_nis_getservent_r (struct servent *serv, char *buffer, size_t buflen, |
243 | int *errnop) |
244 | { |
245 | enum nss_status status; |
246 | |
247 | __libc_lock_lock (lock); |
248 | |
249 | status = internal_nis_getservent_r (serv, buffer, buflen, errnop); |
250 | |
251 | __libc_lock_unlock (lock); |
252 | |
253 | return status; |
254 | } |
255 | |
256 | enum nss_status |
257 | _nss_nis_getservbyname_r (const char *name, const char *protocol, |
258 | struct servent *serv, char *buffer, size_t buflen, |
259 | int *errnop) |
260 | { |
261 | if (name == NULL) |
262 | { |
263 | *errnop = EINVAL; |
264 | return NSS_STATUS_UNAVAIL; |
265 | } |
266 | |
267 | char *domain; |
268 | if (__glibc_unlikely (yp_get_default_domain (&domain))) |
269 | return NSS_STATUS_UNAVAIL; |
270 | |
271 | /* If the protocol is given, we could try if our NIS server knows |
272 | about services.byservicename map. If yes, we only need one query. */ |
273 | size_t keylen = strlen (name) + (protocol ? 1 + strlen (protocol) : 0); |
274 | /* Limit key length to the maximum size of an RPC packet. */ |
275 | if (keylen > UDPMSGSIZE) |
276 | { |
277 | *errnop = ERANGE; |
278 | return NSS_STATUS_UNAVAIL; |
279 | } |
280 | |
281 | char key[keylen + 1]; |
282 | |
283 | /* key is: "name/proto" */ |
284 | char *cp = stpcpy (key, name); |
285 | if (protocol != NULL) |
286 | { |
287 | *cp++ = '/'; |
288 | strcpy (cp, protocol); |
289 | } |
290 | |
291 | char *result; |
292 | int int_len; |
293 | int status = yp_match (domain, "services.byservicename" , key, |
294 | keylen, &result, &int_len); |
295 | size_t len = int_len; |
296 | |
297 | /* If we found the key, it's ok and parse the result. If not, |
298 | fall through and parse the complete table. */ |
299 | if (__glibc_likely (status == YPERR_SUCCESS)) |
300 | { |
301 | if (__glibc_unlikely ((size_t) (len + 1) > buflen)) |
302 | { |
303 | free (result); |
304 | *errnop = ERANGE; |
305 | return NSS_STATUS_TRYAGAIN; |
306 | } |
307 | |
308 | char *p = strncpy (buffer, result, len); |
309 | buffer[len] = '\0'; |
310 | while (isspace (*p)) |
311 | ++p; |
312 | free (result); |
313 | |
314 | int parse_res = _nss_files_parse_servent (p, serv, (void *) buffer, |
315 | buflen, errnop); |
316 | if (__glibc_unlikely (parse_res < 0)) |
317 | { |
318 | if (parse_res == -1) |
319 | return NSS_STATUS_TRYAGAIN; |
320 | else |
321 | return NSS_STATUS_NOTFOUND; |
322 | } |
323 | else |
324 | return NSS_STATUS_SUCCESS; |
325 | } |
326 | |
327 | /* Check if it is safe to rely on services.byservicename. */ |
328 | if (_nsl_default_nss () & NSS_FLAG_SERVICES_AUTHORITATIVE) |
329 | return yperr2nss (status); |
330 | |
331 | struct ypall_callback ypcb; |
332 | struct search_t req; |
333 | |
334 | ypcb.foreach = dosearch; |
335 | ypcb.data = (char *) &req; |
336 | req.name = name; |
337 | req.proto = protocol; |
338 | req.port = -1; |
339 | req.serv = serv; |
340 | req.buffer = buffer; |
341 | req.buflen = buflen; |
342 | req.errnop = errnop; |
343 | req.status = NSS_STATUS_NOTFOUND; |
344 | status = yp_all (domain, "services.byname" , &ypcb); |
345 | |
346 | if (__glibc_unlikely (status != YPERR_SUCCESS)) |
347 | return yperr2nss (status); |
348 | |
349 | return req.status; |
350 | } |
351 | |
352 | enum nss_status |
353 | _nss_nis_getservbyport_r (int port, const char *protocol, |
354 | struct servent *serv, char *buffer, |
355 | size_t buflen, int *errnop) |
356 | { |
357 | char *domain; |
358 | if (__glibc_unlikely (yp_get_default_domain (&domain))) |
359 | return NSS_STATUS_UNAVAIL; |
360 | |
361 | /* If the protocol is given, we only need one query. |
362 | Otherwise try first port/tcp, then port/udp and then fallback |
363 | to sequential scanning of services.byname. */ |
364 | const char *proto = protocol != NULL ? protocol : "tcp" ; |
365 | /* Limit protocol name length to the maximum size of an RPC packet. */ |
366 | if (strlen (proto) > UDPMSGSIZE) |
367 | { |
368 | *errnop = ERANGE; |
369 | return NSS_STATUS_UNAVAIL; |
370 | } |
371 | |
372 | do |
373 | { |
374 | /* key is: "port/proto" */ |
375 | char key[sizeof (int) * 3 + strlen (proto) + 2]; |
376 | size_t keylen = snprintf (key, sizeof (key), "%d/%s" , ntohs (port), |
377 | proto); |
378 | |
379 | char *result; |
380 | int int_len; |
381 | int status = yp_match (domain, "services.byname" , key, keylen, &result, |
382 | &int_len); |
383 | size_t len = int_len; |
384 | |
385 | /* If we found the key, it's ok and parse the result. If not, |
386 | fall through and parse the complete table. */ |
387 | if (__glibc_likely (status == YPERR_SUCCESS)) |
388 | { |
389 | if (__glibc_unlikely ((size_t) (len + 1) > buflen)) |
390 | { |
391 | free (result); |
392 | *errnop = ERANGE; |
393 | return NSS_STATUS_TRYAGAIN; |
394 | } |
395 | |
396 | char *p = strncpy (buffer, result, len); |
397 | buffer[len] = '\0'; |
398 | while (isspace (*p)) |
399 | ++p; |
400 | free (result); |
401 | int parse_res = _nss_files_parse_servent (p, serv, (void *) buffer, |
402 | buflen, errnop); |
403 | if (__glibc_unlikely (parse_res < 0)) |
404 | { |
405 | if (parse_res == -1) |
406 | return NSS_STATUS_TRYAGAIN; |
407 | else |
408 | return NSS_STATUS_NOTFOUND; |
409 | } |
410 | |
411 | return NSS_STATUS_SUCCESS; |
412 | } |
413 | } |
414 | while (protocol == NULL && (proto[0] == 't' ? (proto = "udp" ) : NULL)); |
415 | |
416 | if (port == -1) |
417 | return NSS_STATUS_NOTFOUND; |
418 | |
419 | struct ypall_callback ypcb; |
420 | struct search_t req; |
421 | |
422 | ypcb.foreach = dosearch; |
423 | ypcb.data = (char *) &req; |
424 | req.name = NULL; |
425 | req.proto = protocol; |
426 | req.port = port; |
427 | req.serv = serv; |
428 | req.buffer = buffer; |
429 | req.buflen = buflen; |
430 | req.errnop = errnop; |
431 | req.status = NSS_STATUS_NOTFOUND; |
432 | int status = yp_all (domain, "services.byname" , &ypcb); |
433 | |
434 | if (__glibc_unlikely (status != YPERR_SUCCESS)) |
435 | return yperr2nss (status); |
436 | |
437 | return req.status; |
438 | } |
439 | |