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