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