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