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