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