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