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