(_nss_nis_getservbyname_r): If protocol == NULL, try name/tcp and name/udp
[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 instatus;
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 YP_FALSE; /* 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      If the protocol is not given, try first name/tcp, then name/udp
284      and then fallback to sequential scanning of services.byname map.  */
285   const char *proto = protocol != NULL ? protocol : "tcp";
286   do
287     {
288       char key[strlen (name) + strlen (proto) + 2];
289       char *cp, *result;
290       size_t keylen, len;
291       int int_len;
292
293       /* key is: "name/proto" */
294       cp = stpcpy (key, name);
295       *cp++ = '/';
296       stpcpy (cp, proto);
297       keylen = strlen (key);
298       status = yperr2nss (yp_match (domain, "services.byservicename", key,
299                                     keylen, &result, &int_len));
300       len = int_len;
301
302       /* If we found the key, it's ok and parse the result. If not,
303          fall through and parse the complete table. */
304       if (status == NSS_STATUS_SUCCESS)
305         {
306           struct parser_data *pdata = (void *) buffer;
307           int parse_res;
308           char *p;
309
310           if ((size_t) (len + 1) > buflen)
311             {
312               free (result);
313               *errnop = ERANGE;
314               return NSS_STATUS_TRYAGAIN;
315             }
316
317           p = strncpy (buffer, result, len);
318           buffer[len] = '\0';
319           while (isspace (*p))
320             ++p;
321           free (result);
322           parse_res = _nss_files_parse_servent (p, serv, pdata,
323                                                 buflen, errnop);
324           if (parse_res < 0)
325             {
326               if (parse_res == -1)
327                 return NSS_STATUS_TRYAGAIN;
328               else
329                 return NSS_STATUS_NOTFOUND;
330             }
331           else
332             return NSS_STATUS_SUCCESS;
333         }
334     }
335   while (protocol == NULL && (proto[0] == 't' ? (proto = "udp") : NULL));
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 }