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