xref: /freebsd/lib/libc/net/getservent.c (revision aea86367e8facf0990be2a2f7b0e5d9a61176fad)
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=='+' && serv_mdata->compat_mode != 0)
319 				st->compat_mode_active = 1;
320 		}
321 
322 		if (st->compat_mode_active != 0) {
323 			switch (serv_mdata->how) {
324 			case nss_lt_name:
325 				rv = nsdispatch(retval, compat_dtab,
326 				    NSDB_SERVICES_COMPAT, "getservbyname_r",
327 				    compat_src, name, proto, serv, buffer,
328 				    bufsize, errnop);
329 				break;
330 			case nss_lt_id:
331 				rv = nsdispatch(retval, compat_dtab,
332 				    NSDB_SERVICES_COMPAT, "getservbyport_r",
333 				    compat_src, port, proto, serv, buffer,
334 					bufsize, errnop);
335 				break;
336 			case nss_lt_all:
337 				rv = nsdispatch(retval, compat_dtab,
338 				    NSDB_SERVICES_COMPAT, "getservent_r",
339 				    compat_src, serv, buffer, bufsize, errnop);
340 				break;
341 			}
342 
343 			if (!(rv & NS_TERMINATE) ||
344 			    serv_mdata->how != nss_lt_all)
345 				st->compat_mode_active = 0;
346 
347 			continue;
348 		}
349 
350 		if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) {
351 			*errnop = ERANGE;
352 			rv = NS_RETURN;
353 			break;
354 		}
355 		aliases = (char **)_ALIGN(&buffer[linesize + 1]);
356 		aliases_size = (buffer + bufsize -
357 		    (char *)aliases) / sizeof(char *);
358 		if (aliases_size < 1) {
359 			*errnop = ERANGE;
360 			rv = NS_RETURN;
361 			break;
362 		}
363 
364 		memcpy(buffer, line, linesize);
365 		buffer[linesize] = '\0';
366 
367 		rv = servent_unpack(buffer, serv, aliases, aliases_size,
368 		    errnop);
369 		if (rv !=0 ) {
370 			if (*errnop == 0) {
371 				rv = NS_NOTFOUND;
372 				continue;
373 			}
374 			else {
375 				rv = NS_RETURN;
376 				break;
377 			}
378 		}
379 
380 		rv = NS_NOTFOUND;
381 		switch (serv_mdata->how) {
382 		case nss_lt_name:
383 			if (strcmp(name, serv->s_name) == 0)
384 				goto gotname;
385 			for (cp = serv->s_aliases; *cp; cp++)
386 				if (strcmp(name, *cp) == 0)
387 					goto gotname;
388 
389 			continue;
390 		gotname:
391 			if (proto == 0 || strcmp(serv->s_proto, proto) == 0)
392 				rv = NS_SUCCESS;
393 			break;
394 		case nss_lt_id:
395 			if (port != serv->s_port)
396 				continue;
397 
398 			if (proto == 0 || strcmp(serv->s_proto, proto) == 0)
399 				rv = NS_SUCCESS;
400 			break;
401 		case nss_lt_all:
402 			rv = NS_SUCCESS;
403 			break;
404 		}
405 
406 	} while (!(rv & NS_TERMINATE));
407 
408 	if (!stayopen && st->fp != NULL) {
409 		fclose(st->fp);
410 		st->fp = NULL;
411 	}
412 
413 	if ((rv == NS_SUCCESS) && (retval != NULL))
414 		*(struct servent **)retval=serv;
415 
416 	return (rv);
417 }
418 
419 static int
420 files_setservent(void *retval, void *mdata, va_list ap)
421 {
422 	struct files_state *st;
423 	int rv;
424 	int f;
425 
426 	rv = files_getstate(&st);
427 	if (rv != 0)
428 		return (NS_UNAVAIL);
429 
430 	switch ((enum constants)mdata) {
431 	case SETSERVENT:
432 		f = va_arg(ap,int);
433 		if (st->fp == NULL)
434 			st->fp = fopen(_PATH_SERVICES, "r");
435 		else
436 			rewind(st->fp);
437 		st->stayopen |= f;
438 		break;
439 	case ENDSERVENT:
440 		if (st->fp != NULL) {
441 			fclose(st->fp);
442 			st->fp = NULL;
443 		}
444 		st->stayopen = 0;
445 		break;
446 	default:
447 		break;
448 	};
449 
450 	st->compat_mode_active = 0;
451 	return (NS_UNAVAIL);
452 }
453 
454 /* nis backend implementation */
455 #ifdef YP
456 static 	void
457 nis_endstate(void *p)
458 {
459 	if (p == NULL)
460 		return;
461 
462 	free(((struct nis_state *)p)->yp_key);
463 	free(p);
464 }
465 
466 static int
467 nis_servent(void *retval, void *mdata, va_list ap)
468 {
469 	char *resultbuf, *lastkey;
470 	int resultbuflen;
471 	char buf[YPMAXRECORD + 2];
472 
473 	struct nis_state *st;
474 	int rv;
475 
476 	enum nss_lookup_type how;
477 	char *name;
478 	char *proto;
479 	int port;
480 
481 	struct servent *serv;
482 	char *buffer;
483 	size_t bufsize;
484 	int *errnop;
485 
486 	char **aliases;
487 	int aliases_size;
488 
489 	name = NULL;
490 	proto = NULL;
491 	how = (enum nss_lookup_type)mdata;
492 	switch (how) {
493 	case nss_lt_name:
494 		name = va_arg(ap, char *);
495 		proto = va_arg(ap, char *);
496 		break;
497 	case nss_lt_id:
498 		port = va_arg(ap, int);
499 		proto = va_arg(ap, char *);
500 		break;
501 	case nss_lt_all:
502 		break;
503 	default:
504 		return NS_NOTFOUND;
505 	};
506 
507 	serv = va_arg(ap, struct servent *);
508 	buffer  = va_arg(ap, char *);
509 	bufsize = va_arg(ap, size_t);
510 	errnop = va_arg(ap, int *);
511 
512 	*errnop = nis_getstate(&st);
513 	if (*errnop != 0)
514 		return (NS_UNAVAIL);
515 
516 	if (st->yp_domain[0] == '\0') {
517 		if (getdomainname(st->yp_domain, sizeof st->yp_domain)) {
518 			*errnop = errno;
519 			return (NS_UNAVAIL);
520 		}
521 	}
522 
523 	do {
524 		switch (how) {
525 		case nss_lt_name:
526 			snprintf(buf, sizeof(buf), "%s/%s", name, proto);
527 			if (yp_match(st->yp_domain, "services.byname", buf,
528 			    strlen(buf), &resultbuf, &resultbuflen)) {
529 				rv = NS_NOTFOUND;
530 				goto fin;
531 			}
532 			break;
533 		case nss_lt_id:
534 			snprintf(buf, sizeof(buf), "%d/%s", ntohs(port),
535 			    proto);
536 
537 			/*
538 			 * We have to be a little flexible
539 			 * here. Ideally you're supposed to have both
540 			 * a services.byname and a services.byport
541 			 * map, but some systems have only
542 			 * services.byname. FreeBSD cheats a little by
543 			 * putting the services.byport information in
544 			 * the same map as services.byname so that
545 			 * either case will work. We allow for both
546 			 * possibilities here: if there is no
547 			 * services.byport map, we try services.byname
548 			 * instead.
549 			 */
550 			rv = yp_match(st->yp_domain, "services.byport", buf,
551 			    strlen(buf), &resultbuf, &resultbuflen);
552 			if (rv) {
553 				if (rv == YPERR_MAP) {
554 					if (yp_match(st->yp_domain,
555 					    "services.byname", buf,
556 					    strlen(buf), &resultbuf,
557 					    &resultbuflen)) {
558 						rv = NS_NOTFOUND;
559 						goto fin;
560 					}
561 				} else {
562 					rv = NS_NOTFOUND;
563 					goto fin;
564 				}
565 			}
566 
567 			break;
568 		case nss_lt_all:
569 			if (!st->yp_stepping) {
570 				free(st->yp_key);
571 				rv = yp_first(st->yp_domain, "services.byname",
572 				    &st->yp_key, &st->yp_keylen, &resultbuf,
573 				    &resultbuflen);
574 				if (rv) {
575 					rv = NS_NOTFOUND;
576 					goto fin;
577 				}
578 				st->yp_stepping = 1;
579 			} else {
580 				lastkey = st->yp_key;
581 				rv = yp_next(st->yp_domain, "services.byname",
582 				    st->yp_key, st->yp_keylen, &st->yp_key,
583 				    &st->yp_keylen, &resultbuf, &resultbuflen);
584 				free(lastkey);
585 				if (rv) {
586 					st->yp_stepping = 0;
587 					rv = NS_NOTFOUND;
588 					goto fin;
589 				}
590 			}
591 			break;
592 		};
593 
594 		if (bufsize <= resultbuflen + _ALIGNBYTES + sizeof(char *)) {
595 			*errnop = ERANGE;
596 			rv = NS_RETURN;
597 			break;
598 		}
599 
600 		aliases = (char **)_ALIGN(&buffer[resultbuflen + 1]);
601 		aliases_size =
602 		    (buffer + bufsize - (char *)aliases) / sizeof(char *);
603 		if (aliases_size < 1) {
604 			*errnop = ERANGE;
605 			rv = NS_RETURN;
606 			break;
607 		}
608 
609 		memcpy(buffer, resultbuf, resultbuflen);
610 		buffer[resultbuflen] = '\0';
611 
612 		if (servent_unpack(buffer, serv, aliases, aliases_size,
613 		    errnop) != 0) {
614 			if (*errnop == 0)
615 				rv = NS_NOTFOUND;
616 			else
617 				rv = NS_RETURN;
618 		} else
619 			rv = NS_SUCCESS;
620 		free(resultbuf);
621 
622 	} while (!(rv & NS_TERMINATE) && how == nss_lt_all);
623 
624 fin:
625 	if (rv == NS_SUCCESS && retval != NULL)
626 		*(struct servent **)retval = serv;
627 
628 	return (rv);
629 }
630 
631 static int
632 nis_setservent(void *result, void *mdata, va_list ap)
633 {
634 	struct nis_state *st;
635 	int rv;
636 
637 	rv = nis_getstate(&st);
638 	if (rv != 0)
639 		return (NS_UNAVAIL);
640 
641 	switch ((enum constants)mdata) {
642 	case SETSERVENT:
643 	case ENDSERVENT:
644 		free(st->yp_key);
645 		st->yp_key = NULL;
646 		st->yp_stepping = 0;
647 		break;
648 	default:
649 		break;
650 	};
651 
652 	return (NS_UNAVAIL);
653 }
654 #endif
655 
656 /* compat backend implementation */
657 static int
658 compat_setservent(void *retval, void *mdata, va_list ap)
659 {
660 	static const ns_src compat_src[] = {
661 #ifdef YP
662 		{ NSSRC_NIS, NS_SUCCESS },
663 #endif
664 		{ NULL, 0 }
665 	};
666 	ns_dtab compat_dtab[] = {
667 #ifdef YP
668 		{ NSSRC_NIS, nis_setservent, mdata },
669 #endif
670 		{ NULL, NULL, NULL }
671 	};
672 	int f;
673 
674 	(void)files_setservent(retval, mdata, ap);
675 
676 	switch ((enum constants)mdata) {
677 	case SETSERVENT:
678 		f = va_arg(ap,int);
679 		(void)nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT,
680 		    "setservent", compat_src, f);
681 		break;
682 	case ENDSERVENT:
683 		(void)nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT,
684 		    "endservent", compat_src);
685 		break;
686 	default:
687 		break;
688 	}
689 
690 	return (NS_UNAVAIL);
691 }
692 
693 #ifdef NS_CACHING
694 static int
695 serv_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata)
696 {
697 	char *name;
698 	char *proto;
699 	int port;
700 
701 	size_t desired_size, size, size2;
702 	enum nss_lookup_type lookup_type;
703 	int res = NS_UNAVAIL;
704 
705 	lookup_type = (enum nss_lookup_type)cache_mdata;
706 	switch (lookup_type) {
707 	case nss_lt_name:
708 		name = va_arg(ap, char *);
709 		proto = va_arg(ap, char *);
710 
711 		size = strlen(name);
712 		desired_size = sizeof(enum nss_lookup_type) + size + 1;
713 		if (proto != NULL) {
714 			size2 = strlen(proto);
715 			desired_size += size2 + 1;
716 		} else
717 			size2 = 0;
718 
719 		if (desired_size > *buffer_size) {
720 			res = NS_RETURN;
721 			goto fin;
722 		}
723 
724 		memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
725 		memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1);
726 
727 		if (proto != NULL)
728 			memcpy(buffer + sizeof(enum nss_lookup_type) + size + 1,
729 			    proto, size2 + 1);
730 
731 		res = NS_SUCCESS;
732 		break;
733 	case nss_lt_id:
734 		port = va_arg(ap, int);
735 		proto = va_arg(ap, char *);
736 
737 		desired_size = sizeof(enum nss_lookup_type) + sizeof(int);
738 		if (proto != NULL) {
739 			size = strlen(proto);
740 			desired_size += size + 1;
741 		} else
742 			size = 0;
743 
744 		if (desired_size > *buffer_size) {
745 			res = NS_RETURN;
746 			goto fin;
747 		}
748 
749 		memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
750 		memcpy(buffer + sizeof(enum nss_lookup_type), &port,
751 		    sizeof(int));
752 
753 		if (proto != NULL)
754 			memcpy(buffer + sizeof(enum nss_lookup_type) +
755 			    sizeof(int), proto, size + 1);
756 
757 		res = NS_SUCCESS;
758 		break;
759 	default:
760 		/* should be unreachable */
761 		return (NS_UNAVAIL);
762 	}
763 
764 fin:
765 	*buffer_size = desired_size;
766 	return (res);
767 }
768 
769 int
770 serv_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap,
771     void *cache_mdata)
772 {
773 	char *name;
774 	char *proto;
775 	int port;
776 	struct servent *serv;
777 	char *orig_buf;
778 	size_t orig_buf_size;
779 
780 	struct servent new_serv;
781 	size_t desired_size;
782 	char **alias;
783 	char *p;
784 	size_t size;
785 	size_t aliases_size;
786 
787 	switch ((enum nss_lookup_type)cache_mdata) {
788 	case nss_lt_name:
789 		name = va_arg(ap, char *);
790 		proto = va_arg(ap, char *);
791 		break;
792 	case nss_lt_id:
793 		port = va_arg(ap, int);
794 		proto = va_arg(ap, char *);
795 		break;
796 	case nss_lt_all:
797 		break;
798 	default:
799 		/* should be unreachable */
800 		return (NS_UNAVAIL);
801 	}
802 
803 	serv = va_arg(ap, struct servent *);
804 	orig_buf = va_arg(ap, char *);
805 	orig_buf_size = va_arg(ap, size_t);
806 
807 	desired_size = _ALIGNBYTES + sizeof(struct servent) + sizeof(char *);
808 	if (serv->s_name != NULL)
809 		desired_size += strlen(serv->s_name) + 1;
810 	if (serv->s_proto != NULL)
811 		desired_size += strlen(serv->s_proto) + 1;
812 
813 	aliases_size = 0;
814 	if (serv->s_aliases != NULL) {
815 		for (alias = serv->s_aliases; *alias; ++alias) {
816 			desired_size += strlen(*alias) + 1;
817 			++aliases_size;
818 		}
819 
820 		desired_size += _ALIGNBYTES +
821 		    sizeof(char *) * (aliases_size + 1);
822 	}
823 
824 	if (*buffer_size < desired_size) {
825 		/* this assignment is here for future use */
826 		*buffer_size = desired_size;
827 		return (NS_RETURN);
828 	}
829 
830 	memcpy(&new_serv, serv, sizeof(struct servent));
831 	memset(buffer, 0, desired_size);
832 
833 	*buffer_size = desired_size;
834 	p = buffer + sizeof(struct servent) + sizeof(char *);
835 	memcpy(buffer + sizeof(struct servent), &p, sizeof(char *));
836 	p = (char *)_ALIGN(p);
837 
838 	if (new_serv.s_name != NULL) {
839 		size = strlen(new_serv.s_name);
840 		memcpy(p, new_serv.s_name, size);
841 		new_serv.s_name = p;
842 		p += size + 1;
843 	}
844 
845 	if (new_serv.s_proto != NULL) {
846 		size = strlen(new_serv.s_proto);
847 		memcpy(p, new_serv.s_proto, size);
848 		new_serv.s_proto = p;
849 		p += size + 1;
850 	}
851 
852 	if (new_serv.s_aliases != NULL) {
853 		p = (char *)_ALIGN(p);
854 		memcpy(p, new_serv.s_aliases, sizeof(char *) * aliases_size);
855 		new_serv.s_aliases = (char **)p;
856 		p += sizeof(char *) * (aliases_size + 1);
857 
858 		for (alias = new_serv.s_aliases; *alias; ++alias) {
859 			size = strlen(*alias);
860 			memcpy(p, *alias, size);
861 			*alias = p;
862 			p += size + 1;
863 		}
864 	}
865 
866 	memcpy(buffer, &new_serv, sizeof(struct servent));
867 	return (NS_SUCCESS);
868 }
869 
870 int
871 serv_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap,
872     void *cache_mdata)
873 {
874 	char *name;
875 	char *proto;
876 	int port;
877 	struct servent *serv;
878 	char *orig_buf;
879 	char *p;
880 	char **alias;
881 	size_t orig_buf_size;
882 	int *ret_errno;
883 
884 	switch ((enum nss_lookup_type)cache_mdata) {
885 	case nss_lt_name:
886 		name = va_arg(ap, char *);
887 		proto = va_arg(ap, char *);
888 		break;
889 	case nss_lt_id:
890 		port = va_arg(ap, int);
891 		proto = va_arg(ap, char *);
892 		break;
893 	case nss_lt_all:
894 		break;
895 	default:
896 		/* should be unreachable */
897 		return (NS_UNAVAIL);
898 	}
899 
900 	serv = va_arg(ap, struct servent *);
901 	orig_buf = va_arg(ap, char *);
902 	orig_buf_size = va_arg(ap, size_t);
903 	ret_errno = va_arg(ap, int *);
904 
905 	if (orig_buf_size <
906 	    buffer_size - sizeof(struct servent) - sizeof(char *)) {
907 		*ret_errno = ERANGE;
908 		return (NS_RETURN);
909 	}
910 
911 	memcpy(serv, buffer, sizeof(struct servent));
912 	memcpy(&p, buffer + sizeof(struct servent), sizeof(char *));
913 
914 	orig_buf = (char *)_ALIGN(orig_buf);
915 	memcpy(orig_buf, buffer + sizeof(struct servent) + sizeof(char *) +
916 	    (_ALIGN(p) - (size_t)p),
917 	    buffer_size - sizeof(struct servent) - sizeof(char *) -
918 	    (_ALIGN(p) - (size_t)p));
919 	p = (char *)_ALIGN(p);
920 
921 	NS_APPLY_OFFSET(serv->s_name, orig_buf, p, char *);
922 	NS_APPLY_OFFSET(serv->s_proto, orig_buf, p, char *);
923 	if (serv->s_aliases != NULL) {
924 		NS_APPLY_OFFSET(serv->s_aliases, orig_buf, p, char **);
925 
926 		for (alias = serv->s_aliases; *alias; ++alias)
927 			NS_APPLY_OFFSET(*alias, orig_buf, p, char *);
928 	}
929 
930 	if (retval != NULL)
931 		*((struct servent **)retval) = serv;
932 	return (NS_SUCCESS);
933 }
934 
935 NSS_MP_CACHE_HANDLING(services);
936 #endif /* NS_CACHING */
937 
938 /* get**_r functions implementation */
939 int
940 getservbyname_r(const char *name, const char *proto, struct servent *serv,
941     char *buffer, size_t bufsize, struct servent **result)
942 {
943 	static const struct servent_mdata mdata = { nss_lt_name, 0 };
944 	static const struct servent_mdata compat_mdata = { nss_lt_name, 1 };
945 #ifdef NS_CACHING
946 	static const nss_cache_info cache_info =
947 	NS_COMMON_CACHE_INFO_INITIALIZER(
948 		services, (void *)nss_lt_name,
949 		serv_id_func, serv_marshal_func, serv_unmarshal_func);
950 #endif /* NS_CACHING */
951 	static const ns_dtab dtab[] = {
952 		{ NSSRC_FILES, files_servent, (void *)&mdata },
953 #ifdef YP
954 		{ NSSRC_NIS, nis_servent, (void *)nss_lt_name },
955 #endif
956 		{ NSSRC_COMPAT, files_servent, (void *)&compat_mdata },
957 #ifdef NS_CACHING
958 		NS_CACHE_CB(&cache_info)
959 #endif
960 		{ NULL, NULL, NULL }
961 	};
962 	int	rv, ret_errno;
963 
964 	ret_errno = 0;
965 	*result = NULL;
966 	rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyname_r",
967 	    defaultsrc, name, proto, serv, buffer, bufsize, &ret_errno);
968 
969 	if (rv == NS_SUCCESS)
970 		return (0);
971 	else
972 		return (ret_errno);
973 }
974 
975 int
976 getservbyport_r(int port, const char *proto, struct servent *serv,
977     char *buffer, size_t bufsize, struct servent **result)
978 {
979 	static const struct servent_mdata mdata = { nss_lt_id, 0 };
980 	static const struct servent_mdata compat_mdata = { nss_lt_id, 1 };
981 #ifdef NS_CACHING
982 	static const nss_cache_info cache_info =
983 	NS_COMMON_CACHE_INFO_INITIALIZER(
984 		services, (void *)nss_lt_id,
985 		serv_id_func, serv_marshal_func, serv_unmarshal_func);
986 #endif
987 	static const ns_dtab dtab[] = {
988 		{ NSSRC_FILES, files_servent, (void *)&mdata },
989 #ifdef YP
990 		{ NSSRC_NIS, nis_servent, (void *)nss_lt_id },
991 #endif
992 		{ NSSRC_COMPAT, files_servent, (void *)&compat_mdata },
993 #ifdef NS_CACHING
994 		NS_CACHE_CB(&cache_info)
995 #endif
996 		{ NULL, NULL, NULL }
997 	};
998 	int rv, ret_errno;
999 
1000 	ret_errno = 0;
1001 	*result = NULL;
1002 	rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyport_r",
1003 	    defaultsrc, port, proto, serv, buffer, bufsize, &ret_errno);
1004 
1005 	if (rv == NS_SUCCESS)
1006 		return (0);
1007 	else
1008 		return (ret_errno);
1009 }
1010 
1011 int
1012 getservent_r(struct servent *serv, char *buffer, size_t bufsize,
1013     struct servent **result)
1014 {
1015 	static const struct servent_mdata mdata = { nss_lt_all, 0 };
1016 	static const struct servent_mdata compat_mdata = { nss_lt_all, 1 };
1017 #ifdef NS_CACHING
1018 	static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
1019 		services, (void *)nss_lt_all,
1020 		serv_marshal_func, serv_unmarshal_func);
1021 #endif
1022 	static const ns_dtab dtab[] = {
1023 		{ NSSRC_FILES, files_servent, (void *)&mdata },
1024 #ifdef YP
1025 		{ NSSRC_NIS, nis_servent, (void *)nss_lt_all },
1026 #endif
1027 		{ NSSRC_COMPAT, files_servent, (void *)&compat_mdata },
1028 #ifdef NS_CACHING
1029 		NS_CACHE_CB(&cache_info)
1030 #endif
1031 		{ NULL, NULL, NULL }
1032 	};
1033 	int rv, ret_errno;
1034 
1035 	ret_errno = 0;
1036 	*result = NULL;
1037 	rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservent_r",
1038 	    defaultsrc, serv, buffer, bufsize, &ret_errno);
1039 
1040 	if (rv == NS_SUCCESS)
1041 		return (0);
1042 	else
1043 		return (ret_errno);
1044 }
1045 
1046 void
1047 setservent(int stayopen)
1048 {
1049 #ifdef NS_CACHING
1050 	static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
1051 		services, (void *)nss_lt_all,
1052 		NULL, NULL);
1053 #endif
1054 	static const ns_dtab dtab[] = {
1055 		{ NSSRC_FILES, files_setservent, (void *)SETSERVENT },
1056 #ifdef YP
1057 		{ NSSRC_NIS, nis_setservent, (void *)SETSERVENT },
1058 #endif
1059 		{ NSSRC_COMPAT, compat_setservent, (void *)SETSERVENT },
1060 #ifdef NS_CACHING
1061 		NS_CACHE_CB(&cache_info)
1062 #endif
1063 		{ NULL, NULL, NULL }
1064 	};
1065 
1066 	(void)nsdispatch(NULL, dtab, NSDB_SERVICES, "setservent", defaultsrc,
1067 	    stayopen);
1068 }
1069 
1070 void
1071 endservent()
1072 {
1073 #ifdef NS_CACHING
1074 	static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
1075 		services, (void *)nss_lt_all,
1076 		NULL, NULL);
1077 #endif
1078 	static const ns_dtab dtab[] = {
1079 		{ NSSRC_FILES, files_setservent, (void *)ENDSERVENT },
1080 #ifdef YP
1081 		{ NSSRC_NIS, nis_setservent, (void *)ENDSERVENT },
1082 #endif
1083 		{ NSSRC_COMPAT, compat_setservent, (void *)ENDSERVENT },
1084 #ifdef NS_CACHING
1085 		NS_CACHE_CB(&cache_info)
1086 #endif
1087 		{ NULL, NULL, NULL }
1088 	};
1089 
1090 	(void)nsdispatch(NULL, dtab, NSDB_SERVICES, "endservent", defaultsrc);
1091 }
1092 
1093 /* get** wrappers for get**_r functions implementation */
1094 static void
1095 servent_endstate(void *p)
1096 {
1097 	if (p == NULL)
1098 		return;
1099 
1100 	free(((struct servent_state *)p)->buffer);
1101 	free(p);
1102 }
1103 
1104 static int
1105 wrap_getservbyname_r(struct key key, struct servent *serv, char *buffer,
1106     size_t bufsize, struct servent **res)
1107 {
1108 	return (getservbyname_r(key.name, key.proto, serv, buffer, bufsize,
1109 	    res));
1110 }
1111 
1112 static int
1113 wrap_getservbyport_r(struct key key, struct servent *serv, char *buffer,
1114     size_t bufsize, struct servent **res)
1115 {
1116 	return (getservbyport_r(key.port, key.proto, serv, buffer, bufsize,
1117 	    res));
1118 }
1119 
1120 static	int
1121 wrap_getservent_r(struct key key, struct servent *serv, char *buffer,
1122     size_t bufsize, struct servent **res)
1123 {
1124 	return (getservent_r(serv, buffer, bufsize, res));
1125 }
1126 
1127 static struct servent *
1128 getserv(int (*fn)(struct key, struct servent *, char *, size_t,
1129     struct servent **), struct key key)
1130 {
1131 	int rv;
1132 	struct servent *res;
1133 	struct servent_state * st;
1134 
1135 	rv = servent_getstate(&st);
1136 	if (rv != 0) {
1137 		errno = rv;
1138 		return NULL;
1139 	}
1140 
1141 	if (st->buffer == NULL) {
1142 		st->buffer = malloc(SERVENT_STORAGE_INITIAL);
1143 		if (st->buffer == NULL)
1144 			return (NULL);
1145 		st->bufsize = SERVENT_STORAGE_INITIAL;
1146 	}
1147 	do {
1148 		rv = fn(key, &st->serv, st->buffer, st->bufsize, &res);
1149 		if (res == NULL && rv == ERANGE) {
1150 			free(st->buffer);
1151 			if ((st->bufsize << 1) > SERVENT_STORAGE_MAX) {
1152 				st->buffer = NULL;
1153 				errno = ERANGE;
1154 				return (NULL);
1155 			}
1156 			st->bufsize <<= 1;
1157 			st->buffer = malloc(st->bufsize);
1158 			if (st->buffer == NULL)
1159 				return (NULL);
1160 		}
1161 	} while (res == NULL && rv == ERANGE);
1162 	if (rv != 0)
1163 		errno = rv;
1164 
1165 	return (res);
1166 }
1167 
1168 struct servent *
1169 getservbyname(const char *name, const char *proto)
1170 {
1171 	struct key key;
1172 
1173 	key.name = name;
1174 	key.proto = proto;
1175 
1176 	return (getserv(wrap_getservbyname_r, key));
1177 }
1178 
1179 struct servent *
1180 getservbyport(int port, const char *proto)
1181 {
1182 	struct key key;
1183 
1184 	key.port = port;
1185 	key.proto = proto;
1186 
1187 	return (getserv(wrap_getservbyport_r, key));
1188 }
1189 
1190 struct servent *
1191 getservent()
1192 {
1193 	struct key key;
1194 
1195 	key.proto = NULL;
1196 	key.port = 0;
1197 
1198 	return (getserv(wrap_getservent_r, key));
1199 }
1200