1/*
2 * Copyright (c) 1983, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#if defined(LIBC_SCCS) && !defined(lint)
31static char sccsid[] = "@(#)getservent.c	8.1 (Berkeley) 6/4/93";
32#endif /* LIBC_SCCS and not lint */
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD$");
35
36#include <sys/param.h>
37#include <sys/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
63enum 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
71struct servent_mdata
72{
73	enum nss_lookup_type how;
74	int compat_mode;
75};
76
77static const ns_src defaultsrc[] = {
78	{ NSSRC_COMPAT, NS_SUCCESS },
79	{ NULL, 0 }
80};
81
82static int servent_unpack(char *, struct servent *, char **, size_t, int *);
83
84/* files backend declarations */
85struct files_state
86{
87	FILE *fp;
88	int stayopen;
89
90	int compat_mode_active;
91};
92static void files_endstate(void *);
93NSS_TLS_HANDLING(files);
94
95static int files_servent(void *, void *, va_list);
96static int files_setservent(void *, void *, va_list);
97
98/* db backend declarations */
99struct db_state
100{
101        DB *db;
102	int stayopen;
103	int keynum;
104};
105static void db_endstate(void *);
106NSS_TLS_HANDLING(db);
107
108static int db_servent(void *, void *, va_list);
109static int db_setservent(void *, void *, va_list);
110
111#ifdef YP
112/* nis backend declarations */
113static 	int 	nis_servent(void *, void *, va_list);
114static 	int 	nis_setservent(void *, void *, va_list);
115
116struct nis_state
117{
118	int yp_stepping;
119	char yp_domain[MAXHOSTNAMELEN];
120	char *yp_key;
121	int yp_keylen;
122};
123static void nis_endstate(void *);
124NSS_TLS_HANDLING(nis);
125
126static int nis_servent(void *, void *, va_list);
127static int nis_setservent(void *, void *, va_list);
128#endif
129
130/* compat backend declarations */
131static int compat_setservent(void *, void *, va_list);
132
133/* get** wrappers for get**_r functions declarations */
134struct servent_state {
135	struct servent serv;
136	char *buffer;
137	size_t bufsize;
138};
139static	void	servent_endstate(void *);
140NSS_TLS_HANDLING(servent);
141
142struct key {
143	const char *proto;
144	union {
145		const char *name;
146		int port;
147	};
148};
149
150static int wrap_getservbyname_r(struct key, struct servent *, char *, size_t,
151    struct servent **);
152static int wrap_getservbyport_r(struct key, struct servent *, char *, size_t,
153    struct servent **);
154static int wrap_getservent_r(struct key, struct servent *, char *, size_t,
155    struct servent **);
156static struct servent *getserv(int (*fn)(struct key, struct servent *, char *,
157    size_t, struct servent **), struct key);
158
159#ifdef NS_CACHING
160static int serv_id_func(char *, size_t *, va_list, void *);
161static int serv_marshal_func(char *, size_t *, void *, va_list, void *);
162static int serv_unmarshal_func(char *, size_t, void *, va_list, void *);
163#endif
164
165static int
166servent_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
224static int
225parse_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 */
251static	void
252files_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 */
270static int
271files_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
436static int
437files_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)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 */
472static	void
473db_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
487static int
488db_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)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
603db_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
615static int
616db_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)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
650static 	void
651nis_endstate(void *p)
652{
653	if (p == NULL)
654		return;
655
656	free(((struct nis_state *)p)->yp_key);
657	free(p);
658}
659
660static int
661nis_servent(void *retval, void *mdata, va_list ap)
662{
663	char *resultbuf, *lastkey;
664	int resultbuflen;
665	char buf[YPMAXRECORD + 2];
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	how = (enum nss_lookup_type)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			snprintf(buf, sizeof(buf), "%s/%s", name, proto);
718			if (yp_match(st->yp_domain, "services.byname", buf,
719			    strlen(buf), &resultbuf, &resultbuflen)) {
720				rv = NS_NOTFOUND;
721				goto fin;
722			}
723			break;
724		case nss_lt_id:
725			snprintf(buf, sizeof(buf), "%d/%s", ntohs(port),
726			    proto);
727
728			/*
729			 * We have to be a little flexible
730			 * here. Ideally you're supposed to have both
731			 * a services.byname and a services.byport
732			 * map, but some systems have only
733			 * services.byname. FreeBSD cheats a little by
734			 * putting the services.byport information in
735			 * the same map as services.byname so that
736			 * either case will work. We allow for both
737			 * possibilities here: if there is no
738			 * services.byport map, we try services.byname
739			 * instead.
740			 */
741			rv = yp_match(st->yp_domain, "services.byport", buf,
742			    strlen(buf), &resultbuf, &resultbuflen);
743			if (rv) {
744				if (rv == YPERR_MAP) {
745					if (yp_match(st->yp_domain,
746					    "services.byname", buf,
747					    strlen(buf), &resultbuf,
748					    &resultbuflen)) {
749						rv = NS_NOTFOUND;
750						goto fin;
751					}
752				} else {
753					rv = NS_NOTFOUND;
754					goto fin;
755				}
756			}
757
758			break;
759		case nss_lt_all:
760			if (!st->yp_stepping) {
761				free(st->yp_key);
762				rv = yp_first(st->yp_domain, "services.byname",
763				    &st->yp_key, &st->yp_keylen, &resultbuf,
764				    &resultbuflen);
765				if (rv) {
766					rv = NS_NOTFOUND;
767					goto fin;
768				}
769				st->yp_stepping = 1;
770			} else {
771				lastkey = st->yp_key;
772				rv = yp_next(st->yp_domain, "services.byname",
773				    st->yp_key, st->yp_keylen, &st->yp_key,
774				    &st->yp_keylen, &resultbuf, &resultbuflen);
775				free(lastkey);
776				if (rv) {
777					st->yp_stepping = 0;
778					rv = NS_NOTFOUND;
779					goto fin;
780				}
781			}
782			break;
783		}
784
785		rv = parse_result(serv, buffer, bufsize, resultbuf,
786		    resultbuflen, errnop);
787		free(resultbuf);
788
789	} while (!(rv & NS_TERMINATE) && how == nss_lt_all);
790
791fin:
792	if (rv == NS_SUCCESS && retval != NULL)
793		*(struct servent **)retval = serv;
794
795	return (rv);
796}
797
798static int
799nis_setservent(void *result, void *mdata, va_list ap)
800{
801	struct nis_state *st;
802	int rv;
803
804	rv = nis_getstate(&st);
805	if (rv != 0)
806		return (NS_UNAVAIL);
807
808	switch ((enum constants)mdata) {
809	case SETSERVENT:
810	case ENDSERVENT:
811		free(st->yp_key);
812		st->yp_key = NULL;
813		st->yp_stepping = 0;
814		break;
815	default:
816		break;
817	}
818
819	return (NS_UNAVAIL);
820}
821#endif
822
823/* compat backend implementation */
824static int
825compat_setservent(void *retval, void *mdata, va_list ap)
826{
827	static const ns_src compat_src[] = {
828#ifdef YP
829		{ NSSRC_NIS, NS_SUCCESS },
830#endif
831		{ NULL, 0 }
832	};
833	ns_dtab compat_dtab[] = {
834		{ NSSRC_DB, db_setservent, mdata },
835#ifdef YP
836		{ NSSRC_NIS, nis_setservent, mdata },
837#endif
838		{ NULL, NULL, NULL }
839	};
840	int f;
841
842	(void)files_setservent(retval, mdata, ap);
843
844	switch ((enum constants)mdata) {
845	case SETSERVENT:
846		f = va_arg(ap,int);
847		(void)nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT,
848		    "setservent", compat_src, f);
849		break;
850	case ENDSERVENT:
851		(void)nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT,
852		    "endservent", compat_src);
853		break;
854	default:
855		break;
856	}
857
858	return (NS_UNAVAIL);
859}
860
861#ifdef NS_CACHING
862static int
863serv_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata)
864{
865	char *name;
866	char *proto;
867	int port;
868
869	size_t desired_size, size, size2;
870	enum nss_lookup_type lookup_type;
871	int res = NS_UNAVAIL;
872
873	lookup_type = (enum nss_lookup_type)cache_mdata;
874	switch (lookup_type) {
875	case nss_lt_name:
876		name = va_arg(ap, char *);
877		proto = va_arg(ap, char *);
878
879		size = strlen(name);
880		desired_size = sizeof(enum nss_lookup_type) + size + 1;
881		if (proto != NULL) {
882			size2 = strlen(proto);
883			desired_size += size2 + 1;
884		} else
885			size2 = 0;
886
887		if (desired_size > *buffer_size) {
888			res = NS_RETURN;
889			goto fin;
890		}
891
892		memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
893		memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1);
894
895		if (proto != NULL)
896			memcpy(buffer + sizeof(enum nss_lookup_type) + size + 1,
897			    proto, size2 + 1);
898
899		res = NS_SUCCESS;
900		break;
901	case nss_lt_id:
902		port = va_arg(ap, int);
903		proto = va_arg(ap, char *);
904
905		desired_size = sizeof(enum nss_lookup_type) + sizeof(int);
906		if (proto != NULL) {
907			size = strlen(proto);
908			desired_size += size + 1;
909		} else
910			size = 0;
911
912		if (desired_size > *buffer_size) {
913			res = NS_RETURN;
914			goto fin;
915		}
916
917		memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
918		memcpy(buffer + sizeof(enum nss_lookup_type), &port,
919		    sizeof(int));
920
921		if (proto != NULL)
922			memcpy(buffer + sizeof(enum nss_lookup_type) +
923			    sizeof(int), proto, size + 1);
924
925		res = NS_SUCCESS;
926		break;
927	default:
928		/* should be unreachable */
929		return (NS_UNAVAIL);
930	}
931
932fin:
933	*buffer_size = desired_size;
934	return (res);
935}
936
937int
938serv_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap,
939    void *cache_mdata)
940{
941	char *name;
942	char *proto;
943	int port;
944	struct servent *serv;
945	char *orig_buf;
946	size_t orig_buf_size;
947
948	struct servent new_serv;
949	size_t desired_size;
950	char **alias;
951	char *p;
952	size_t size;
953	size_t aliases_size;
954
955	switch ((enum nss_lookup_type)cache_mdata) {
956	case nss_lt_name:
957		name = va_arg(ap, char *);
958		proto = va_arg(ap, char *);
959		break;
960	case nss_lt_id:
961		port = va_arg(ap, int);
962		proto = va_arg(ap, char *);
963		break;
964	case nss_lt_all:
965		break;
966	default:
967		/* should be unreachable */
968		return (NS_UNAVAIL);
969	}
970
971	serv = va_arg(ap, struct servent *);
972	orig_buf = va_arg(ap, char *);
973	orig_buf_size = va_arg(ap, size_t);
974
975	desired_size = _ALIGNBYTES + sizeof(struct servent) + sizeof(char *);
976	if (serv->s_name != NULL)
977		desired_size += strlen(serv->s_name) + 1;
978	if (serv->s_proto != NULL)
979		desired_size += strlen(serv->s_proto) + 1;
980
981	aliases_size = 0;
982	if (serv->s_aliases != NULL) {
983		for (alias = serv->s_aliases; *alias; ++alias) {
984			desired_size += strlen(*alias) + 1;
985			++aliases_size;
986		}
987
988		desired_size += _ALIGNBYTES +
989		    sizeof(char *) * (aliases_size + 1);
990	}
991
992	if (*buffer_size < desired_size) {
993		/* this assignment is here for future use */
994		*buffer_size = desired_size;
995		return (NS_RETURN);
996	}
997
998	memcpy(&new_serv, serv, sizeof(struct servent));
999	memset(buffer, 0, desired_size);
1000
1001	*buffer_size = desired_size;
1002	p = buffer + sizeof(struct servent) + sizeof(char *);
1003	memcpy(buffer + sizeof(struct servent), &p, sizeof(char *));
1004	p = (char *)_ALIGN(p);
1005
1006	if (new_serv.s_name != NULL) {
1007		size = strlen(new_serv.s_name);
1008		memcpy(p, new_serv.s_name, size);
1009		new_serv.s_name = p;
1010		p += size + 1;
1011	}
1012
1013	if (new_serv.s_proto != NULL) {
1014		size = strlen(new_serv.s_proto);
1015		memcpy(p, new_serv.s_proto, size);
1016		new_serv.s_proto = p;
1017		p += size + 1;
1018	}
1019
1020	if (new_serv.s_aliases != NULL) {
1021		p = (char *)_ALIGN(p);
1022		memcpy(p, new_serv.s_aliases, sizeof(char *) * aliases_size);
1023		new_serv.s_aliases = (char **)p;
1024		p += sizeof(char *) * (aliases_size + 1);
1025
1026		for (alias = new_serv.s_aliases; *alias; ++alias) {
1027			size = strlen(*alias);
1028			memcpy(p, *alias, size);
1029			*alias = p;
1030			p += size + 1;
1031		}
1032	}
1033
1034	memcpy(buffer, &new_serv, sizeof(struct servent));
1035	return (NS_SUCCESS);
1036}
1037
1038int
1039serv_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap,
1040    void *cache_mdata)
1041{
1042	char *name;
1043	char *proto;
1044	int port;
1045	struct servent *serv;
1046	char *orig_buf;
1047	char *p;
1048	char **alias;
1049	size_t orig_buf_size;
1050	int *ret_errno;
1051
1052	switch ((enum nss_lookup_type)cache_mdata) {
1053	case nss_lt_name:
1054		name = va_arg(ap, char *);
1055		proto = va_arg(ap, char *);
1056		break;
1057	case nss_lt_id:
1058		port = va_arg(ap, int);
1059		proto = va_arg(ap, char *);
1060		break;
1061	case nss_lt_all:
1062		break;
1063	default:
1064		/* should be unreachable */
1065		return (NS_UNAVAIL);
1066	}
1067
1068	serv = va_arg(ap, struct servent *);
1069	orig_buf = va_arg(ap, char *);
1070	orig_buf_size = va_arg(ap, size_t);
1071	ret_errno = va_arg(ap, int *);
1072
1073	if (orig_buf_size <
1074	    buffer_size - sizeof(struct servent) - sizeof(char *)) {
1075		*ret_errno = ERANGE;
1076		return (NS_RETURN);
1077	}
1078
1079	memcpy(serv, buffer, sizeof(struct servent));
1080	memcpy(&p, buffer + sizeof(struct servent), sizeof(char *));
1081
1082	orig_buf = (char *)_ALIGN(orig_buf);
1083	memcpy(orig_buf, buffer + sizeof(struct servent) + sizeof(char *) +
1084	    (_ALIGN(p) - (size_t)p),
1085	    buffer_size - sizeof(struct servent) - sizeof(char *) -
1086	    (_ALIGN(p) - (size_t)p));
1087	p = (char *)_ALIGN(p);
1088
1089	NS_APPLY_OFFSET(serv->s_name, orig_buf, p, char *);
1090	NS_APPLY_OFFSET(serv->s_proto, orig_buf, p, char *);
1091	if (serv->s_aliases != NULL) {
1092		NS_APPLY_OFFSET(serv->s_aliases, orig_buf, p, char **);
1093
1094		for (alias = serv->s_aliases; *alias; ++alias)
1095			NS_APPLY_OFFSET(*alias, orig_buf, p, char *);
1096	}
1097
1098	if (retval != NULL)
1099		*((struct servent **)retval) = serv;
1100	return (NS_SUCCESS);
1101}
1102
1103NSS_MP_CACHE_HANDLING(services);
1104#endif /* NS_CACHING */
1105
1106/* get**_r functions implementation */
1107int
1108getservbyname_r(const char *name, const char *proto, struct servent *serv,
1109    char *buffer, size_t bufsize, struct servent **result)
1110{
1111	static const struct servent_mdata mdata = { nss_lt_name, 0 };
1112	static const struct servent_mdata compat_mdata = { nss_lt_name, 1 };
1113#ifdef NS_CACHING
1114	static const nss_cache_info cache_info =
1115	NS_COMMON_CACHE_INFO_INITIALIZER(
1116		services, (void *)nss_lt_name,
1117		serv_id_func, serv_marshal_func, serv_unmarshal_func);
1118#endif /* NS_CACHING */
1119	static const ns_dtab dtab[] = {
1120		{ NSSRC_FILES, files_servent, (void *)&mdata },
1121		{ NSSRC_DB, db_servent, (void *)nss_lt_name },
1122#ifdef YP
1123		{ NSSRC_NIS, nis_servent, (void *)nss_lt_name },
1124#endif
1125		{ NSSRC_COMPAT, files_servent, (void *)&compat_mdata },
1126#ifdef NS_CACHING
1127		NS_CACHE_CB(&cache_info)
1128#endif
1129		{ NULL, NULL, NULL }
1130	};
1131	int	rv, ret_errno;
1132
1133	ret_errno = 0;
1134	*result = NULL;
1135	rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyname_r",
1136	    defaultsrc, name, proto, serv, buffer, bufsize, &ret_errno);
1137
1138	if (rv == NS_SUCCESS)
1139		return (0);
1140	else
1141		return (ret_errno);
1142}
1143
1144int
1145getservbyport_r(int port, const char *proto, struct servent *serv,
1146    char *buffer, size_t bufsize, struct servent **result)
1147{
1148	static const struct servent_mdata mdata = { nss_lt_id, 0 };
1149	static const struct servent_mdata compat_mdata = { nss_lt_id, 1 };
1150#ifdef NS_CACHING
1151	static const nss_cache_info cache_info =
1152	NS_COMMON_CACHE_INFO_INITIALIZER(
1153		services, (void *)nss_lt_id,
1154		serv_id_func, serv_marshal_func, serv_unmarshal_func);
1155#endif
1156	static const ns_dtab dtab[] = {
1157		{ NSSRC_FILES, files_servent, (void *)&mdata },
1158		{ NSSRC_DB, db_servent, (void *)nss_lt_id },
1159#ifdef YP
1160		{ NSSRC_NIS, nis_servent, (void *)nss_lt_id },
1161#endif
1162		{ NSSRC_COMPAT, files_servent, (void *)&compat_mdata },
1163#ifdef NS_CACHING
1164		NS_CACHE_CB(&cache_info)
1165#endif
1166		{ NULL, NULL, NULL }
1167	};
1168	int rv, ret_errno;
1169
1170	ret_errno = 0;
1171	*result = NULL;
1172	rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyport_r",
1173	    defaultsrc, port, proto, serv, buffer, bufsize, &ret_errno);
1174
1175	if (rv == NS_SUCCESS)
1176		return (0);
1177	else
1178		return (ret_errno);
1179}
1180
1181int
1182getservent_r(struct servent *serv, char *buffer, size_t bufsize,
1183    struct servent **result)
1184{
1185	static const struct servent_mdata mdata = { nss_lt_all, 0 };
1186	static const struct servent_mdata compat_mdata = { nss_lt_all, 1 };
1187#ifdef NS_CACHING
1188	static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
1189		services, (void *)nss_lt_all,
1190		serv_marshal_func, serv_unmarshal_func);
1191#endif
1192	static const ns_dtab dtab[] = {
1193		{ NSSRC_FILES, files_servent, (void *)&mdata },
1194		{ NSSRC_DB, db_servent, (void *)nss_lt_all },
1195#ifdef YP
1196		{ NSSRC_NIS, nis_servent, (void *)nss_lt_all },
1197#endif
1198		{ NSSRC_COMPAT, files_servent, (void *)&compat_mdata },
1199#ifdef NS_CACHING
1200		NS_CACHE_CB(&cache_info)
1201#endif
1202		{ NULL, NULL, NULL }
1203	};
1204	int rv, ret_errno;
1205
1206	ret_errno = 0;
1207	*result = NULL;
1208	rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservent_r",
1209	    defaultsrc, serv, buffer, bufsize, &ret_errno);
1210
1211	if (rv == NS_SUCCESS)
1212		return (0);
1213	else
1214		return (ret_errno);
1215}
1216
1217void
1218setservent(int stayopen)
1219{
1220#ifdef NS_CACHING
1221	static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
1222		services, (void *)nss_lt_all,
1223		NULL, NULL);
1224#endif
1225	static const ns_dtab dtab[] = {
1226		{ NSSRC_FILES, files_setservent, (void *)SETSERVENT },
1227		{ NSSRC_DB, db_setservent, (void *)SETSERVENT },
1228#ifdef YP
1229		{ NSSRC_NIS, nis_setservent, (void *)SETSERVENT },
1230#endif
1231		{ NSSRC_COMPAT, compat_setservent, (void *)SETSERVENT },
1232#ifdef NS_CACHING
1233		NS_CACHE_CB(&cache_info)
1234#endif
1235		{ NULL, NULL, NULL }
1236	};
1237
1238	(void)nsdispatch(NULL, dtab, NSDB_SERVICES, "setservent", defaultsrc,
1239	    stayopen);
1240}
1241
1242void
1243endservent(void)
1244{
1245#ifdef NS_CACHING
1246	static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
1247		services, (void *)nss_lt_all,
1248		NULL, NULL);
1249#endif
1250	static const ns_dtab dtab[] = {
1251		{ NSSRC_FILES, files_setservent, (void *)ENDSERVENT },
1252		{ NSSRC_DB, db_setservent, (void *)ENDSERVENT },
1253#ifdef YP
1254		{ NSSRC_NIS, nis_setservent, (void *)ENDSERVENT },
1255#endif
1256		{ NSSRC_COMPAT, compat_setservent, (void *)ENDSERVENT },
1257#ifdef NS_CACHING
1258		NS_CACHE_CB(&cache_info)
1259#endif
1260		{ NULL, NULL, NULL }
1261	};
1262
1263	(void)nsdispatch(NULL, dtab, NSDB_SERVICES, "endservent", defaultsrc);
1264}
1265
1266/* get** wrappers for get**_r functions implementation */
1267static void
1268servent_endstate(void *p)
1269{
1270	if (p == NULL)
1271		return;
1272
1273	free(((struct servent_state *)p)->buffer);
1274	free(p);
1275}
1276
1277static int
1278wrap_getservbyname_r(struct key key, struct servent *serv, char *buffer,
1279    size_t bufsize, struct servent **res)
1280{
1281	return (getservbyname_r(key.name, key.proto, serv, buffer, bufsize,
1282	    res));
1283}
1284
1285static int
1286wrap_getservbyport_r(struct key key, struct servent *serv, char *buffer,
1287    size_t bufsize, struct servent **res)
1288{
1289	return (getservbyport_r(key.port, key.proto, serv, buffer, bufsize,
1290	    res));
1291}
1292
1293static	int
1294wrap_getservent_r(struct key key, struct servent *serv, char *buffer,
1295    size_t bufsize, struct servent **res)
1296{
1297	return (getservent_r(serv, buffer, bufsize, res));
1298}
1299
1300static struct servent *
1301getserv(int (*fn)(struct key, struct servent *, char *, size_t,
1302    struct servent **), struct key key)
1303{
1304	int rv;
1305	struct servent *res;
1306	struct servent_state * st;
1307
1308	rv = servent_getstate(&st);
1309	if (rv != 0) {
1310		errno = rv;
1311		return NULL;
1312	}
1313
1314	if (st->buffer == NULL) {
1315		st->buffer = malloc(SERVENT_STORAGE_INITIAL);
1316		if (st->buffer == NULL)
1317			return (NULL);
1318		st->bufsize = SERVENT_STORAGE_INITIAL;
1319	}
1320	do {
1321		rv = fn(key, &st->serv, st->buffer, st->bufsize, &res);
1322		if (res == NULL && rv == ERANGE) {
1323			free(st->buffer);
1324			if ((st->bufsize << 1) > SERVENT_STORAGE_MAX) {
1325				st->buffer = NULL;
1326				errno = ERANGE;
1327				return (NULL);
1328			}
1329			st->bufsize <<= 1;
1330			st->buffer = malloc(st->bufsize);
1331			if (st->buffer == NULL)
1332				return (NULL);
1333		}
1334	} while (res == NULL && rv == ERANGE);
1335	if (rv != 0)
1336		errno = rv;
1337
1338	return (res);
1339}
1340
1341struct servent *
1342getservbyname(const char *name, const char *proto)
1343{
1344	struct key key;
1345
1346	key.name = name;
1347	key.proto = proto;
1348
1349	return (getserv(wrap_getservbyname_r, key));
1350}
1351
1352struct servent *
1353getservbyport(int port, const char *proto)
1354{
1355	struct key key;
1356
1357	key.port = port;
1358	key.proto = proto;
1359
1360	return (getserv(wrap_getservbyport_r, key));
1361}
1362
1363struct servent *
1364getservent(void)
1365{
1366	struct key key;
1367
1368	key.proto = NULL;
1369	key.port = 0;
1370
1371	return (getserv(wrap_getservent_r, key));
1372}
1373