(saveit): Fix return codes in error case.
[kopensolaris-gnu/glibc.git] / nis / nss_nis / nis-service.c
1 /* Copyright (C) 1996-2001, 2002, 2003, 2004 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, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <nss.h>
21 #include <netdb.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <bits/libc-lock.h>
26 #include <rpcsvc/yp.h>
27 #include <rpcsvc/ypclnt.h>
28
29 #include "nss-nis.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 struct response_t
40 {
41   struct response_t *next;
42   char val[0];
43 };
44
45 struct intern_t
46 {
47   struct response_t *start;
48   struct response_t *next;
49 };
50 typedef struct intern_t intern_t;
51
52 static intern_t intern = { NULL, NULL };
53
54 struct search_t
55 {
56   const char *name;
57   const char *proto;
58   int port;
59   enum nss_status status;
60   struct servent *serv;
61   char *buffer;
62   size_t buflen;
63   int *errnop;
64 };
65
66 static int
67 saveit (int instatus, char *inkey, int inkeylen, char *inval,
68         int invallen, char *indata)
69 {
70   intern_t *intern = (intern_t *) indata;
71
72   if (instatus != YP_TRUE)
73     return 1;
74
75   if (inkey && inkeylen > 0 && inval && invallen > 0)
76     {
77       struct response_t *newp = malloc (sizeof (struct response_t)
78                                         + invallen + 1);
79       if (newp == NULL)
80         return 1; /* We have no error code for out of memory */
81
82       if (intern->start == NULL)
83         intern->start = newp;
84       else
85         intern->next->next = newp;
86       intern->next = newp;
87
88       newp->next = NULL;
89       *((char *) mempcpy (newp->val, inval, invallen)) = '\0';
90     }
91
92   return 0;
93 }
94
95 static int
96 dosearch (int instatus, char *inkey, int inkeylen, char *inval,
97           int invallen, char *indata)
98 {
99   struct search_t *req = (struct search_t *) indata;
100
101   if (instatus != YP_TRUE)
102     return 1;
103
104   if (inkey && inkeylen > 0 && inval && invallen > 0)
105     {
106       struct parser_data *pdata = (void *) req->buffer;
107       int parse_res;
108       char *p;
109
110       if ((size_t) (invallen + 1) > req->buflen)
111         {
112           *req->errnop = ERANGE;
113           req->status = NSS_STATUS_TRYAGAIN;
114           return 1;
115         }
116
117       p = strncpy (req->buffer, inval, invallen);
118       req->buffer[invallen] = '\0';
119       while (isspace (*p))
120         ++p;
121
122       parse_res = _nss_files_parse_servent (p, req->serv, pdata, req->buflen,
123                                             req->errnop);
124       if (parse_res == -1)
125         {
126           req->status = NSS_STATUS_TRYAGAIN;
127           return 1;
128         }
129
130       if (!parse_res)
131         return 0;
132
133       if (req->proto != NULL && strcmp (req->serv->s_proto, req->proto) != 0)
134         return 0;
135
136       if (req->port != -1 && req->serv->s_port != req->port)
137         return 0;
138
139       if (req->name != NULL && strcmp (req->serv->s_name, req->name) != 0)
140         {
141           char **cp;
142           for (cp = req->serv->s_aliases; *cp; cp++)
143             if (strcmp (req->name, *cp) == 0)
144               break;
145
146           if (*cp == NULL)
147             return 0;
148         }
149
150       req->status = NSS_STATUS_SUCCESS;
151       return 1;
152     }
153
154   return 0;
155 }
156
157 static enum nss_status
158 internal_nis_endservent (intern_t * intern)
159 {
160   while (intern->start != NULL)
161     {
162       intern->next = intern->start;
163       intern->start = intern->start->next;
164       free (intern->next);
165     }
166
167   return NSS_STATUS_SUCCESS;
168 }
169
170 enum nss_status
171 _nss_nis_endservent (void)
172 {
173   enum nss_status status;
174
175   __libc_lock_lock (lock);
176
177   status = internal_nis_endservent (&intern);
178
179   __libc_lock_unlock (lock);
180
181   return status;
182 }
183
184 static enum nss_status
185 internal_nis_setservent (intern_t *intern)
186 {
187   char *domainname;
188   struct ypall_callback ypcb;
189   enum nss_status status;
190
191   if (yp_get_default_domain (&domainname))
192     return NSS_STATUS_UNAVAIL;
193
194   (void) internal_nis_endservent (intern);
195
196   ypcb.foreach = saveit;
197   ypcb.data = (char *) intern;
198   status = yperr2nss (yp_all (domainname, "services.byname", &ypcb));
199   intern->next = intern->start;
200
201   return status;
202 }
203
204 enum nss_status
205 _nss_nis_setservent (int stayopen)
206 {
207   enum nss_status status;
208
209   __libc_lock_lock (lock);
210
211   status = internal_nis_setservent (&intern);
212
213   __libc_lock_unlock (lock);
214
215   return status;
216 }
217
218 static enum nss_status
219 internal_nis_getservent_r (struct servent *serv, char *buffer,
220                            size_t buflen, int *errnop, intern_t *data)
221 {
222   struct parser_data *pdata = (void *) buffer;
223   int parse_res;
224   char *p;
225
226   if (data->start == NULL)
227     internal_nis_setservent (data);
228
229   /* Get the next entry until we found a correct one. */
230   do
231     {
232       if (data->next == NULL)
233         return NSS_STATUS_NOTFOUND;
234
235       p = strncpy (buffer, data->next->val, buflen);
236       while (isspace (*p))
237         ++p;
238
239       parse_res = _nss_files_parse_servent (p, serv, pdata, buflen, errnop);
240       if (parse_res == -1)
241         return NSS_STATUS_TRYAGAIN;
242       data->next = data->next->next;
243     }
244   while (!parse_res);
245
246   return NSS_STATUS_SUCCESS;
247 }
248
249 enum nss_status
250 _nss_nis_getservent_r (struct servent *serv, char *buffer, size_t buflen,
251                        int *errnop)
252 {
253   enum nss_status status;
254
255   __libc_lock_lock (lock);
256
257   status = internal_nis_getservent_r (serv, buffer, buflen, errnop, &intern);
258
259   __libc_lock_unlock (lock);
260
261   return status;
262 }
263
264 enum nss_status
265 _nss_nis_getservbyname_r (const char *name, const char *protocol,
266                           struct servent *serv, char *buffer, size_t buflen,
267                           int *errnop)
268 {
269   enum nss_status status;
270   char *domain;
271
272   if (name == NULL)
273     {
274       *errnop = EINVAL;
275       return NSS_STATUS_UNAVAIL;
276     }
277
278   if (yp_get_default_domain (&domain))
279     return NSS_STATUS_UNAVAIL;
280
281   /* If the protocol is given, we could try if our NIS server knows
282      about services.byservicename map. If yes, we only need one query.  */
283   char key[strlen (name) + (protocol ? strlen (protocol) : 0) + 2];
284   char *cp, *result;
285   size_t keylen, len;
286   int int_len;
287
288   /* key is: "name/proto" */
289   cp = stpcpy (key, name);
290   if (protocol)
291     {
292       *cp++ = '/';
293       strcpy (cp, protocol);
294     }
295   keylen = strlen (key);
296   status = yperr2nss (yp_match (domain, "services.byservicename", key,
297                                 keylen, &result, &int_len));
298   len = int_len;
299
300   /* If we found the key, it's ok and parse the result. If not,
301      fall through and parse the complete table. */
302   if (status == NSS_STATUS_SUCCESS)
303     {
304       struct parser_data *pdata = (void *) buffer;
305       int parse_res;
306       char *p;
307
308       if ((size_t) (len + 1) > buflen)
309         {
310           free (result);
311           *errnop = ERANGE;
312           return NSS_STATUS_TRYAGAIN;
313         }
314
315       p = strncpy (buffer, result, len);
316       buffer[len] = '\0';
317       while (isspace (*p))
318         ++p;
319       free (result);
320       parse_res = _nss_files_parse_servent (p, serv, pdata,
321                                             buflen, errnop);
322       if (parse_res < 0)
323         {
324           if (parse_res == -1)
325             return NSS_STATUS_TRYAGAIN;
326           else
327             return NSS_STATUS_NOTFOUND;
328         }
329       else
330         return NSS_STATUS_SUCCESS;
331     }
332
333   /* Check if it is safe to rely on services.byservicename.  */
334   if (_nis_default_nss () & NSS_FLAG_SERVICES_AUTHORITATIVE)
335     return status;
336
337   struct ypall_callback ypcb;
338   struct search_t req;
339
340   ypcb.foreach = dosearch;
341   ypcb.data = (char *) &req;
342   req.name = name;
343   req.proto = protocol;
344   req.port = -1;
345   req.serv = serv;
346   req.buffer = buffer;
347   req.buflen = buflen;
348   req.errnop = errnop;
349   req.status = NSS_STATUS_NOTFOUND;
350   status = yperr2nss (yp_all (domain, "services.byname", &ypcb));
351
352   if (status != NSS_STATUS_SUCCESS)
353     return status;
354
355   return req.status;
356 }
357
358 enum nss_status
359 _nss_nis_getservbyport_r (int port, const char *protocol,
360                           struct servent *serv, char *buffer,
361                           size_t buflen, int *errnop)
362 {
363   enum nss_status status;
364   char *domain;
365
366   if (yp_get_default_domain (&domain))
367     return NSS_STATUS_UNAVAIL;
368
369   /* If the protocol is given, we only need one query.
370      Otherwise try first port/tcp, then port/udp and then fallback
371      to sequential scanning of services.byname.  */
372   const char *proto = protocol != NULL ? protocol : "tcp";
373   do
374     {
375       char key[sizeof (int) * 3 + strlen (proto) + 2];
376       char *result;
377       size_t keylen, len;
378       int int_len;
379
380       /* key is: "port/proto" */
381       keylen = snprintf (key, sizeof (key), "%d/%s", ntohs (port), proto);
382       status = yperr2nss (yp_match (domain, "services.byname", key,
383                                     keylen, &result, &int_len));
384       len = int_len;
385
386       /* If we found the key, it's ok and parse the result. If not,
387          fall through and parse the complete table. */
388       if (status == NSS_STATUS_SUCCESS)
389         {
390           struct parser_data *pdata = (void *) buffer;
391           int parse_res;
392           char *p;
393
394           if ((size_t) (len + 1) > buflen)
395             {
396               free (result);
397               *errnop = ERANGE;
398               return NSS_STATUS_TRYAGAIN;
399             }
400
401           p = strncpy (buffer, result, len);
402           buffer[len] = '\0';
403           while (isspace (*p))
404             ++p;
405           free (result);
406           parse_res = _nss_files_parse_servent (p, serv, pdata,
407                                                 buflen, errnop);
408           if (parse_res < 0)
409             {
410               if (parse_res == -1)
411                 return NSS_STATUS_TRYAGAIN;
412               else
413                 return NSS_STATUS_NOTFOUND;
414             }
415           else
416             return NSS_STATUS_SUCCESS;
417         }
418     }
419   while (protocol == NULL && (proto[0] == 't' ? (proto = "udp") : NULL));
420
421   if (port == -1)
422     return NSS_STATUS_NOTFOUND;
423
424   struct ypall_callback ypcb;
425   struct search_t req;
426
427   ypcb.foreach = dosearch;
428   ypcb.data = (char *) &req;
429   req.name = NULL;
430   req.proto = protocol;
431   req.port = port;
432   req.serv = serv;
433   req.buffer = buffer;
434   req.buflen = buflen;
435   req.errnop = errnop;
436   req.status = NSS_STATUS_NOTFOUND;
437   status = yperr2nss (yp_all (domain, "services.byname", &ypcb));
438
439   if (status != NSS_STATUS_SUCCESS)
440     return status;
441
442   return req.status;
443 }