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