Move non-deprecated RPC-related functions from sunrpc to inet
[platform/upstream/glibc.git] / nis / nss_nis / nis-service.c
1 /* Copyright (C) 1996-2020 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    <https://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 }