xref: /freebsd/lib/libc/net/getservent.c (revision a03411e84728e9b267056fd31c7d1d9d1dc1b01e)
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
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
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
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
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
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
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
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
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
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
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
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
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
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 int
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 int
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
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
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
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
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
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
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
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
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
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 *
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 *
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 *
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 *
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