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