1/*	$NetBSD: getservent_r.c,v 1.13 2022/03/12 17:31:39 christos Exp $	*/
2
3/*
4 * Copyright (c) 1983, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#if defined(LIBC_SCCS) && !defined(lint)
34#if 0
35static char sccsid[] = "@(#)getservent.c	8.1 (Berkeley) 6/4/93";
36#else
37__RCSID("$NetBSD: getservent_r.c,v 1.13 2022/03/12 17:31:39 christos Exp $");
38#endif
39#endif /* LIBC_SCCS and not lint */
40
41#include "namespace.h"
42#include <cdbr.h>
43#include <errno.h>
44#include <fcntl.h>
45#include <netdb.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49
50#include "servent.h"
51
52#ifdef __weak_alias
53__weak_alias(endservent_r,_endservent_r)
54__weak_alias(getservent_r,_getservent_r)
55__weak_alias(setservent_r,_setservent_r)
56#endif
57
58int
59_servent_open(struct servent_data *sd)
60{
61	if (sd->flags & (_SV_CDB | _SV_PLAINFILE)) {
62		sd->flags |= _SV_FIRST;
63		return 0;
64	}
65
66	free(sd->line);
67	sd->line = NULL;
68	free(sd->cdb_buf);
69	sd->cdb_buf = NULL;
70	sd->cdb_buf_len = 0;
71	free(sd->aliases);
72	sd->aliases = NULL;
73	sd->maxaliases = 0;
74	sd->flags |= _SV_FIRST;
75
76	sd->cdb = cdbr_open(_PATH_SERVICES_CDB, CDBR_DEFAULT);
77	if (sd->cdb != NULL) {
78		sd->flags |= _SV_CDB;
79		return 0;
80	}
81
82	sd->plainfile = fopen(_PATH_SERVICES, "re");
83	if (sd->plainfile != NULL) {
84		sd->flags |= _SV_PLAINFILE;
85		return 0;
86	}
87	return -1;
88}
89
90void
91_servent_close(struct servent_data *sd)
92{
93	if (sd->flags & _SV_CDB) {
94		cdbr_close(sd->cdb);
95		sd->cdb = NULL;
96		sd->flags &= ~_SV_CDB;
97	}
98
99	if (sd->flags & _SV_PLAINFILE) {
100		(void)fclose(sd->plainfile);
101		sd->plainfile = NULL;
102		sd->flags &= ~_SV_PLAINFILE;
103	}
104	sd->flags &= ~_SV_STAYOPEN;
105}
106
107
108int
109_servent_getline(struct servent_data *sd)
110{
111
112	if (sd->flags & _SV_CDB)
113		return -1;
114
115	if ((sd->flags & _SV_PLAINFILE) == 0)
116		return -1;
117
118	free(sd->line);
119	sd->line = NULL;
120
121	if (sd->flags & _SV_FIRST) {
122		(void)rewind((FILE *)sd->plainfile);
123		sd->flags &= ~_SV_FIRST;
124	}
125	sd->line = fparseln(sd->plainfile, NULL, NULL, NULL,
126	    FPARSELN_UNESCALL);
127	return sd->line == NULL ? -1 : 0;
128}
129
130struct servent *
131_servent_parseline(struct servent_data *sd, struct servent *sp)
132{
133	size_t i = 0;
134	int oerrno;
135	char *p, *cp, **q;
136
137	if (sd->line == NULL)
138		return NULL;
139
140	sp->s_name = p = sd->line;
141	p = strpbrk(p, " \t");
142	if (p == NULL)
143		return NULL;
144	*p++ = '\0';
145	while (*p == ' ' || *p == '\t')
146		p++;
147	cp = strpbrk(p, ",/");
148	if (cp == NULL)
149		return NULL;
150	*cp++ = '\0';
151	sp->s_port = htons((u_short)atoi(p));
152	sp->s_proto = cp;
153	if (sd->aliases == NULL) {
154		sd->maxaliases = 10;
155		sd->aliases = calloc(sd->maxaliases, sizeof(*sd->aliases));
156		if (sd->aliases == NULL) {
157			oerrno = errno;
158			endservent_r(sd);
159			errno = oerrno;
160			return NULL;
161		}
162	}
163	sp->s_aliases = sd->aliases;
164	cp = strpbrk(cp, " \t");
165	if (cp != NULL)
166		*cp++ = '\0';
167	while (cp && *cp) {
168		if (*cp == ' ' || *cp == '\t') {
169			cp++;
170			continue;
171		}
172		if (i == sd->maxaliases - 2) {
173			sd->maxaliases *= 2;
174			q = realloc(sd->aliases, sd->maxaliases * sizeof(*q));
175			if (q == NULL) {
176				oerrno = errno;
177				endservent_r(sd);
178				errno = oerrno;
179				return NULL;
180			}
181			sp->s_aliases = sd->aliases = q;
182		}
183		sp->s_aliases[i++] = cp;
184		cp = strpbrk(cp, " \t");
185		if (cp != NULL)
186			*cp++ = '\0';
187	}
188	sp->s_aliases[i] = NULL;
189	return sp;
190}
191
192void
193setservent_r(int f, struct servent_data *sd)
194{
195	(void)_servent_open(sd);
196	sd->flags |= f ? _SV_STAYOPEN : 0;
197}
198
199void
200endservent_r(struct servent_data *sd)
201{
202	_servent_close(sd);
203	free(sd->aliases);
204	sd->aliases = NULL;
205	sd->maxaliases = 0;
206	free(sd->line);
207	sd->line = NULL;
208	free(sd->cdb_buf);
209	sd->cdb_buf = NULL;
210	sd->cdb_buf_len = 0;
211}
212
213struct servent *
214getservent_r(struct servent *sp, struct servent_data *sd)
215{
216
217	if ((sd->flags & (_SV_CDB | _SV_PLAINFILE)) == 0 &&
218	    _servent_open(sd) == -1)
219		return NULL;
220
221	if (sd->flags & _SV_CDB) {
222		const void *data;
223		size_t len;
224
225		if (sd->flags & _SV_FIRST) {
226			sd->cdb_index = 0;
227			sd->flags &= ~_SV_FIRST;
228		}
229
230		if (cdbr_get(sd->cdb, sd->cdb_index, &data, &len))
231			return NULL;
232		++sd->cdb_index;
233		return _servent_parsedb(sd, sp, data, len);
234	}
235	if (sd->flags & _SV_PLAINFILE) {
236		for (;;) {
237			if (_servent_getline(sd) == -1)
238				return NULL;
239			if (_servent_parseline(sd, sp) == NULL)
240				continue;
241			return sp;
242		}
243	}
244	return NULL;
245}
246
247struct servent *
248_servent_parsedb(struct servent_data *sd, struct servent *sp,
249    const uint8_t *data, size_t len)
250{
251	char **q;
252	size_t i;
253	int oerrno;
254
255	if ((sd->flags & _SV_STAYOPEN) == 0) {
256		if (len > sd->cdb_buf_len) {
257			void *tmp = realloc(sd->cdb_buf, len);
258			if (tmp == NULL)
259				goto fail;
260			sd->cdb_buf = tmp;
261			sd->cdb_buf_len = len;
262		}
263		memcpy(sd->cdb_buf, data, len);
264		data = sd->cdb_buf;
265	}
266
267	if (len < 2)
268		goto fail;
269	sp->s_port = htobe16(be16dec(data));
270	data += 2;
271	len -= 2;
272
273	if (len == 0 || len < (size_t)data[0] + 2)
274		goto fail;
275	sp->s_proto = __UNCONST(data + 1);
276
277	if (sp->s_proto[data[0]] != '\0')
278		goto fail;
279
280	len -= 2 + data[0];
281	data += 2 + data[0];
282
283	if (len == 0)
284		goto fail;
285	if (len < (size_t)data[0] + 2)
286		goto fail;
287
288	sp->s_name = __UNCONST(data + 1);
289	len -= 2 + data[0];
290	data += 2 + data[0];
291
292	if (sd->aliases == NULL) {
293		sd->maxaliases = 10;
294		sd->aliases = NULL;
295		errno = reallocarr(&sd->aliases,
296		    sd->maxaliases, sizeof(*sd->aliases));
297		if (errno)
298			goto fail;
299	}
300	sp->s_aliases = sd->aliases;
301	i = 0;
302	while (len) {
303		if (len < (size_t)data[0] + 2)
304			goto fail;
305		if (i == sd->maxaliases - 2) {
306			sd->maxaliases *= 2;
307			q = sd->aliases;
308			errno = reallocarr(&q, sd->maxaliases, sizeof(*q));
309			if (errno)
310				goto fail;
311			sp->s_aliases = sd->aliases = q;
312		}
313		sp->s_aliases[i++] = __UNCONST(data + 1);
314		len -= 2 + data[0];
315		data += 2 + data[0];
316	}
317	sp->s_aliases[i] = NULL;
318	return sp;
319
320fail:
321	oerrno = errno;
322	endservent_r(sd);
323	errno = oerrno;
324	return NULL;
325}
326
327