1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  *	nis/getservent.c -- "nis" backend for nsswitch "services" database
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #include "nis_common.h"
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <signal.h>
35 #include <malloc.h>
36 #include <netdb.h>
37 #include <synch.h>
38 #include <ctype.h>
39 #include <rpcsvc/ypclnt.h>
40 #include <thread.h>
41 #include <sys/types.h>
42 #include <netinet/in.h>
43 
44 static int
45 check_name(args)
46 	nss_XbyY_args_t		*args;
47 {
48 	struct servent		*serv	= (struct servent *)args->returnval;
49 	const char		*name	= args->key.serv.serv.name;
50 	const char		*proto	= args->key.serv.proto;
51 	char			**aliasp;
52 
53 	if (proto != 0 && strcmp(serv->s_proto, proto) != 0) {
54 		return (0);
55 	}
56 	if (strcmp(serv->s_name, name) == 0) {
57 		return (1);
58 	}
59 	for (aliasp = serv->s_aliases;  *aliasp != 0;  aliasp++) {
60 		if (strcmp(*aliasp, name) == 0) {
61 			return (1);
62 		}
63 	}
64 	return (0);
65 }
66 
67 static int
68 check_name2(nss_XbyY_args_t *argp)
69 {
70 	const char	*limit, *linep, *keyp;
71 	int		name_match = 0;
72 
73 	linep = (const char *)argp->buf.buffer;
74 	limit = linep + strlen(argp->buf.buffer);
75 	keyp = argp->key.serv.serv.name;
76 
77 	/* compare name */
78 	while (*keyp && linep < limit && !isspace(*linep) && *keyp == *linep) {
79 		keyp++;
80 		linep++;
81 	}
82 	if (*keyp == '\0' && linep < limit && isspace(*linep)) {
83 		if (argp->key.serv.proto == NULL)
84 			return (1);
85 		else
86 			name_match = 1;
87 	}
88 
89 	/* skip remainder of the name, if any */
90 	while (linep < limit && !isspace(*linep))
91 		linep++;
92 	/* skip the delimiting spaces */
93 	while (linep < limit && isspace(*linep))
94 		linep++;
95 	/* skip port number */
96 	while (linep < limit && !isspace(*linep) && *linep != '/')
97 		linep++;
98 	if (linep == limit || *linep != '/')
99 		return (0);
100 
101 	linep++;
102 	if ((keyp = argp->key.serv.proto) == NULL) {
103 		/* skip protocol */
104 		while (linep < limit && !isspace(*linep))
105 			linep++;
106 	} else {
107 		/* compare protocol */
108 		while (*keyp && linep < limit && !isspace(*linep) &&
109 				*keyp == *linep) {
110 			keyp++;
111 			linep++;
112 		}
113 		/* no protocol match */
114 		if (*keyp || (linep < limit && !isspace(*linep)))
115 			return (0);
116 		/* protocol and name match, return */
117 		if (name_match)
118 			return (1);
119 		/* protocol match but name yet to be matched, so continue */
120 	}
121 
122 	/* compare with the aliases */
123 	while (linep < limit) {
124 		/* skip the delimiting spaces */
125 		while (linep < limit && isspace(*linep))
126 			linep++;
127 
128 		/* compare with the alias name */
129 		keyp = argp->key.serv.serv.name;
130 		while (*keyp && linep < limit && !isspace(*linep) &&
131 				*keyp == *linep) {
132 			keyp++;
133 			linep++;
134 		}
135 		if (*keyp == '\0' && (linep == limit || isspace(*linep)))
136 				return (1);
137 
138 		/* skip remainder of the alias name, if any */
139 		while (linep < limit && !isspace(*linep))
140 			linep++;
141 	}
142 	return (0);
143 }
144 
145 static mutex_t	no_byname_lock	= DEFAULTMUTEX;
146 static int	no_byname_map	= 0;
147 
148 static nss_status_t
149 getbyname(be, a)
150 	nis_backend_ptr_t	be;
151 	void			*a;
152 {
153 	nss_XbyY_args_t		*argp = (nss_XbyY_args_t *)a;
154 	const char		*name	= argp->key.serv.serv.name;
155 	const char		*proto	= argp->key.serv.proto;
156 	int			no_map;
157 	sigset_t		oldmask, newmask;
158 
159 	(void) sigfillset(&newmask);
160 	(void) _thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask);
161 	(void) _mutex_lock(&no_byname_lock);
162 	no_map = no_byname_map;
163 	(void) _mutex_unlock(&no_byname_lock);
164 	(void) _thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
165 
166 	if (no_map == 0) {
167 		int		yp_status;
168 		nss_status_t	res;
169 
170 		if (proto == 0) {
171 			res = _nss_nis_lookup(be, argp, 1,
172 			    "services.byservicename", name, &yp_status);
173 		} else {
174 			int len = strlen(name) + strlen(proto) + 3;
175 			char *key = malloc(len);
176 
177 			if (key == NULL) {
178 				return (NSS_UNAVAIL);
179 			}
180 			(void) snprintf(key, len, "%s/%s", name, proto);
181 			res = _nss_nis_lookup(be, argp, 1,
182 			    "services.byservicename", key, &yp_status);
183 			free(key);
184 		}
185 
186 		if (yp_status == YPERR_MAP) {
187 			(void) sigfillset(&newmask);
188 			_thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask);
189 			_mutex_lock(&no_byname_lock);
190 			no_byname_map = 1;
191 			_mutex_unlock(&no_byname_lock);
192 			_thr_sigsetmask(SIG_SETMASK, &oldmask,
193 					(sigset_t *)NULL);
194 		} else /* if (res == NSS_SUCCESS) <==== */ {
195 			return (res);
196 		}
197 	}
198 
199 	/*
200 	 * use check_anme to compare service name if nss1 or nss2 and
201 	 * request is not from nscd; otherwise use check_name2
202 	 */
203 	if (argp->buf.result != NULL)
204 		return (_nss_nis_XY_all(be, argp, 1, name, check_name));
205 	else
206 		return (_nss_nis_XY_all(be, argp, 1, name, check_name2));
207 }
208 
209 static int
210 check_port(args)
211 	nss_XbyY_args_t		*args;
212 {
213 	struct servent		*serv	= (struct servent *)args->returnval;
214 
215 	/*
216 	 * We only resorted to _nss_nis_XY_all because proto == 0, so just...
217 	 */
218 	return (serv->s_port == args->key.serv.serv.port);
219 }
220 
221 static int
222 check_port2(nss_XbyY_args_t *argp)
223 {
224 	const char	*limit, *linep, *keyp, *numstart;
225 	int		numlen, s_port;
226 	char		numbuf[12], *numend;
227 
228 	linep = (const char *)argp->buf.buffer;
229 	limit = linep + strlen(argp->buf.buffer);
230 
231 	/* skip name */
232 	while (linep < limit && !isspace(*linep))
233 		linep++;
234 	/* skip the delimiting spaces */
235 	while (linep < limit && isspace(*linep))
236 		linep++;
237 
238 	/* compare port num */
239 	numstart = linep;
240 	while (linep < limit && !isspace(*linep) && *linep != '/')
241 		linep++;
242 	if (linep == limit || *linep != '/')
243 		return (0);
244 	numlen = linep - numstart;
245 	if (numlen == 0 || numlen >= sizeof (numbuf))
246 		return (0);
247 	(void) memcpy(numbuf, numstart, numlen);
248 	numbuf[numlen] = '\0';
249 	s_port = htons((int)strtol(numbuf, &numend, 10));
250 	if (*numend != '\0')
251 		return (0);
252 	if (s_port == argp->key.serv.serv.port) {
253 		if ((keyp = argp->key.serv.proto) == NULL)
254 			return (1);
255 	} else
256 		return (0);
257 
258 	/* compare protocol */
259 	linep++;
260 	while (*keyp && linep < limit && !isspace(*linep) && *keyp == *linep) {
261 		keyp++;
262 		linep++;
263 	}
264 	return (*keyp == '\0' && (linep == limit || isspace(*linep)));
265 }
266 
267 
268 static nss_status_t
269 getbyport(be, a)
270 	nis_backend_ptr_t	be;
271 	void			*a;
272 {
273 	nss_XbyY_args_t		*argp	= (nss_XbyY_args_t *)a;
274 	int			port	= ntohs(argp->key.serv.serv.port);
275 	const char		*proto	= argp->key.serv.proto;
276 	char			*key;
277 	nss_status_t		res;
278 	int			len;
279 
280 	if (proto == 0) {
281 		char		portstr[12];
282 
283 		(void) snprintf(portstr, 12, "%d", port);
284 		/*
285 		 * use check_port to compare service port if nss1 or
286 		 * nss2 and request is not from nscd; otherwise use
287 		 * check_port2
288 		 */
289 		if (argp->buf.result != NULL)
290 			return (_nss_nis_XY_all(be, argp, 1, portstr,
291 				check_port));
292 		else
293 			return (_nss_nis_XY_all(be, argp, 1, portstr,
294 				check_port2));
295 	}
296 
297 	len = strlen(proto) + 14;
298 	if ((key = malloc(len)) == 0) {
299 		return (NSS_UNAVAIL);
300 	}
301 	(void) snprintf(key, len, "%d/%s", port, proto);
302 
303 	res = _nss_nis_lookup(be, argp, 1, "services.byname", key, 0);
304 
305 	free(key);
306 	return (res);
307 }
308 
309 static nis_backend_op_t serv_ops[] = {
310 	_nss_nis_destr,
311 	_nss_nis_endent,
312 	_nss_nis_setent,
313 	_nss_nis_getent_netdb,
314 	getbyname,
315 	getbyport
316 };
317 
318 /*ARGSUSED*/
319 nss_backend_t *
320 _nss_nis_services_constr(dummy1, dummy2, dummy3)
321 	const char	*dummy1, *dummy2, *dummy3;
322 {
323 	return (_nss_nis_constr(serv_ops,
324 				sizeof (serv_ops) / sizeof (serv_ops[0]),
325 				"services.byname"));
326 }
327