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