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