xref: /freebsd/lib/libc/net/getservent.c (revision d3ac2b30)
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. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #if defined(LIBC_SCCS) && !defined(lint)
35 static char sccsid[] = "@(#)getservent.c	8.1 (Berkeley) 6/4/93";
36 #endif /* LIBC_SCCS and not lint */
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
39 
40 #include <sys/param.h>
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <arpa/inet.h>
44 #include <errno.h>
45 #include <limits.h>
46 #include <netdb.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include <stdlib.h>
50 #ifdef YP
51 #include <rpc/rpc.h>
52 #include <rpcsvc/yp_prot.h>
53 #include <rpcsvc/ypclnt.h>
54 #endif
55 #include "namespace.h"
56 #include "reentrant.h"
57 #include "un-namespace.h"
58 #include "netdb_private.h"
59 
60 NETDB_THREAD_ALLOC(servent_data)
61 NETDB_THREAD_ALLOC(servdata)
62 
63 static void
64 servent_data_clear(struct servent_data *sed)
65 {
66 	if (sed->fp) {
67 		fclose(sed->fp);
68 		sed->fp = NULL;
69 	}
70 #ifdef YP
71 	free(sed->yp_key);
72 	sed->yp_key = NULL;
73 #endif
74 }
75 
76 static void
77 servent_data_free(void *ptr)
78 {
79 	struct servent_data *sed = ptr;
80 
81 	servent_data_clear(sed);
82 	free(sed);
83 }
84 
85 static void
86 servdata_free(void *ptr)
87 {
88 	free(ptr);
89 }
90 
91 int
92 __copy_servent(struct servent *se, struct servent *sptr, char *buf,
93     size_t buflen)
94 {
95 	char *cp;
96 	int i, n;
97 	int numptr, len;
98 
99 	/* Find out the amount of space required to store the answer. */
100 	numptr = 1; /* NULL ptr */
101 	len = (char *)ALIGN(buf) - buf;
102 	for (i = 0; se->s_aliases[i]; i++, numptr++) {
103 		len += strlen(se->s_aliases[i]) + 1;
104 	}
105 	len += strlen(se->s_name) + 1;
106 	len += strlen(se->s_proto) + 1;
107 	len += numptr * sizeof(char*);
108 
109 	if (len > (int)buflen) {
110 		errno = ERANGE;
111 		return (-1);
112 	}
113 
114 	/* copy port value */
115 	sptr->s_port = se->s_port;
116 
117 	cp = (char *)ALIGN(buf) + numptr * sizeof(char *);
118 
119 	/* copy official name */
120 	n = strlen(se->s_name) + 1;
121 	strcpy(cp, se->s_name);
122 	sptr->s_name = cp;
123 	cp += n;
124 
125 	/* copy aliases */
126 	sptr->s_aliases = (char **)ALIGN(buf);
127 	for (i = 0 ; se->s_aliases[i]; i++) {
128 		n = strlen(se->s_aliases[i]) + 1;
129 		strcpy(cp, se->s_aliases[i]);
130 		sptr->s_aliases[i] = cp;
131 		cp += n;
132 	}
133 	sptr->s_aliases[i] = NULL;
134 
135 	/* copy proto */
136 	n = strlen(se->s_proto) + 1;
137 	strcpy(cp, se->s_proto);
138 	sptr->s_proto = cp;
139 	cp += n;
140 
141 	return (0);
142 }
143 
144 #ifdef YP
145 static int
146 _getservbyport_yp(struct servent_data *sed)
147 {
148 	char *result;
149 	int resultlen;
150 	char buf[YPMAXRECORD + 2];
151 	int rv;
152 
153 	snprintf(buf, sizeof(buf), "%d/%s", ntohs(sed->yp_port),
154 	    sed->yp_proto);
155 
156 	sed->yp_port = 0;
157 	sed->yp_proto = NULL;
158 
159 	if (!sed->yp_domain) {
160 		if (yp_get_default_domain(&sed->yp_domain))
161 			return (0);
162 	}
163 
164 	/*
165 	 * We have to be a little flexible here. Ideally you're supposed
166 	 * to have both a services.byname and a services.byport map, but
167 	 * some systems have only services.byname. FreeBSD cheats a little
168 	 * by putting the services.byport information in the same map as
169 	 * services.byname so that either case will work. We allow for both
170 	 * possibilities here: if there is no services.byport map, we try
171 	 * services.byname instead.
172 	 */
173 	if ((rv = yp_match(sed->yp_domain, "services.byport", buf, strlen(buf),
174 						&result, &resultlen))) {
175 		if (rv == YPERR_MAP) {
176 			if (yp_match(sed->yp_domain, "services.byname", buf,
177 					strlen(buf), &result, &resultlen))
178 			return(0);
179 		} else
180 			return(0);
181 	}
182 
183 	/* getservent() expects lines terminated with \n -- make it happy */
184 	snprintf(sed->line, sizeof sed->line, "%.*s\n", resultlen, result);
185 
186 	free(result);
187 	return(1);
188 }
189 
190 static int
191 _getservbyname_yp(struct servent_data *sed)
192 {
193 	char *result;
194 	int resultlen;
195 	char buf[YPMAXRECORD + 2];
196 
197 	if(!sed->yp_domain) {
198 		if(yp_get_default_domain(&sed->yp_domain))
199 			return (0);
200 	}
201 
202 	snprintf(buf, sizeof(buf), "%s/%s", sed->yp_name, sed->yp_proto);
203 
204 	sed->yp_name = 0;
205 	sed->yp_proto = NULL;
206 
207 	if (yp_match(sed->yp_domain, "services.byname", buf, strlen(buf),
208 	    &result, &resultlen)) {
209 		return(0);
210 	}
211 
212 	/* getservent() expects lines terminated with \n -- make it happy */
213 	snprintf(sed->line, sizeof sed->line, "%.*s\n", resultlen, result);
214 
215 	free(result);
216 	return(1);
217 }
218 
219 static int
220 _getservent_yp(struct servent_data *sed)
221 {
222 	char *lastkey, *result;
223 	int resultlen;
224 	int rv;
225 
226 	if (!sed->yp_domain) {
227 		if (yp_get_default_domain(&sed->yp_domain))
228 			return (0);
229 	}
230 
231 	if (!sed->yp_stepping) {
232 		free(sed->yp_key);
233 		rv = yp_first(sed->yp_domain, "services.byname", &sed->yp_key,
234 		    &sed->yp_keylen, &result, &resultlen);
235 		if (rv) {
236 			sed->yp_stepping = 0;
237 			return(0);
238 		}
239 		sed->yp_stepping = 1;
240 	} else {
241 		lastkey = sed->yp_key;
242 		rv = yp_next(sed->yp_domain, "services.byname", sed->yp_key,
243 		    sed->yp_keylen, &sed->yp_key, &sed->yp_keylen, &result,
244 		    &resultlen);
245 		free(lastkey);
246 		if (rv) {
247 			sed->yp_stepping = 0;
248 			return (0);
249 		}
250 	}
251 
252 	/* getservent() expects lines terminated with \n -- make it happy */
253 	snprintf(sed->line, sizeof sed->line, "%.*s\n", resultlen, result);
254 
255 	free(result);
256 
257 	return(1);
258 }
259 #endif
260 
261 void
262 __setservent_p(int f, struct servent_data *sed)
263 {
264 	if (sed->fp == NULL)
265 		sed->fp = fopen(_PATH_SERVICES, "r");
266 	else
267 		rewind(sed->fp);
268 	sed->stayopen |= f;
269 }
270 
271 void
272 __endservent_p(struct servent_data *sed)
273 {
274 	servent_data_clear(sed);
275 	sed->stayopen = 0;
276 #ifdef YP
277 	sed->yp_stepping = 0;
278 	sed->yp_domain = NULL;
279 #endif
280 }
281 
282 int
283 __getservent_p(struct servent *se, struct servent_data *sed)
284 {
285 	char *p;
286 	char *cp, **q, *endp;
287 	long l;
288 
289 #ifdef YP
290 	if (sed->yp_stepping && _getservent_yp(sed)) {
291 		p = sed->line;
292 		goto unpack;
293 	}
294 tryagain:
295 #endif
296 	if (sed->fp == NULL && (sed->fp = fopen(_PATH_SERVICES, "r")) == NULL)
297 		return (-1);
298 again:
299 	if ((p = fgets(sed->line, sizeof sed->line, sed->fp)) == NULL)
300 		return (-1);
301 #ifdef YP
302 	if (*p == '+' && _yp_check(NULL)) {
303 		if (sed->yp_name != NULL) {
304 			if (!_getservbyname_yp(sed))
305 				goto tryagain;
306 		}
307 		else if (sed->yp_port != 0) {
308 			if (!_getservbyport_yp(sed))
309 				goto tryagain;
310 		}
311 		else if (!_getservent_yp(sed))
312 			goto tryagain;
313 	}
314 unpack:
315 #endif
316 	if (*p == '#')
317 		goto again;
318 	cp = strpbrk(p, "#\n");
319 	if (cp != NULL)
320 		*cp = '\0';
321 	se->s_name = p;
322 	p = strpbrk(p, " \t");
323 	if (p == NULL)
324 		goto again;
325 	*p++ = '\0';
326 	while (*p == ' ' || *p == '\t')
327 		p++;
328 	cp = strpbrk(p, ",/");
329 	if (cp == NULL)
330 		goto again;
331 	*cp++ = '\0';
332 	l = strtol(p, &endp, 10);
333 	if (endp == p || *endp != '\0' || l < 0 || l > USHRT_MAX)
334 		goto again;
335 	se->s_port = htons((in_port_t)l);
336 	se->s_proto = cp;
337 	q = se->s_aliases = sed->aliases;
338 	cp = strpbrk(cp, " \t");
339 	if (cp != NULL)
340 		*cp++ = '\0';
341 	while (cp && *cp) {
342 		if (*cp == ' ' || *cp == '\t') {
343 			cp++;
344 			continue;
345 		}
346 		if (q < &sed->aliases[_MAXALIASES - 1])
347 			*q++ = cp;
348 		cp = strpbrk(cp, " \t");
349 		if (cp != NULL)
350 			*cp++ = '\0';
351 	}
352 	*q = NULL;
353 	return (0);
354 }
355 
356 int
357 getservent_r(struct servent *sptr, char *buffer, size_t buflen,
358     struct servent **result)
359 {
360 	struct servent se;
361 	struct servent_data *sed;
362 
363 	if ((sed = __servent_data_init()) == NULL)
364 		return (-1);
365 
366 	if (__getservent_p(&se, sed) != 0)
367 		return (-1);
368 	if (__copy_servent(&se, sptr, buffer, buflen) != 0)
369 		return (-1);
370 	*result = sptr;
371 	return (0);
372 }
373 
374 void
375 setservent(int f)
376 {
377 	struct servent_data *sed;
378 
379 	if ((sed = __servent_data_init()) == NULL)
380 		return;
381 	__setservent_p(f, sed);
382 }
383 
384 void
385 endservent(void)
386 {
387 	struct servent_data *sed;
388 
389 	if ((sed = __servent_data_init()) == NULL)
390 		return;
391 	__endservent_p(sed);
392 }
393 
394 struct servent *
395 getservent(void)
396 {
397 	struct servdata *sd;
398 	struct servent *rval;
399 
400 	if ((sd = __servdata_init()) == NULL)
401 		return (NULL);
402 	if (getservent_r(&sd->serv, sd->data, sizeof(sd->data), &rval) != 0)
403 		return (NULL);
404 	return (rval);
405 }
406