xref: /freebsd/lib/libc/net/getservent.c (revision a2a775011ce266113bd38d22d618fa6258d4d8cb)
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/types.h>
41 #include <sys/socket.h>
42 #include <arpa/inet.h>
43 #include <netdb.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <stdlib.h>
47 #ifdef YP
48 #include <rpc/rpc.h>
49 #include <rpcsvc/yp_prot.h>
50 #include <rpcsvc/ypclnt.h>
51 #endif
52 #include "namespace.h"
53 #include "reentrant.h"
54 #include "un-namespace.h"
55 #include "netdb_private.h"
56 
57 static struct servdata servdata;
58 static thread_key_t servdata_key;
59 static once_t servdata_init_once = ONCE_INITIALIZER;
60 static int servdata_thr_keycreated = 0;
61 
62 static void
63 servent_data_clear(struct servent_data *sed)
64 {
65 	if (sed->fp) {
66 		fclose(sed->fp);
67 		sed->fp = NULL;
68 	}
69 #ifdef YP
70 	free(sed->yp_key);
71 	sed->yp_key = NULL;
72 #endif
73 }
74 
75 static void
76 servdata_free(void *ptr)
77 {
78 	struct servdata *sd = ptr;
79 
80 	if (sd == NULL)
81 		return;
82 	servent_data_clear(&sd->data);
83 	free(sd);
84 }
85 
86 static void
87 servdata_keycreate(void)
88 {
89 	servdata_thr_keycreated =
90 	    (thr_keycreate(&servdata_key, servdata_free) == 0);
91 }
92 
93 struct servdata *
94 __servdata_init(void)
95 {
96 	struct servdata *sd;
97 
98 	if (thr_main() != 0)
99 		return (&servdata);
100 	if (thr_once(&servdata_init_once, servdata_keycreate) != 0 ||
101 	    !servdata_thr_keycreated)
102 		return (NULL);
103 	if ((sd = thr_getspecific(servdata_key)) != NULL)
104 		return (sd);
105 	if ((sd = calloc(1, sizeof(*sd))) == NULL)
106 		return (NULL);
107 	if (thr_setspecific(servdata_key, sd) == 0)
108 		return (sd);
109 	free(sd);
110 	return (NULL);
111 }
112 
113 #ifdef YP
114 static int
115 _getservbyport_yp(struct servent_data *sed)
116 {
117 	char *result;
118 	int resultlen;
119 	char buf[YPMAXRECORD + 2];
120 	int rv;
121 
122 	snprintf(buf, sizeof(buf), "%d/%s", ntohs(sed->yp_port),
123 	    sed->yp_proto);
124 
125 	sed->yp_port = 0;
126 	sed->yp_proto = NULL;
127 
128 	if (!sed->yp_domain) {
129 		if (yp_get_default_domain(&sed->yp_domain))
130 			return (0);
131 	}
132 
133 	/*
134 	 * We have to be a little flexible here. Ideally you're supposed
135 	 * to have both a services.byname and a services.byport map, but
136 	 * some systems have only services.byname. FreeBSD cheats a little
137 	 * by putting the services.byport information in the same map as
138 	 * services.byname so that either case will work. We allow for both
139 	 * possibilities here: if there is no services.byport map, we try
140 	 * services.byname instead.
141 	 */
142 	if ((rv = yp_match(sed->yp_domain, "services.byport", buf, strlen(buf),
143 						&result, &resultlen))) {
144 		if (rv == YPERR_MAP) {
145 			if (yp_match(sed->yp_domain, "services.byname", buf,
146 					strlen(buf), &result, &resultlen))
147 			return(0);
148 		} else
149 			return(0);
150 	}
151 
152 	/* getservent() expects lines terminated with \n -- make it happy */
153 	snprintf(sed->line, sizeof sed->line, "%.*s\n", resultlen, result);
154 
155 	free(result);
156 	return(1);
157 }
158 
159 static int
160 _getservbyname_yp(struct servent_data *sed)
161 {
162 	char *result;
163 	int resultlen;
164 	char buf[YPMAXRECORD + 2];
165 
166 	if(!sed->yp_domain) {
167 		if(yp_get_default_domain(&sed->yp_domain))
168 			return (0);
169 	}
170 
171 	snprintf(buf, sizeof(buf), "%s/%s", sed->yp_name, sed->yp_proto);
172 
173 	sed->yp_name = 0;
174 	sed->yp_proto = NULL;
175 
176 	if (yp_match(sed->yp_domain, "services.byname", buf, strlen(buf),
177 	    &result, &resultlen)) {
178 		return(0);
179 	}
180 
181 	/* getservent() expects lines terminated with \n -- make it happy */
182 	snprintf(sed->line, sizeof sed->line, "%.*s\n", resultlen, result);
183 
184 	free(result);
185 	return(1);
186 }
187 
188 static int
189 _getservent_yp(struct servent_data *sed)
190 {
191 	char *lastkey, *result;
192 	int resultlen;
193 	int rv;
194 
195 	if (!sed->yp_domain) {
196 		if (yp_get_default_domain(&sed->yp_domain))
197 			return (0);
198 	}
199 
200 	if (!sed->yp_stepping) {
201 		free(sed->yp_key);
202 		rv = yp_first(sed->yp_domain, "services.byname", &sed->yp_key,
203 		    &sed->yp_keylen, &result, &resultlen);
204 		if (rv) {
205 			sed->yp_stepping = 0;
206 			return(0);
207 		}
208 		sed->yp_stepping = 1;
209 	} else {
210 		lastkey = sed->yp_key;
211 		rv = yp_next(sed->yp_domain, "services.byname", sed->yp_key,
212 		    sed->yp_keylen, &sed->yp_key, &sed->yp_keylen, &result,
213 		    &resultlen);
214 		free(lastkey);
215 		if (rv) {
216 			sed->yp_stepping = 0;
217 			return (0);
218 		}
219 	}
220 
221 	/* getservent() expects lines terminated with \n -- make it happy */
222 	snprintf(sed->line, sizeof sed->line, "%.*s\n", resultlen, result);
223 
224 	free(result);
225 
226 	return(1);
227 }
228 #endif
229 
230 void
231 setservent_r(int f, struct servent_data *sed)
232 {
233 	if (sed->fp == NULL)
234 		sed->fp = fopen(_PATH_SERVICES, "r");
235 	else
236 		rewind(sed->fp);
237 	sed->stayopen |= f;
238 }
239 
240 void
241 endservent_r(struct servent_data *sed)
242 {
243 	servent_data_clear(sed);
244 	sed->stayopen = 0;
245 #ifdef YP
246 	sed->yp_stepping = 0;
247 	sed->yp_domain = NULL;
248 #endif
249 }
250 
251 int
252 getservent_r(struct servent *se, struct servent_data *sed)
253 {
254 	char *p;
255 	char *cp, **q, *endp;
256 	long l;
257 
258 #ifdef YP
259 	if (sed->yp_stepping && _getservent_yp(sed)) {
260 		p = sed->line;
261 		goto unpack;
262 	}
263 tryagain:
264 #endif
265 	if (sed->fp == NULL && (sed->fp = fopen(_PATH_SERVICES, "r")) == NULL)
266 		return (-1);
267 again:
268 	if ((p = fgets(sed->line, sizeof sed->line, sed->fp)) == NULL)
269 		return (-1);
270 #ifdef YP
271 	if (*p == '+' && _yp_check(NULL)) {
272 		if (sed->yp_name != NULL) {
273 			if (!_getservbyname_yp(sed))
274 				goto tryagain;
275 		}
276 		else if (sed->yp_port != 0) {
277 			if (!_getservbyport_yp(sed))
278 				goto tryagain;
279 		}
280 		else if (!_getservent_yp(sed))
281 			goto tryagain;
282 	}
283 unpack:
284 #endif
285 	if (*p == '#')
286 		goto again;
287 	cp = strpbrk(p, "#\n");
288 	if (cp != NULL)
289 		*cp = '\0';
290 	se->s_name = p;
291 	p = strpbrk(p, " \t");
292 	if (p == NULL)
293 		goto again;
294 	*p++ = '\0';
295 	while (*p == ' ' || *p == '\t')
296 		p++;
297 	cp = strpbrk(p, ",/");
298 	if (cp == NULL)
299 		goto again;
300 	*cp++ = '\0';
301 	l = strtol(p, &endp, 10);
302 	if (endp == p || *endp != '\0' || l < 0 || l > USHRT_MAX)
303 		goto again;
304 	se->s_port = htons((in_port_t)l);
305 	se->s_proto = cp;
306 	q = se->s_aliases = sed->aliases;
307 	cp = strpbrk(cp, " \t");
308 	if (cp != NULL)
309 		*cp++ = '\0';
310 	while (cp && *cp) {
311 		if (*cp == ' ' || *cp == '\t') {
312 			cp++;
313 			continue;
314 		}
315 		if (q < &sed->aliases[_MAXALIASES - 1])
316 			*q++ = cp;
317 		cp = strpbrk(cp, " \t");
318 		if (cp != NULL)
319 			*cp++ = '\0';
320 	}
321 	*q = NULL;
322 	return (0);
323 }
324 
325 void
326 setservent(int f)
327 {
328 	struct servdata *sd;
329 
330 	if ((sd = __servdata_init()) == NULL)
331 		return;
332 	setservent_r(f, &sd->data);
333 }
334 
335 void
336 endservent(void)
337 {
338 	struct servdata *sd;
339 
340 	if ((sd = __servdata_init()) == NULL)
341 		return;
342 	endservent_r(&sd->data);
343 }
344 
345 struct servent *
346 getservent(void)
347 {
348 	struct servdata *sd;
349 
350 	if ((sd = __servdata_init()) == NULL)
351 		return (NULL);
352 	if (getservent_r(&sd->serv, &sd->data) != 0)
353 		return (NULL);
354 	return (&sd->serv);
355 }
356