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