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