54557c786d4ac6b13eeba280becd3815ce61218b
[dragonfly.git] / lib / libc / net / getservent.c
1 /*
2  * Copyright (c) 1983, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#)getservent.c     8.1 (Berkeley) 6/4/93
30  * $FreeBSD: src/lib/libc/net/getservent.c,v 1.23 2007/01/09 00:28:02 imp Exp $
31  * $DragonFly: src/lib/libc/net/getservent.c,v 1.7 2005/11/13 02:04:47 swildner Exp $
32  */
33
34 #include <sys/param.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <arpa/inet.h>
38 #include <errno.h>
39 #include <limits.h>
40 #include <netdb.h>
41 #include <nsswitch.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #ifdef YP
47 #include <rpc/rpc.h>
48 #include <rpcsvc/yp_prot.h>
49 #include <rpcsvc/ypclnt.h>
50 #endif
51 #include "namespace.h"
52 #include "reentrant.h"
53 #include "un-namespace.h"
54 #include "netdb_private.h"
55 #ifdef NS_CACHING
56 #include "nscache.h"
57 #endif
58 #include "nss_tls.h"
59
60 enum constants
61 {
62         SETSERVENT              = 1,
63         ENDSERVENT              = 2,
64         SERVENT_STORAGE_INITIAL = 1 << 10, /* 1 KByte */
65         SERVENT_STORAGE_MAX     = 1 << 20, /* 1 MByte */
66 };
67
68 struct servent_mdata
69 {
70         enum nss_lookup_type how;
71         int compat_mode;
72 };
73
74 static const ns_src defaultsrc[] = {
75         { NSSRC_COMPAT, NS_SUCCESS },
76         { NULL, 0 }
77 };
78
79 static int servent_unpack(char *, struct servent *, char **, size_t, int *);
80
81 /* files backend declarations */
82 struct files_state
83 {
84         FILE *fp;
85         int stayopen;
86
87         int compat_mode_active;
88 };
89 static void files_endstate(void *);
90 NSS_TLS_HANDLING(files);
91
92 static int files_servent(void *, void *, va_list);
93 static int files_setservent(void *, void *, va_list);
94
95 #ifdef YP
96 /* nis backend declarations */
97 static  int     nis_servent(void *, void *, va_list);
98 static  int     nis_setservent(void *, void *, va_list);
99
100 struct nis_state
101 {
102         int yp_stepping;
103         char yp_domain[MAXHOSTNAMELEN];
104         char *yp_key;
105         int yp_keylen;
106 };
107 static void nis_endstate(void *);
108 NSS_TLS_HANDLING(nis);
109
110 static int nis_servent(void *, void *, va_list);
111 static int nis_setservent(void *, void *, va_list);
112 #endif
113
114 /* compat backend declarations */
115 static int compat_setservent(void *, void *, va_list);
116
117 /* get** wrappers for get**_r functions declarations */
118 struct servent_state {
119         struct servent serv;
120         char *buffer;
121         size_t bufsize;
122 };
123 static  void    servent_endstate(void *);
124 NSS_TLS_HANDLING(servent);
125
126 struct key {
127         const char *proto;
128         union {
129                 const char *name;
130                 int port;
131         };
132 };
133
134 static int wrap_getservbyname_r(struct key, struct servent *, char *, size_t,
135     struct servent **);
136 static int wrap_getservbyport_r(struct key, struct servent *, char *, size_t,
137     struct servent **);
138 static int wrap_getservent_r(struct key, struct servent *, char *, size_t,
139     struct servent **);
140 static struct servent *getserv(int (*fn)(struct key, struct servent *, char *,
141     size_t, struct servent **), struct key);
142
143 #ifdef NS_CACHING
144 static int serv_id_func(char *, size_t *, va_list, void *);
145 static int serv_marshal_func(char *, size_t *, void *, va_list, void *);
146 static int serv_unmarshal_func(char *, size_t, void *, va_list, void *);
147 #endif
148
149 static int
150 servent_unpack(char *p, struct servent *serv, char **aliases,
151     size_t aliases_size, int *errnop)
152 {
153         char *cp, **q, *endp;
154         long l;
155
156         if (*p == '#')
157                 return -1;
158
159         memset(serv, 0, sizeof(struct servent));
160
161         cp = strpbrk(p, "#\n");
162         if (cp != NULL)
163                 *cp = '\0';
164         serv->s_name = p;
165
166         p = strpbrk(p, " \t");
167         if (p == NULL)
168                 return -1;
169         *p++ = '\0';
170         while (*p == ' ' || *p == '\t')
171                 p++;
172         cp = strpbrk(p, ",/");
173         if (cp == NULL)
174                 return -1;
175
176         *cp++ = '\0';
177         l = strtol(p, &endp, 10);
178         if (endp == p || *endp != '\0' || l < 0 || l > USHRT_MAX)
179                 return -1;
180         serv->s_port = htons((in_port_t)l);
181         serv->s_proto = cp;
182
183         q = serv->s_aliases = aliases;
184         cp = strpbrk(cp, " \t");
185         if (cp != NULL)
186                 *cp++ = '\0';
187         while (cp && *cp) {
188                 if (*cp == ' ' || *cp == '\t') {
189                         cp++;
190                         continue;
191                 }
192                 if (q < &aliases[aliases_size - 1]) {
193                         *q++ = cp;
194                 } else {
195                         *q = NULL;
196                         *errnop = ERANGE;
197                         return -1;
198                 }
199                 cp = strpbrk(cp, " \t");
200                 if (cp != NULL)
201                         *cp++ = '\0';
202         }
203         *q = NULL;
204
205         return 0;
206 }
207
208 /* files backend implementation */
209 static  void
210 files_endstate(void *p)
211 {
212         FILE * f;
213
214         if (p == NULL)
215                 return;
216
217         f = ((struct files_state *)p)->fp;
218         if (f != NULL)
219                 fclose(f);
220
221         free(p);
222 }
223
224 /*
225  * compat structures. compat and files sources functionalities are almost
226  * equal, so they all are managed by files_servent function
227  */
228 static int
229 files_servent(void *retval, void *mdata, va_list ap)
230 {
231         static const ns_src compat_src[] = {
232 #ifdef YP
233                 { NSSRC_NIS, NS_SUCCESS },
234 #endif
235                 { NULL, 0 }
236         };
237         ns_dtab compat_dtab[] = {
238 #ifdef YP
239                 { NSSRC_NIS, nis_servent,
240                         (void *)((struct servent_mdata *)mdata)->how },
241 #endif
242                 { NULL, NULL, NULL }
243         };
244
245         struct files_state *st;
246         int rv;
247         int stayopen;
248
249         struct servent_mdata *serv_mdata;
250         char *name;
251         char *proto;
252         int port;
253
254         struct servent *serv;
255         char *buffer;
256         size_t bufsize;
257         int *errnop;
258
259         char **aliases;
260         int aliases_size;
261         size_t linesize;
262         char *line;
263         char **cp;
264
265         name = NULL;
266         proto = NULL;
267         serv_mdata = (struct servent_mdata *)mdata;
268         switch (serv_mdata->how) {
269         case nss_lt_name:
270                 name = va_arg(ap, char *);
271                 proto = va_arg(ap, char *);
272                 break;
273         case nss_lt_id:
274                 port = va_arg(ap, int);
275                 proto = va_arg(ap, char *);
276                 break;
277         case nss_lt_all:
278                 break;
279         default:
280                 return NS_NOTFOUND;
281         };
282
283         serv = va_arg(ap, struct servent *);
284         buffer  = va_arg(ap, char *);
285         bufsize = va_arg(ap, size_t);
286         errnop = va_arg(ap,int *);
287
288         *errnop = files_getstate(&st);
289         if (*errnop != 0)
290                 return (NS_UNAVAIL);
291
292         if (st->fp == NULL)
293                 st->compat_mode_active = 0;
294
295         if (st->fp == NULL && (st->fp = fopen(_PATH_SERVICES, "r")) == NULL) {
296                 *errnop = errno;
297                 return (NS_UNAVAIL);
298         }
299
300         if (serv_mdata->how == nss_lt_all)
301                 stayopen = 1;
302         else {
303                 rewind(st->fp);
304                 stayopen = st->stayopen;
305         }
306
307         rv = NS_NOTFOUND;
308         do {
309                 if (!st->compat_mode_active) {
310                         if ((line = fgetln(st->fp, &linesize)) == NULL) {
311                                 *errnop = errno;
312                                 rv = NS_RETURN;
313                                 break;
314                         }
315
316                         if (*line=='+') {
317                                 if (serv_mdata->compat_mode != 0)
318                                         st->compat_mode_active = 1;
319                         } else {
320                                 if (bufsize <= linesize + _ALIGNBYTES +
321                                     sizeof(char *)) {
322                                         *errnop = ERANGE;
323                                         rv = NS_RETURN;
324                                         break;
325                                 }
326                                 aliases = (char **)_ALIGN(&buffer[linesize+1]);
327                                 aliases_size = (buffer + bufsize -
328                                     (char *)aliases) / sizeof(char *);
329                                 if (aliases_size < 1) {
330                                         *errnop = ERANGE;
331                                         rv = NS_RETURN;
332                                         break;
333                                 }
334
335                                 memcpy(buffer, line, linesize);
336                                 buffer[linesize] = '\0';
337                         }
338                 }
339
340                 if (st->compat_mode_active != 0) {
341                         switch (serv_mdata->how) {
342                         case nss_lt_name:
343                                 rv = nsdispatch(retval, compat_dtab,
344                                     NSDB_SERVICES_COMPAT, "getservbyname_r",
345                                     compat_src, name, proto, serv, buffer,
346                                     bufsize, errnop);
347                                 break;
348                         case nss_lt_id:
349                                 rv = nsdispatch(retval, compat_dtab,
350                                     NSDB_SERVICES_COMPAT, "getservbyport_r",
351                                     compat_src, port, proto, serv, buffer,
352                                         bufsize, errnop);
353                                 break;
354                         case nss_lt_all:
355                                 rv = nsdispatch(retval, compat_dtab,
356                                     NSDB_SERVICES_COMPAT, "getservent_r",
357                                     compat_src, serv, buffer, bufsize, errnop);
358                                 break;
359                         }
360
361                         if (!(rv & NS_TERMINATE) ||
362                             serv_mdata->how != nss_lt_all)
363                                 st->compat_mode_active = 0;
364
365                         continue;
366                 }
367
368                 rv = servent_unpack(buffer, serv, aliases, aliases_size,
369                     errnop);
370                 if (rv !=0 ) {
371                         if (*errnop == 0) {
372                                 rv = NS_NOTFOUND;
373                                 continue;
374                         }
375                         else {
376                                 rv = NS_RETURN;
377                                 break;
378                         }
379                 }
380
381                 rv = NS_NOTFOUND;
382                 switch (serv_mdata->how) {
383                 case nss_lt_name:
384                         if (strcmp(name, serv->s_name) == 0)
385                                 goto gotname;
386                         for (cp = serv->s_aliases; *cp; cp++)
387                                 if (strcmp(name, *cp) == 0)
388                                         goto gotname;
389
390                         continue;
391                 gotname:
392                         if (proto == 0 || strcmp(serv->s_proto, proto) == 0)
393                                 rv = NS_SUCCESS;
394                         break;
395                 case nss_lt_id:
396                         if (port != serv->s_port)
397                                 continue;
398
399                         if (proto == 0 || strcmp(serv->s_proto, proto) == 0)
400                                 rv = NS_SUCCESS;
401                         break;
402                 case nss_lt_all:
403                         rv = NS_SUCCESS;
404                         break;
405                 }
406
407         } while (!(rv & NS_TERMINATE));
408
409         if (!stayopen && st->fp != NULL) {
410                 fclose(st->fp);
411                 st->fp = NULL;
412         }
413
414         if ((rv == NS_SUCCESS) && (retval != NULL))
415                 *(struct servent **)retval=serv;
416
417         return (rv);
418 }
419
420 static int
421 files_setservent(void *retval, void *mdata, va_list ap)
422 {
423         struct files_state *st;
424         int rv;
425         int f;
426
427         rv = files_getstate(&st);
428         if (rv != 0)
429                 return (NS_UNAVAIL);
430
431         switch ((enum constants)mdata) {
432         case SETSERVENT:
433                 f = va_arg(ap,int);
434                 if (st->fp == NULL)
435                         st->fp = fopen(_PATH_SERVICES, "r");
436                 else
437                         rewind(st->fp);
438                 st->stayopen |= f;
439                 break;
440         case ENDSERVENT:
441                 if (st->fp != NULL) {
442                         fclose(st->fp);
443                         st->fp = NULL;
444                 }
445                 st->stayopen = 0;
446                 break;
447         default:
448                 break;
449         };
450
451         st->compat_mode_active = 0;
452         return (NS_UNAVAIL);
453 }
454
455 /* nis backend implementation */
456 #ifdef YP
457 static  void
458 nis_endstate(void *p)
459 {
460         if (p == NULL)
461                 return;
462
463         free(((struct nis_state *)p)->yp_key);
464         free(p);
465 }
466
467 static int
468 nis_servent(void *retval, void *mdata, va_list ap)
469 {
470         char *resultbuf, *lastkey;
471         int resultbuflen;
472         char buf[YPMAXRECORD + 2];
473
474         struct nis_state *st;
475         int rv;
476
477         enum nss_lookup_type how;
478         char *name;
479         char *proto;
480         int port;
481
482         struct servent *serv;
483         char *buffer;
484         size_t bufsize;
485         int *errnop;
486
487         char **aliases;
488         int aliases_size;
489
490         name = NULL;
491         proto = NULL;
492         how = (enum nss_lookup_type)mdata;
493         switch (how) {
494         case nss_lt_name:
495                 name = va_arg(ap, char *);
496                 proto = va_arg(ap, char *);
497                 break;
498         case nss_lt_id:
499                 port = va_arg(ap, int);
500                 proto = va_arg(ap, char *);
501                 break;
502         case nss_lt_all:
503                 break;
504         default:
505                 return NS_NOTFOUND;
506         };
507
508         serv = va_arg(ap, struct servent *);
509         buffer  = va_arg(ap, char *);
510         bufsize = va_arg(ap, size_t);
511         errnop = va_arg(ap, int *);
512
513         *errnop = nis_getstate(&st);
514         if (*errnop != 0)
515                 return (NS_UNAVAIL);
516
517         if (st->yp_domain[0] == '\0') {
518                 if (getdomainname(st->yp_domain, sizeof st->yp_domain)) {
519                         *errnop = errno;
520                         return (NS_UNAVAIL);
521                 }
522         }
523
524         do {
525                 switch (how) {
526                 case nss_lt_name:
527                         snprintf(buf, sizeof(buf), "%s/%s", name, proto);
528                         if (yp_match(st->yp_domain, "services.byname", buf,
529                             strlen(buf), &resultbuf, &resultbuflen)) {
530                                 rv = NS_NOTFOUND;
531                                 goto fin;
532                         }
533                         break;
534                 case nss_lt_id:
535                         snprintf(buf, sizeof(buf), "%d/%s", ntohs(port),
536                             proto);
537
538                         /*
539                          * We have to be a little flexible
540                          * here. Ideally you're supposed to have both
541                          * a services.byname and a services.byport
542                          * map, but some systems have only
543                          * services.byname. FreeBSD cheats a little by
544                          * putting the services.byport information in
545                          * the same map as services.byname so that
546                          * either case will work. We allow for both
547                          * possibilities here: if there is no
548                          * services.byport map, we try services.byname
549                          * instead.
550                          */
551                         rv = yp_match(st->yp_domain, "services.byport", buf,
552                             strlen(buf), &resultbuf, &resultbuflen);
553                         if (rv) {
554                                 if (rv == YPERR_MAP) {
555                                         if (yp_match(st->yp_domain,
556                                             "services.byname", buf,
557                                             strlen(buf), &resultbuf,
558                                             &resultbuflen)) {
559                                                 rv = NS_NOTFOUND;
560                                                 goto fin;
561                                         }
562                                 } else {
563                                         rv = NS_NOTFOUND;
564                                         goto fin;
565                                 }
566                         }
567
568                         break;
569                 case nss_lt_all:
570                         if (!st->yp_stepping) {
571                                 free(st->yp_key);
572                                 rv = yp_first(st->yp_domain, "services.byname",
573                                     &st->yp_key, &st->yp_keylen, &resultbuf,
574                                     &resultbuflen);
575                                 if (rv) {
576                                         rv = NS_NOTFOUND;
577                                         goto fin;
578                                 }
579                                 st->yp_stepping = 1;
580                         } else {
581                                 lastkey = st->yp_key;
582                                 rv = yp_next(st->yp_domain, "services.byname",
583                                     st->yp_key, st->yp_keylen, &st->yp_key,
584                                     &st->yp_keylen, &resultbuf, &resultbuflen);
585                                 free(lastkey);
586                                 if (rv) {
587                                         st->yp_stepping = 0;
588                                         rv = NS_NOTFOUND;
589                                         goto fin;
590                                 }
591                         }
592                         break;
593                 };
594
595                 /* we need a room for additional \n symbol */
596                 if (bufsize <=
597                     resultbuflen + 1 + _ALIGNBYTES + sizeof(char *)) {
598                         *errnop = ERANGE;
599                         rv = NS_RETURN;
600                         break;
601                 }
602
603                 aliases = (char **)_ALIGN(&buffer[resultbuflen + 2]);
604                 aliases_size =
605                     (buffer + bufsize - (char *)aliases) / sizeof(char *);
606                 if (aliases_size < 1) {
607                         *errnop = ERANGE;
608                         rv = NS_RETURN;
609                         break;
610                 }
611
612                 /*
613                  * servent_unpack expects lines terminated with \n --
614                  * make it happy
615                  */
616                 memcpy(buffer, resultbuf, resultbuflen);
617                 buffer[resultbuflen] = '\n';
618                 buffer[resultbuflen + 1] = '\0';
619
620                 if (servent_unpack(buffer, serv, aliases, aliases_size,
621                     errnop) != 0) {
622                         if (*errnop == 0)
623                                 rv = NS_NOTFOUND;
624                         else
625                                 rv = NS_RETURN;
626                 } else
627                         rv = NS_SUCCESS;
628                 free(resultbuf);
629
630         } while (!(rv & NS_TERMINATE) && how == nss_lt_all);
631
632 fin:
633         if (rv == NS_SUCCESS && retval != NULL)
634                 *(struct servent **)retval = serv;
635
636         return (rv);
637 }
638
639 static int
640 nis_setservent(void *result, void *mdata, va_list ap)
641 {
642         struct nis_state *st;
643         int rv;
644
645         rv = nis_getstate(&st);
646         if (rv != 0)
647                 return (NS_UNAVAIL);
648
649         switch ((enum constants)mdata) {
650         case SETSERVENT:
651         case ENDSERVENT:
652                 free(st->yp_key);
653                 st->yp_key = NULL;
654                 st->yp_stepping = 0;
655                 break;
656         default:
657                 break;
658         };
659
660         return (NS_UNAVAIL);
661 }
662 #endif
663
664 /* compat backend implementation */
665 static int
666 compat_setservent(void *retval, void *mdata, va_list ap)
667 {
668         static const ns_src compat_src[] = {
669 #ifdef YP
670                 { NSSRC_NIS, NS_SUCCESS },
671 #endif
672                 { NULL, 0 }
673         };
674         ns_dtab compat_dtab[] = {
675 #ifdef YP
676                 { NSSRC_NIS, nis_setservent, mdata },
677 #endif
678                 { NULL, NULL, NULL }
679         };
680         int f;
681
682         files_setservent(retval, mdata, ap);
683
684         switch ((enum constants)mdata) {
685         case SETSERVENT:
686                 f = va_arg(ap, int);
687                 nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT,
688                     "setservent", compat_src, f);
689                 break;
690         case ENDSERVENT:
691                 nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT,
692                     "endservent", compat_src);
693                 break;
694         default:
695                 break;
696         }
697
698         return (NS_UNAVAIL);
699 }
700
701 #ifdef NS_CACHING
702 static int
703 serv_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata)
704 {
705         char *name;
706         char *proto;
707         int port;
708
709         size_t desired_size, size, size2;
710         enum nss_lookup_type lookup_type;
711         int res = NS_UNAVAIL;
712
713         lookup_type = (enum nss_lookup_type)cache_mdata;
714         switch (lookup_type) {
715         case nss_lt_name:
716                 name = va_arg(ap, char *);
717                 proto = va_arg(ap, char *);
718
719                 size = strlen(name);
720                 desired_size = sizeof(enum nss_lookup_type) + size + 1;
721                 if (proto != NULL) {
722                         size2 = strlen(proto);
723                         desired_size += size2 + 1;
724                 } else
725                         size2 = 0;
726
727                 if (desired_size > *buffer_size) {
728                         res = NS_RETURN;
729                         goto fin;
730                 }
731
732                 memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
733                 memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1);
734
735                 if (proto != NULL)
736                         memcpy(buffer + sizeof(enum nss_lookup_type) + size + 1,
737                             proto, size2 + 1);
738
739                 res = NS_SUCCESS;
740                 break;
741         case nss_lt_id:
742                 port = va_arg(ap, int);
743                 proto = va_arg(ap, char *);
744
745                 desired_size = sizeof(enum nss_lookup_type) + sizeof(int);
746                 if (proto != NULL) {
747                         size = strlen(proto);
748                         desired_size += size + 1;
749                 } else
750                         size = 0;
751
752                 if (desired_size > *buffer_size) {
753                         res = NS_RETURN;
754                         goto fin;
755                 }
756
757                 memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
758                 memcpy(buffer + sizeof(enum nss_lookup_type), &port,
759                     sizeof(int));
760
761                 if (proto != NULL)
762                         memcpy(buffer + sizeof(enum nss_lookup_type) +
763                             sizeof(int), proto, size + 1);
764
765                 res = NS_SUCCESS;
766                 break;
767         default:
768                 /* should be unreachable */
769                 return (NS_UNAVAIL);
770         }
771
772 fin:
773         *buffer_size = desired_size;
774         return (res);
775 }
776
777 int
778 serv_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap,
779     void *cache_mdata)
780 {
781         char *name;
782         char *proto;
783         int port;
784         struct servent *serv;
785         char *orig_buf;
786         size_t orig_buf_size;
787
788         struct servent new_serv;
789         size_t desired_size;
790         char **alias;
791         char *p;
792         size_t size;
793         size_t aliases_size;
794
795         switch ((enum nss_lookup_type)cache_mdata) {
796         case nss_lt_name:
797                 name = va_arg(ap, char *);
798                 proto = va_arg(ap, char *);
799                 break;
800         case nss_lt_id:
801                 port = va_arg(ap, int);
802                 proto = va_arg(ap, char *);
803                 break;
804         case nss_lt_all:
805                 break;
806         default:
807                 /* should be unreachable */
808                 return (NS_UNAVAIL);
809         }
810
811         serv = va_arg(ap, struct servent *);
812         orig_buf = va_arg(ap, char *);
813         orig_buf_size = va_arg(ap, size_t);
814
815         desired_size = _ALIGNBYTES + sizeof(struct servent) + sizeof(char *);
816         if (serv->s_name != NULL)
817                 desired_size += strlen(serv->s_name) + 1;
818         if (serv->s_proto != NULL)
819                 desired_size += strlen(serv->s_proto) + 1;
820
821         aliases_size = 0;
822         if (serv->s_aliases != NULL) {
823                 for (alias = serv->s_aliases; *alias; ++alias) {
824                         desired_size += strlen(*alias) + 1;
825                         ++aliases_size;
826                 }
827
828                 desired_size += _ALIGNBYTES +
829                     sizeof(char *) * (aliases_size + 1);
830         }
831
832         if (*buffer_size < desired_size) {
833                 /* this assignment is here for future use */
834                 *buffer_size = desired_size;
835                 return (NS_RETURN);
836         }
837
838         memcpy(&new_serv, serv, sizeof(struct servent));
839         memset(buffer, 0, desired_size);
840
841         *buffer_size = desired_size;
842         p = buffer + sizeof(struct servent) + sizeof(char *);
843         memcpy(buffer + sizeof(struct servent), &p, sizeof(char *));
844         p = (char *)_ALIGN(p);
845
846         if (new_serv.s_name != NULL) {
847                 size = strlen(new_serv.s_name);
848                 memcpy(p, new_serv.s_name, size);
849                 new_serv.s_name = p;
850                 p += size + 1;
851         }
852
853         if (new_serv.s_proto != NULL) {
854                 size = strlen(new_serv.s_proto);
855                 memcpy(p, new_serv.s_proto, size);
856                 new_serv.s_proto = p;
857                 p += size + 1;
858         }
859
860         if (new_serv.s_aliases != NULL) {
861                 p = (char *)_ALIGN(p);
862                 memcpy(p, new_serv.s_aliases, sizeof(char *) * aliases_size);
863                 new_serv.s_aliases = (char **)p;
864                 p += sizeof(char *) * (aliases_size + 1);
865
866                 for (alias = new_serv.s_aliases; *alias; ++alias) {
867                         size = strlen(*alias);
868                         memcpy(p, *alias, size);
869                         *alias = p;
870                         p += size + 1;
871                 }
872         }
873
874         memcpy(buffer, &new_serv, sizeof(struct servent));
875         return (NS_SUCCESS);
876 }
877
878 int
879 serv_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap,
880     void *cache_mdata)
881 {
882         char *name;
883         char *proto;
884         int port;
885         struct servent *serv;
886         char *orig_buf;
887         char *p;
888         char **alias;
889         size_t orig_buf_size;
890         int *ret_errno;
891
892         switch ((enum nss_lookup_type)cache_mdata) {
893         case nss_lt_name:
894                 name = va_arg(ap, char *);
895                 proto = va_arg(ap, char *);
896                 break;
897         case nss_lt_id:
898                 port = va_arg(ap, int);
899                 proto = va_arg(ap, char *);
900                 break;
901         case nss_lt_all:
902                 break;
903         default:
904                 /* should be unreachable */
905                 return (NS_UNAVAIL);
906         }
907
908         serv = va_arg(ap, struct servent *);
909         orig_buf = va_arg(ap, char *);
910         orig_buf_size = va_arg(ap, size_t);
911         ret_errno = va_arg(ap, int *);
912
913         if (orig_buf_size <
914             buffer_size - sizeof(struct servent) - sizeof(char *)) {
915                 *ret_errno = ERANGE;
916                 return (NS_RETURN);
917         }
918
919         memcpy(serv, buffer, sizeof(struct servent));
920         memcpy(&p, buffer + sizeof(struct servent), sizeof(char *));
921
922         orig_buf = (char *)_ALIGN(orig_buf);
923         memcpy(orig_buf, buffer + sizeof(struct servent) + sizeof(char *) +
924             (_ALIGN(p) - (size_t)p),
925             buffer_size - sizeof(struct servent) - sizeof(char *) -
926             (_ALIGN(p) - (size_t)p));
927         p = (char *)_ALIGN(p);
928
929         NS_APPLY_OFFSET(serv->s_name, orig_buf, p, char *);
930         NS_APPLY_OFFSET(serv->s_proto, orig_buf, p, char *);
931         if (serv->s_aliases != NULL) {
932                 NS_APPLY_OFFSET(serv->s_aliases, orig_buf, p, char **);
933
934                 for (alias = serv->s_aliases; *alias; ++alias)
935                         NS_APPLY_OFFSET(*alias, orig_buf, p, char *);
936         }
937
938         if (retval != NULL)
939                 *((struct servent **)retval) = serv;
940         return (NS_SUCCESS);
941 }
942
943 NSS_MP_CACHE_HANDLING(services);
944 #endif /* NS_CACHING */
945
946 /* get**_r functions implementation */
947 int
948 getservbyname_r(const char *name, const char *proto, struct servent *serv,
949     char *buffer, size_t bufsize, struct servent **result)
950 {
951         static const struct servent_mdata mdata = { nss_lt_name, 0 };
952         static const struct servent_mdata compat_mdata = { nss_lt_name, 1 };
953 #ifdef NS_CACHING
954         static const nss_cache_info cache_info =
955         NS_COMMON_CACHE_INFO_INITIALIZER(
956                 services, (void *)nss_lt_name,
957                 serv_id_func, serv_marshal_func, serv_unmarshal_func);
958 #endif /* NS_CACHING */
959         static const ns_dtab dtab[] = {
960                 { NSSRC_FILES, files_servent, (void *)&mdata },
961 #ifdef YP
962                 { NSSRC_NIS, nis_servent, (void *)nss_lt_name },
963 #endif
964                 { NSSRC_COMPAT, files_servent, (void *)&compat_mdata },
965 #ifdef NS_CACHING
966                 NS_CACHE_CB(&cache_info)
967 #endif
968                 { NULL, NULL, NULL }
969         };
970         int     rv, ret_errno;
971
972         ret_errno = 0;
973         *result = NULL;
974         rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyname_r",
975             defaultsrc, name, proto, serv, buffer, bufsize, &ret_errno);
976
977         if (rv == NS_SUCCESS)
978                 return (0);
979         else
980                 return (ret_errno);
981 }
982
983 int
984 getservbyport_r(int port, const char *proto, struct servent *serv,
985     char *buffer, size_t bufsize, struct servent **result)
986 {
987         static const struct servent_mdata mdata = { nss_lt_id, 0 };
988         static const struct servent_mdata compat_mdata = { nss_lt_id, 1 };
989 #ifdef NS_CACHING
990         static const nss_cache_info cache_info =
991         NS_COMMON_CACHE_INFO_INITIALIZER(
992                 services, (void *)nss_lt_id,
993                 serv_id_func, serv_marshal_func, serv_unmarshal_func);
994 #endif
995         static const ns_dtab dtab[] = {
996                 { NSSRC_FILES, files_servent, (void *)&mdata },
997 #ifdef YP
998                 { NSSRC_NIS, nis_servent, (void *)nss_lt_id },
999 #endif
1000                 { NSSRC_COMPAT, files_servent, (void *)&compat_mdata },
1001 #ifdef NS_CACHING
1002                 NS_CACHE_CB(&cache_info)
1003 #endif
1004                 { NULL, NULL, NULL }
1005         };
1006         int rv, ret_errno;
1007
1008         ret_errno = 0;
1009         *result = NULL;
1010         rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyport_r",
1011             defaultsrc, port, proto, serv, buffer, bufsize, &ret_errno);
1012
1013         if (rv == NS_SUCCESS)
1014                 return (0);
1015         else
1016                 return (ret_errno);
1017 }
1018
1019 int
1020 getservent_r(struct servent *serv, char *buffer, size_t bufsize,
1021     struct servent **result)
1022 {
1023         static const struct servent_mdata mdata = { nss_lt_all, 0 };
1024         static const struct servent_mdata compat_mdata = { nss_lt_all, 1 };
1025 #ifdef NS_CACHING
1026         static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
1027                 services, (void *)nss_lt_all,
1028                 serv_marshal_func, serv_unmarshal_func);
1029 #endif
1030         static const ns_dtab dtab[] = {
1031                 { NSSRC_FILES, files_servent, (void *)&mdata },
1032 #ifdef YP
1033                 { NSSRC_NIS, nis_servent, (void *)nss_lt_all },
1034 #endif
1035                 { NSSRC_COMPAT, files_servent, (void *)&compat_mdata },
1036 #ifdef NS_CACHING
1037                 NS_CACHE_CB(&cache_info)
1038 #endif
1039                 { NULL, NULL, NULL }
1040         };
1041         int rv, ret_errno;
1042
1043         ret_errno = 0;
1044         *result = NULL;
1045         rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservent_r",
1046             defaultsrc, serv, buffer, bufsize, &ret_errno);
1047
1048         if (rv == NS_SUCCESS)
1049                 return (0);
1050         else
1051                 return (ret_errno);
1052 }
1053
1054 void
1055 setservent(int stayopen)
1056 {
1057 #ifdef NS_CACHING
1058         static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
1059                 services, (void *)nss_lt_all,
1060                 NULL, NULL);
1061 #endif
1062         static const ns_dtab dtab[] = {
1063                 { NSSRC_FILES, files_setservent, (void *)SETSERVENT },
1064 #ifdef YP
1065                 { NSSRC_NIS, nis_setservent, (void *)SETSERVENT },
1066 #endif
1067                 { NSSRC_COMPAT, compat_setservent, (void *)SETSERVENT },
1068 #ifdef NS_CACHING
1069                 NS_CACHE_CB(&cache_info)
1070 #endif
1071                 { NULL, NULL, NULL }
1072         };
1073
1074         nsdispatch(NULL, dtab, NSDB_SERVICES, "setservent", defaultsrc,
1075             stayopen);
1076 }
1077
1078 void
1079 endservent(void)
1080 {
1081 #ifdef NS_CACHING
1082         static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
1083                 services, (void *)nss_lt_all,
1084                 NULL, NULL);
1085 #endif
1086         static const ns_dtab dtab[] = {
1087                 { NSSRC_FILES, files_setservent, (void *)ENDSERVENT },
1088 #ifdef YP
1089                 { NSSRC_NIS, nis_setservent, (void *)ENDSERVENT },
1090 #endif
1091                 { NSSRC_COMPAT, compat_setservent, (void *)ENDSERVENT },
1092 #ifdef NS_CACHING
1093                 NS_CACHE_CB(&cache_info)
1094 #endif
1095                 { NULL, NULL, NULL }
1096         };
1097
1098         nsdispatch(NULL, dtab, NSDB_SERVICES, "endservent", defaultsrc);
1099 }
1100
1101 /* get** wrappers for get**_r functions implementation */
1102 static void
1103 servent_endstate(void *p)
1104 {
1105         if (p == NULL)
1106                 return;
1107
1108         free(((struct servent_state *)p)->buffer);
1109         free(p);
1110 }
1111
1112 static int
1113 wrap_getservbyname_r(struct key key, struct servent *serv, char *buffer,
1114     size_t bufsize, struct servent **res)
1115 {
1116         return (getservbyname_r(key.name, key.proto, serv, buffer, bufsize,
1117             res));
1118 }
1119
1120 static int
1121 wrap_getservbyport_r(struct key key, struct servent *serv, char *buffer,
1122     size_t bufsize, struct servent **res)
1123 {
1124         return (getservbyport_r(key.port, key.proto, serv, buffer, bufsize,
1125             res));
1126 }
1127
1128 static  int
1129 wrap_getservent_r(struct key key, struct servent *serv, char *buffer,
1130     size_t bufsize, struct servent **res)
1131 {
1132         return (getservent_r(serv, buffer, bufsize, res));
1133 }
1134
1135 static struct servent *
1136 getserv(int (*fn)(struct key, struct servent *, char *, size_t,
1137     struct servent **), struct key key)
1138 {
1139         int rv;
1140         struct servent *res;
1141         struct servent_state * st;
1142
1143         rv = servent_getstate(&st);
1144         if (rv != 0) {
1145                 errno = rv;
1146                 return NULL;
1147         }
1148
1149         if (st->buffer == NULL) {
1150                 st->buffer = malloc(SERVENT_STORAGE_INITIAL);
1151                 if (st->buffer == NULL)
1152                         return (NULL);
1153                 st->bufsize = SERVENT_STORAGE_INITIAL;
1154         }
1155         do {
1156                 rv = fn(key, &st->serv, st->buffer, st->bufsize, &res);
1157                 if (res == NULL && rv == ERANGE) {
1158                         free(st->buffer);
1159                         if ((st->bufsize << 1) > SERVENT_STORAGE_MAX) {
1160                                 st->buffer = NULL;
1161                                 errno = ERANGE;
1162                                 return (NULL);
1163                         }
1164                         st->bufsize <<= 1;
1165                         st->buffer = malloc(st->bufsize);
1166                         if (st->buffer == NULL)
1167                                 return (NULL);
1168                 }
1169         } while (res == NULL && rv == ERANGE);
1170         if (rv != 0)
1171                 errno = rv;
1172
1173         return (res);
1174 }
1175
1176 struct servent *
1177 getservbyname(const char *name, const char *proto)
1178 {
1179         struct key key;
1180
1181         key.name = name;
1182         key.proto = proto;
1183
1184         return (getserv(wrap_getservbyname_r, key));
1185 }
1186
1187 struct servent *
1188 getservbyport(int port, const char *proto)
1189 {
1190         struct key key;
1191
1192         key.port = port;
1193         key.proto = proto;
1194
1195         return (getserv(wrap_getservbyport_r, key));
1196 }
1197
1198 struct servent *
1199 getservent(void)
1200 {
1201         struct key key;
1202
1203         key.proto = NULL;
1204         key.port = 0;
1205
1206         return (getserv(wrap_getservent_r, key));
1207 }