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