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