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)
31static char sccsid[] = "@(#)getprotoent.c	8.1 (Berkeley) 6/4/93";
32#endif /* LIBC_SCCS and not lint */
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: stable/11/lib/libc/net/getprotoent.c 360415 2020-04-27 23:49:13Z brooks $");
35
36#include <sys/param.h>
37#include <sys/socket.h>
38#include <errno.h>
39#include <limits.h>
40#include <netdb.h>
41#include <nsswitch.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include "namespace.h"
46#include "reentrant.h"
47#include "un-namespace.h"
48#include "netdb_private.h"
49#ifdef NS_CACHING
50#include "nscache.h"
51#endif
52#include "nss_tls.h"
53
54static const ns_src defaultsrc[] = {
55	{ NSSRC_FILES, NS_SUCCESS },
56	{ NULL, 0 }
57};
58
59NETDB_THREAD_ALLOC(protoent_data)
60NETDB_THREAD_ALLOC(protodata)
61
62static void
63protoent_data_clear(struct protoent_data *ped)
64{
65	if (ped->fp) {
66		fclose(ped->fp);
67		ped->fp = NULL;
68	}
69}
70
71static void
72protoent_data_free(void *ptr)
73{
74	struct protoent_data *ped = ptr;
75
76	protoent_data_clear(ped);
77	free(ped);
78}
79
80static void
81protodata_free(void *ptr)
82{
83	free(ptr);
84}
85
86#ifdef NS_CACHING
87int
88__proto_id_func(char *buffer, size_t *buffer_size, va_list ap,
89    void *cache_mdata)
90{
91	char *name;
92	int proto;
93
94	size_t desired_size, size;
95	enum nss_lookup_type lookup_type;
96	int res = NS_UNAVAIL;
97
98	lookup_type = (enum nss_lookup_type)(uintptr_t)cache_mdata;
99	switch (lookup_type) {
100	case nss_lt_name:
101		name = va_arg(ap, char *);
102
103		size = strlen(name);
104		desired_size = sizeof(enum nss_lookup_type) + size + 1;
105		if (desired_size > *buffer_size) {
106			res = NS_RETURN;
107			goto fin;
108		}
109
110		memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
111		memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1);
112
113		res = NS_SUCCESS;
114		break;
115	case nss_lt_id:
116		proto = va_arg(ap, int);
117
118		desired_size = sizeof(enum nss_lookup_type) + sizeof(int);
119		if (desired_size > *buffer_size) {
120			res = NS_RETURN;
121			goto fin;
122		}
123
124		memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
125		memcpy(buffer + sizeof(enum nss_lookup_type), &proto,
126			sizeof(int));
127
128		res = NS_SUCCESS;
129		break;
130	default:
131		/* should be unreachable */
132		return (NS_UNAVAIL);
133	}
134
135fin:
136	*buffer_size = desired_size;
137	return (res);
138}
139
140
141int
142__proto_marshal_func(char *buffer, size_t *buffer_size, void *retval,
143    va_list ap, void *cache_mdata)
144{
145	char *name;
146	int num;
147	struct protoent *proto;
148	char *orig_buf;
149	size_t orig_buf_size;
150
151	struct protoent new_proto;
152	size_t desired_size, size, aliases_size;
153	char *p;
154	char **alias;
155
156	switch ((enum nss_lookup_type)(uintptr_t)cache_mdata) {
157	case nss_lt_name:
158		name = va_arg(ap, char *);
159		break;
160	case nss_lt_id:
161		num = va_arg(ap, int);
162		break;
163	case nss_lt_all:
164		break;
165	default:
166		/* should be unreachable */
167		return (NS_UNAVAIL);
168	}
169
170	proto = va_arg(ap, struct protoent *);
171	orig_buf = va_arg(ap, char *);
172	orig_buf_size = va_arg(ap, size_t);
173
174	desired_size = _ALIGNBYTES + sizeof(struct protoent) + sizeof(char *);
175	if (proto->p_name != NULL)
176		desired_size += strlen(proto->p_name) + 1;
177
178	if (proto->p_aliases != NULL) {
179		aliases_size = 0;
180		for (alias = proto->p_aliases; *alias; ++alias) {
181			desired_size += strlen(*alias) + 1;
182			++aliases_size;
183		}
184
185		desired_size += _ALIGNBYTES + (aliases_size + 1) *
186		    sizeof(char *);
187	}
188
189	if (*buffer_size < desired_size) {
190		/* this assignment is here for future use */
191		*buffer_size = desired_size;
192		return (NS_RETURN);
193	}
194
195	memcpy(&new_proto, proto, sizeof(struct protoent));
196
197	*buffer_size = desired_size;
198	memset(buffer, 0, desired_size);
199	p = buffer + sizeof(struct protoent) + sizeof(char *);
200	memcpy(buffer + sizeof(struct protoent), &p, sizeof(char *));
201	p = (char *)_ALIGN(p);
202
203	if (new_proto.p_name != NULL) {
204		size = strlen(new_proto.p_name);
205		memcpy(p, new_proto.p_name, size);
206		new_proto.p_name = p;
207		p += size + 1;
208	}
209
210	if (new_proto.p_aliases != NULL) {
211		p = (char *)_ALIGN(p);
212		memcpy(p, new_proto.p_aliases, sizeof(char *) * aliases_size);
213		new_proto.p_aliases = (char **)p;
214		p += sizeof(char *) * (aliases_size + 1);
215
216		for (alias = new_proto.p_aliases; *alias; ++alias) {
217			size = strlen(*alias);
218			memcpy(p, *alias, size);
219			*alias = p;
220			p += size + 1;
221		}
222	}
223
224	memcpy(buffer, &new_proto, sizeof(struct protoent));
225	return (NS_SUCCESS);
226}
227
228int
229__proto_unmarshal_func(char *buffer, size_t buffer_size, void *retval,
230    va_list ap, void *cache_mdata)
231{
232	char *name;
233	int num;
234	struct protoent *proto;
235	char *orig_buf;
236	size_t orig_buf_size;
237	int *ret_errno;
238
239	char *p;
240	char **alias;
241
242	switch ((enum nss_lookup_type)(uintptr_t)cache_mdata) {
243	case nss_lt_name:
244		name = va_arg(ap, char *);
245		break;
246	case nss_lt_id:
247		num = va_arg(ap, int);
248		break;
249	case nss_lt_all:
250		break;
251	default:
252		/* should be unreachable */
253		return (NS_UNAVAIL);
254	}
255
256	proto = va_arg(ap, struct protoent *);
257	orig_buf = va_arg(ap, char *);
258	orig_buf_size = va_arg(ap, size_t);
259	ret_errno = va_arg(ap, int *);
260
261	if (orig_buf_size <
262	    buffer_size - sizeof(struct protoent) - sizeof(char *)) {
263		*ret_errno = ERANGE;
264		return (NS_RETURN);
265	}
266
267	memcpy(proto, buffer, sizeof(struct protoent));
268	memcpy(&p, buffer + sizeof(struct protoent), sizeof(char *));
269
270	orig_buf = (char *)_ALIGN(orig_buf);
271	memcpy(orig_buf, buffer + sizeof(struct protoent) + sizeof(char *) +
272	    _ALIGN(p) - (size_t)p,
273	    buffer_size - sizeof(struct protoent) - sizeof(char *) -
274	    _ALIGN(p) + (size_t)p);
275	p = (char *)_ALIGN(p);
276
277	NS_APPLY_OFFSET(proto->p_name, orig_buf, p, char *);
278	if (proto->p_aliases != NULL) {
279		NS_APPLY_OFFSET(proto->p_aliases, orig_buf, p, char **);
280
281		for (alias = proto->p_aliases; *alias; ++alias)
282			NS_APPLY_OFFSET(*alias, orig_buf, p, char *);
283	}
284
285	if (retval != NULL)
286		*((struct protoent **)retval) = proto;
287
288	return (NS_SUCCESS);
289}
290
291NSS_MP_CACHE_HANDLING(protocols);
292#endif /* NS_CACHING */
293
294int
295__copy_protoent(struct protoent *pe, struct protoent *pptr, char *buf,
296    size_t buflen)
297{
298	char *cp;
299	int i, n;
300	int numptr, len;
301
302	/* Find out the amount of space required to store the answer. */
303	numptr = 1; /* NULL ptr */
304	len = (char *)ALIGN(buf) - buf;
305	for (i = 0; pe->p_aliases[i]; i++, numptr++) {
306		len += strlen(pe->p_aliases[i]) + 1;
307	}
308	len += strlen(pe->p_name) + 1;
309	len += numptr * sizeof(char*);
310
311	if (len > (int)buflen) {
312		errno = ERANGE;
313		return (-1);
314	}
315
316	/* copy protocol value*/
317	pptr->p_proto = pe->p_proto;
318
319	cp = (char *)ALIGN(buf) + numptr * sizeof(char *);
320
321	/* copy official name */
322	n = strlen(pe->p_name) + 1;
323	strcpy(cp, pe->p_name);
324	pptr->p_name = cp;
325	cp += n;
326
327	/* copy aliases */
328	pptr->p_aliases = (char **)ALIGN(buf);
329	for (i = 0 ; pe->p_aliases[i]; i++) {
330		n = strlen(pe->p_aliases[i]) + 1;
331		strcpy(cp, pe->p_aliases[i]);
332		pptr->p_aliases[i] = cp;
333		cp += n;
334	}
335	pptr->p_aliases[i] = NULL;
336
337	return (0);
338}
339
340void
341__setprotoent_p(int f, struct protoent_data *ped)
342{
343	if (ped->fp == NULL)
344		ped->fp = fopen(_PATH_PROTOCOLS, "re");
345	else
346		rewind(ped->fp);
347	ped->stayopen |= f;
348}
349
350void
351__endprotoent_p(struct protoent_data *ped)
352{
353	if (ped->fp) {
354		fclose(ped->fp);
355		ped->fp = NULL;
356	}
357	ped->stayopen = 0;
358}
359
360int
361__getprotoent_p(struct protoent *pe, struct protoent_data *ped)
362{
363	char *p;
364	char *cp, **q, *endp;
365	long l;
366
367	if (ped->fp == NULL && (ped->fp = fopen(_PATH_PROTOCOLS, "re")) == NULL)
368		return (-1);
369again:
370	if ((p = fgets(ped->line, sizeof ped->line, ped->fp)) == NULL)
371		return (-1);
372	if (*p == '#')
373		goto again;
374	cp = strpbrk(p, "#\n");
375	if (cp != NULL)
376		*cp = '\0';
377	pe->p_name = p;
378	cp = strpbrk(p, " \t");
379	if (cp == NULL)
380		goto again;
381	*cp++ = '\0';
382	while (*cp == ' ' || *cp == '\t')
383		cp++;
384	p = strpbrk(cp, " \t");
385	if (p != NULL)
386		*p++ = '\0';
387	l = strtol(cp, &endp, 10);
388	if (endp == cp || *endp != '\0' || l < 0 || l > USHRT_MAX)
389		goto again;
390	pe->p_proto = l;
391	q = pe->p_aliases = ped->aliases;
392	if (p != NULL) {
393		cp = p;
394		while (cp && *cp) {
395			if (*cp == ' ' || *cp == '\t') {
396				cp++;
397				continue;
398			}
399			if (q < &ped->aliases[_MAXALIASES - 1])
400				*q++ = cp;
401			cp = strpbrk(cp, " \t");
402			if (cp != NULL)
403				*cp++ = '\0';
404		}
405	}
406	*q = NULL;
407	return (0);
408}
409
410static int
411files_getprotoent_r(void *retval, void *mdata, va_list ap)
412{
413	struct protoent pe;
414	struct protoent_data *ped;
415
416	struct protoent	*pptr;
417	char *buffer;
418	size_t buflen;
419	int *errnop;
420
421	pptr = va_arg(ap, struct protoent *);
422	buffer = va_arg(ap, char *);
423	buflen = va_arg(ap, size_t);
424	errnop = va_arg(ap, int *);
425
426	if ((ped = __protoent_data_init()) == NULL) {
427		*errnop = errno;
428		return (NS_NOTFOUND);
429	}
430
431	if (__getprotoent_p(&pe, ped) != 0) {
432		*errnop = errno;
433		return (NS_NOTFOUND);
434	}
435
436	if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) {
437		*errnop = errno;
438		return (NS_RETURN);
439	}
440
441	*((struct protoent **)retval) = pptr;
442	return (NS_SUCCESS);
443}
444
445static int
446files_setprotoent(void *retval, void *mdata, va_list ap)
447{
448	struct protoent_data *ped;
449	int f;
450
451	f = va_arg(ap, int);
452	if ((ped = __protoent_data_init()) == NULL)
453		return (NS_UNAVAIL);
454
455	__setprotoent_p(f, ped);
456	return (NS_UNAVAIL);
457}
458
459static int
460files_endprotoent(void *retval, void *mdata, va_list ap)
461{
462	struct protoent_data *ped;
463
464	if ((ped = __protoent_data_init()) == NULL)
465		return (NS_UNAVAIL);
466
467	__endprotoent_p(ped);
468	return (NS_UNAVAIL);
469}
470
471int
472getprotoent_r(struct protoent *pptr, char *buffer, size_t buflen,
473    struct protoent **result)
474{
475#ifdef NS_CACHING
476	static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
477		protocols, (void *)nss_lt_all,
478		__proto_marshal_func, __proto_unmarshal_func);
479#endif
480	static const ns_dtab dtab[] = {
481		{ NSSRC_FILES, files_getprotoent_r, (void *)nss_lt_all },
482#ifdef NS_CACHING
483		NS_CACHE_CB(&cache_info)
484#endif
485		{ NULL, NULL, NULL }
486	};
487	int rv, ret_errno;
488
489	ret_errno = 0;
490	*result = NULL;
491	rv = nsdispatch(result, dtab, NSDB_PROTOCOLS, "getprotoent_r",
492	    defaultsrc, pptr, buffer, buflen, &ret_errno);
493
494	if (rv != NS_SUCCESS) {
495		errno = ret_errno;
496		return (ret_errno);
497	}
498	return (0);
499}
500
501void
502setprotoent(int stayopen)
503{
504#ifdef NS_CACHING
505	static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
506		protocols, (void *)nss_lt_all,
507		NULL, NULL);
508#endif
509
510	static const ns_dtab dtab[] = {
511		{ NSSRC_FILES, files_setprotoent, NULL },
512#ifdef NS_CACHING
513		NS_CACHE_CB(&cache_info)
514#endif
515		{ NULL, NULL, NULL }
516	};
517
518	(void)nsdispatch(NULL, dtab, NSDB_PROTOCOLS, "setprotoent", defaultsrc,
519		stayopen);
520}
521
522void
523endprotoent(void)
524{
525#ifdef NS_CACHING
526	static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
527		protocols, (void *)nss_lt_all,
528		NULL, NULL);
529#endif
530
531	static const ns_dtab dtab[] = {
532		{ NSSRC_FILES, files_endprotoent, NULL },
533#ifdef NS_CACHING
534		NS_CACHE_CB(&cache_info)
535#endif
536		{ NULL, NULL, NULL }
537	};
538
539	(void)nsdispatch(NULL, dtab, NSDB_PROTOCOLS, "endprotoent", defaultsrc);
540}
541
542struct protoent *
543getprotoent(void)
544{
545	struct protodata *pd;
546	struct protoent *rval;
547
548	if ((pd = __protodata_init()) == NULL)
549		return (NULL);
550	if (getprotoent_r(&pd->proto, pd->data, sizeof(pd->data), &rval) != 0)
551		return (NULL);
552	return (rval);
553}
554