xref: /illumos-gate/usr/src/lib/libnsl/rpc/svc_simple.c (revision f3041bfa)
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 /*
23  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
26 /* All Rights Reserved */
27 /*
28  * Portions of this source code were derived from Berkeley
29  * 4.3 BSD under license from the Regents of the University of
30  * California.
31  */
32 
33 /*
34  * svc_simple.c
35  * Simplified front end to rpc.
36  */
37 
38 /*
39  * This interface creates a virtual listener for all the services
40  * started thru rpc_reg(). It listens on the same endpoint for
41  * all the services and then executes the corresponding service
42  * for the given prognum and procnum.
43  */
44 
45 #include "mt.h"
46 #include "rpc_mt.h"
47 #include <errno.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <rpc/rpc.h>
52 #include <sys/types.h>
53 #include <syslog.h>
54 #include <rpc/nettype.h>
55 
56 static struct proglst {
57 	char *(*p_progname)();
58 	rpcprog_t p_prognum;
59 	rpcvers_t p_versnum;
60 	rpcproc_t p_procnum;
61 	SVCXPRT *p_transp;
62 	char *p_netid;
63 	char *p_xdrbuf;
64 	int p_recvsz;
65 	xdrproc_t p_inproc, p_outproc;
66 	struct proglst *p_nxt;
67 } *proglst;
68 
69 static void universal();
70 
71 static const char rpc_reg_err[] = "%s: %s";
72 static const char rpc_reg_msg[] = "rpc_reg: ";
73 static const char __reg_err1[] = "can't find appropriate transport";
74 static const char __reg_err3[] = "unsupported transport size";
75 static const char __no_mem_str[] = "out of memory";
76 /*
77  * For simplified, easy to use kind of rpc interfaces.
78  * nettype indicates the type of transport on which the service will be
79  * listening. Used for conservation of the system resource. Only one
80  * handle is created for all the services (actually one of each netid)
81  * and same xdrbuf is used for same netid. The size of the arguments
82  * is also limited by the recvsize for that transport, even if it is
83  * a COTS transport. This may be wrong, but for cases like these, they
84  * should not use the simplified interfaces like this.
85  */
86 
87 int
88 rpc_reg(const rpcprog_t prognum, const rpcvers_t versnum,
89 	const rpcproc_t procnum, char *(*progname)(), const xdrproc_t inproc,
90 	const xdrproc_t outproc, const char *nettype)
91 {
92 	struct netconfig *nconf;
93 	int done = FALSE;
94 	void *handle;
95 	extern mutex_t proglst_lock;
96 
97 	if (procnum == NULLPROC) {
98 		(void) syslog(LOG_ERR, (const char *) "%s: %s %d",
99 			rpc_reg_msg,
100 			(const char *) "can't reassign procedure number %d",
101 			NULLPROC);
102 		return (-1);
103 	}
104 
105 	if (nettype == NULL)
106 		nettype = "netpath";		/* The default behavior */
107 	if ((handle = __rpc_setconf((char *)nettype)) == NULL) {
108 		(void) syslog(LOG_ERR, rpc_reg_err, rpc_reg_msg, __reg_err1);
109 		return (-1);
110 	}
111 /* VARIABLES PROTECTED BY proglst_lock: proglst */
112 	(void) mutex_lock(&proglst_lock);
113 	while (nconf = __rpc_getconf(handle)) {
114 		struct proglst *pl;
115 		SVCXPRT *svcxprt;
116 		int madenow;
117 		uint_t recvsz;
118 		char *xdrbuf;
119 		char *netid;
120 
121 		madenow = FALSE;
122 		svcxprt = NULL;
123 		for (pl = proglst; pl; pl = pl->p_nxt)
124 			if (strcmp(pl->p_netid, nconf->nc_netid) == 0) {
125 				svcxprt = pl->p_transp;
126 				xdrbuf = pl->p_xdrbuf;
127 				recvsz = pl->p_recvsz;
128 				netid = pl->p_netid;
129 				break;
130 			}
131 
132 		if (svcxprt == NULL) {
133 			struct t_info tinfo;
134 
135 			svcxprt = svc_tli_create(RPC_ANYFD, nconf, NULL, 0, 0);
136 			if (svcxprt == NULL)
137 				continue;
138 			if (t_getinfo(svcxprt->xp_fd, &tinfo) == -1) {
139 				char errorstr[100];
140 
141 				__tli_sys_strerror(errorstr, sizeof (errorstr),
142 						t_errno, errno);
143 				(void) syslog(LOG_ERR, "%s : %s : %s",
144 					rpc_reg_msg, "t_getinfo failed",
145 					errorstr);
146 				SVC_DESTROY(svcxprt);
147 				continue;
148 			}
149 			if ((recvsz = __rpc_get_t_size(0, tinfo.tsdu)) == 0) {
150 				(void) syslog(LOG_ERR, rpc_reg_err, rpc_reg_msg,
151 					__reg_err3);
152 				SVC_DESTROY(svcxprt);
153 				continue;
154 			}
155 			if (((xdrbuf = malloc((unsigned)recvsz)) == NULL) ||
156 				((netid = strdup(nconf->nc_netid)) == NULL)) {
157 				(void) syslog(LOG_ERR, rpc_reg_err, rpc_reg_msg,
158 					__no_mem_str);
159 				SVC_DESTROY(svcxprt);
160 				break;
161 			}
162 			madenow = TRUE;
163 		}
164 		/*
165 		 * Check if this (program, version, netid) had already been
166 		 * registered.  The check may save a few RPC calls to rpcbind
167 		 */
168 		for (pl = proglst; pl; pl = pl->p_nxt)
169 			if ((pl->p_prognum == prognum) &&
170 				(pl->p_versnum == versnum) &&
171 				(strcmp(pl->p_netid, netid) == 0))
172 				break;
173 		if (pl == NULL) { /* Not yet */
174 			(void) rpcb_unset(prognum, versnum, nconf);
175 		} else {
176 			/* so that svc_reg does not call rpcb_set() */
177 			nconf = NULL;
178 		}
179 
180 		if (!svc_reg(svcxprt, prognum, versnum, universal, nconf)) {
181 			(void) syslog(LOG_ERR,
182 				"%s couldn't register prog %d vers %d for %s",
183 				rpc_reg_msg, prognum, versnum, netid);
184 			if (madenow) {
185 				SVC_DESTROY(svcxprt);
186 				free(xdrbuf);
187 				free(netid);
188 			}
189 			continue;
190 		}
191 
192 		pl = malloc(sizeof (struct proglst));
193 		if (pl == NULL) {
194 			(void) syslog(LOG_ERR, rpc_reg_err, rpc_reg_msg,
195 					__no_mem_str);
196 			if (madenow) {
197 				SVC_DESTROY(svcxprt);
198 				free(xdrbuf);
199 				free(netid);
200 			}
201 			break;
202 		}
203 		pl->p_progname = progname;
204 		pl->p_prognum = prognum;
205 		pl->p_versnum = versnum;
206 		pl->p_procnum = procnum;
207 		pl->p_inproc = inproc;
208 		pl->p_outproc = outproc;
209 		pl->p_transp = svcxprt;
210 		pl->p_xdrbuf = xdrbuf;
211 		pl->p_recvsz = recvsz;
212 		pl->p_netid = netid;
213 		pl->p_nxt = proglst;
214 		proglst = pl;
215 		done = TRUE;
216 	}
217 	__rpc_endconf(handle);
218 	(void) mutex_unlock(&proglst_lock);
219 
220 	if (done == FALSE) {
221 		(void) syslog(LOG_ERR,
222 			(const char *) "%s cant find suitable transport for %s",
223 			rpc_reg_msg, nettype);
224 		return (-1);
225 	}
226 	return (0);
227 }
228 
229 /*
230  * The universal handler for the services registered using registerrpc.
231  * It handles both the connectionless and the connection oriented cases.
232  */
233 
234 static void
235 universal(struct svc_req *rqstp, SVCXPRT *transp)
236 {
237 	rpcprog_t prog;
238 	rpcvers_t vers;
239 	rpcproc_t proc;
240 	char *outdata;
241 	char *xdrbuf;
242 	struct proglst *pl;
243 	extern mutex_t proglst_lock;
244 
245 	/*
246 	 * enforce "procnum 0 is echo" convention
247 	 */
248 	if (rqstp->rq_proc == NULLPROC) {
249 		if (svc_sendreply(transp, (xdrproc_t)xdr_void, NULL) == FALSE) {
250 			(void) syslog(LOG_ERR,
251 				(const char *) "svc_sendreply failed");
252 		}
253 		return;
254 	}
255 	prog = rqstp->rq_prog;
256 	vers = rqstp->rq_vers;
257 	proc = rqstp->rq_proc;
258 	(void) mutex_lock(&proglst_lock);
259 	for (pl = proglst; pl; pl = pl->p_nxt) {
260 		if (pl->p_prognum == prog && pl->p_procnum == proc &&
261 			pl->p_versnum == vers &&
262 			(strcmp(pl->p_netid, transp->xp_netid) == 0)) {
263 			/* decode arguments into a CLEAN buffer */
264 			xdrbuf = pl->p_xdrbuf;
265 			/* Zero the arguments: reqd ! */
266 			(void) memset(xdrbuf, 0, pl->p_recvsz);
267 			/*
268 			 * Assuming that sizeof (xdrbuf) would be enough
269 			 * for the arguments; if not then the program
270 			 * may bomb. BEWARE!
271 			 */
272 			if (!svc_getargs(transp, pl->p_inproc, xdrbuf)) {
273 				svcerr_decode(transp);
274 				(void) mutex_unlock(&proglst_lock);
275 				return;
276 			}
277 			outdata = (*(pl->p_progname))(xdrbuf);
278 			if (outdata == NULL &&
279 				pl->p_outproc != (xdrproc_t)xdr_void) {
280 				/* there was an error */
281 				(void) mutex_unlock(&proglst_lock);
282 				return;
283 			}
284 			if (!svc_sendreply(transp, pl->p_outproc, outdata)) {
285 				(void) syslog(LOG_ERR, (const char *)
286 			"rpc: rpc_reg trouble replying to prog %d vers %d",
287 				prog, vers);
288 				(void) mutex_unlock(&proglst_lock);
289 				return;
290 			}
291 			/* free the decoded arguments */
292 			(void) svc_freeargs(transp, pl->p_inproc, xdrbuf);
293 			(void) mutex_unlock(&proglst_lock);
294 			return;
295 		}
296 	}
297 	(void) mutex_unlock(&proglst_lock);
298 	/* This should never happen */
299 	(void) syslog(LOG_ERR, (const char *)
300 		"rpc: rpc_reg: never registered prog %d vers %d",
301 		prog, vers);
302 }
303