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