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