xref: /freebsd/lib/libc/net/getservent.c (revision 96f79dca7680fa814d77b3a580136360e573ea66)
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 	if (sed->key) {
70 		free(sed->key);
71 		sed->key = NULL;
72 	}
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",
123 	    ntohs(sed->getservbyport_yp), sed->getservbyproto_yp);
124 
125 	sed->getservbyport_yp = 0;
126 	sed->getservbyproto_yp = 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, BUFSIZ, "%.*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->getservbyname_yp,
172 	    sed->getservbyproto_yp);
173 
174 	sed->getservbyname_yp = 0;
175 	sed->getservbyproto_yp = 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, BUFSIZ, "%.*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->stepping_yp) {
202 		if (sed->key)
203 			free(sed->key);
204 		rv = yp_first(sed->yp_domain, "services.byname", &sed->key,
205 		    &sed->keylen, &result, &resultlen);
206 		if (rv) {
207 			sed->stepping_yp = 0;
208 			return(0);
209 		}
210 		sed->stepping_yp = 1;
211 	} else {
212 		lastkey = sed->key;
213 		rv = yp_next(sed->yp_domain, "services.byname", sed->key,
214 		    sed->keylen, &sed->key, &sed->keylen, &result, &resultlen);
215 		free(lastkey);
216 		if (rv) {
217 			sed->stepping_yp = 0;
218 			return (0);
219 		}
220 	}
221 
222 	/* getservent() expects lines terminated with \n -- make it happy */
223 	snprintf(sed->line, BUFSIZ, "%.*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 	sed->stepping_yp = 0;
247 	sed->yp_domain = NULL;
248 }
249 
250 int
251 getservent_r(struct servent *se, struct servent_data *sed)
252 {
253 	char *p;
254 	char *cp, **q, *endp;
255 	long l;
256 
257 #ifdef YP
258 	if (sed->stepping_yp && _getservent_yp(sed)) {
259 		p = sed->line;
260 		goto unpack;
261 	}
262 tryagain:
263 #endif
264 	if (sed->fp == NULL && (sed->fp = fopen(_PATH_SERVICES, "r")) == NULL)
265 		return (-1);
266 again:
267 	if ((p = fgets(sed->line, BUFSIZ, sed->fp)) == NULL)
268 		return (-1);
269 #ifdef YP
270 	if (*p == '+' && _yp_check(NULL)) {
271 		if (sed->getservbyname_yp != NULL) {
272 			if (!_getservbyname_yp(sed))
273 				goto tryagain;
274 		}
275 		else if (sed->getservbyport_yp != 0) {
276 			if (!_getservbyport_yp(sed))
277 				goto tryagain;
278 		}
279 		else if (!_getservent_yp(sed))
280 			goto tryagain;
281 	}
282 unpack:
283 #endif
284 	if (*p == '#')
285 		goto again;
286 	cp = strpbrk(p, "#\n");
287 	if (cp != NULL)
288 		*cp = '\0';
289 	se->s_name = p;
290 	p = strpbrk(p, " \t");
291 	if (p == NULL)
292 		goto again;
293 	*p++ = '\0';
294 	while (*p == ' ' || *p == '\t')
295 		p++;
296 	cp = strpbrk(p, ",/");
297 	if (cp == NULL)
298 		goto again;
299 	*cp++ = '\0';
300 	l = strtol(p, &endp, 10);
301 	if (endp == p || *endp != '\0' || l < 0 || l > USHRT_MAX)
302 		goto again;
303 	se->s_port = htons((in_port_t)l);
304 	se->s_proto = cp;
305 	q = se->s_aliases = sed->aliases;
306 	cp = strpbrk(cp, " \t");
307 	if (cp != NULL)
308 		*cp++ = '\0';
309 	while (cp && *cp) {
310 		if (*cp == ' ' || *cp == '\t') {
311 			cp++;
312 			continue;
313 		}
314 		if (q < &sed->aliases[SERVENT_MAXALIASES - 1])
315 			*q++ = cp;
316 		cp = strpbrk(cp, " \t");
317 		if (cp != NULL)
318 			*cp++ = '\0';
319 	}
320 	*q = NULL;
321 	return (0);
322 }
323 
324 void
325 setservent(int f)
326 {
327 	struct servdata *sd;
328 
329 	if ((sd = _servdata_init()) == NULL)
330 		return;
331 	setservent_r(f, &sd->data);
332 }
333 
334 void
335 endservent(void)
336 {
337 	struct servdata *sd;
338 
339 	if ((sd = _servdata_init()) == NULL)
340 		return;
341 	endservent_r(&sd->data);
342 }
343 
344 struct servent *
345 getservent(void)
346 {
347 	struct servdata *sd;
348 
349 	if ((sd = _servdata_init()) == NULL)
350 		return (NULL);
351 	if (getservent_r(&sd->serv, &sd->data) != 0)
352 		return (NULL);
353 	return (&sd->serv);
354 }
355