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