xref: /illumos-gate/usr/src/lib/libnsl/rpc/rpc_generic.c (revision 797f979d1fe26bfb1cdeb3e7a86ed24c0b654200)
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 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29 /*
30  * Portions of this source code were derived from Berkeley
31  * 4.3 BSD under license from the Regents of the University of
32  * California.
33  */
34 
35 #pragma ident	"%Z%%M%	%I%	%E% SMI"
36 
37 /*
38  * Miscl routines for RPC.
39  */
40 
41 #include "mt.h"
42 #include "rpc_mt.h"
43 #include <stdio.h>
44 #include <sys/types.h>
45 #include <rpc/rpc.h>
46 #include <rpc/nettype.h>
47 #include <sys/param.h>
48 #include <sys/mkdev.h>
49 #include <sys/stat.h>
50 #include <ctype.h>
51 #include <errno.h>
52 #include <sys/resource.h>
53 #include <netconfig.h>
54 #include <malloc.h>
55 #include <syslog.h>
56 #include <string.h>
57 #include <sys/systeminfo.h>
58 #include <netdir.h>
59 #include <netdb.h>
60 
61 struct handle {
62 	NCONF_HANDLE *nhandle;
63 	int nflag;		/* Whether NETPATH or NETCONFIG */
64 	int nettype;
65 };
66 
67 struct _rpcnettype {
68 	const char *name;
69 	const int type;
70 } _rpctypelist[] = {
71 	"netpath", _RPC_NETPATH,
72 	"visible", _RPC_VISIBLE,
73 	"circuit_v", _RPC_CIRCUIT_V,
74 	"datagram_v", _RPC_DATAGRAM_V,
75 	"circuit_n", _RPC_CIRCUIT_N,
76 	"datagram_n", _RPC_DATAGRAM_N,
77 	"tcp", _RPC_TCP,
78 	"udp", _RPC_UDP,
79 	"local", _RPC_LOCAL,
80 	"door", _RPC_DOOR,
81 	"door_local", _RPC_DOOR_LOCAL,
82 	"door_netpath", _RPC_DOOR_NETPATH,
83 	0, _RPC_NONE
84 };
85 
86 /*
87  * Cache the result of getrlimit(), so we don't have to do an
88  * expensive call every time. Since many old programs assume
89  * it will not return more than 1024 and use svc_fdset, return
90  * maximum of FD_SETSIZE.
91  */
92 int
93 __rpc_dtbsize(void)
94 {
95 	static int tbsize;
96 	struct rlimit rl;
97 
98 	if (tbsize)
99 		return (tbsize);
100 	if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
101 		tbsize = rl.rlim_max;
102 		/*
103 		 * backward compatibility; too many places
104 		 * this function is called assuming it returns
105 		 * maximum of 1024.
106 		 */
107 		if (tbsize > FD_SETSIZE)
108 			tbsize = FD_SETSIZE;
109 		return (tbsize);
110 	}
111 	/*
112 	 * Something wrong.  I'll try to save face by returning a
113 	 * pessimistic number.
114 	 */
115 	return (32);
116 }
117 
118 /*
119  * Find the appropriate buffer size
120  */
121 uint_t
122 __rpc_get_t_size(
123 	t_scalar_t size,	/* Size requested */
124 	t_scalar_t bufsize)	/* Supported by the transport */
125 {
126 	if (bufsize == -2)	/* transfer of data unsupported */
127 		return ((uint_t)0);
128 	if (size == 0) {
129 		if ((bufsize == -1) || (bufsize == 0)) {
130 			/*
131 			 * bufsize == -1 : No limit on the size
132 			 * bufsize == 0 : Concept of tsdu foreign. Choose
133 			 *			a value.
134 			 */
135 			return ((uint_t)RPC_MAXDATASIZE);
136 		}
137 		return ((uint_t)bufsize);
138 	}
139 	if ((bufsize == -1) || (bufsize == 0))
140 		return ((uint_t)size);
141 	/* Check whether the value is within the upper max limit */
142 	return (size > bufsize ? (uint_t)bufsize : (uint_t)size);
143 }
144 
145 /*
146  * Find the appropriate address buffer size
147  */
148 uint_t
149 __rpc_get_a_size(
150 	t_scalar_t size)	/* normally tinfo.addr */
151 {
152 	if (size >= 0)
153 		return ((uint_t)size);
154 	if (size <= -2)
155 		return ((uint_t)0);
156 	/*
157 	 * (size == -1) No limit on the size. we impose a limit here.
158 	 */
159 	return ((uint_t)RPC_MAXADDRSIZE);
160 }
161 
162 /*
163  * Returns the type of the network as defined in <rpc/nettype.h>
164  * If nettype is NULL, it defaults to NETPATH.
165  */
166 static int
167 getnettype(const char *nettype)
168 {
169 	int i;
170 
171 	if ((nettype == NULL) || (nettype[0] == NULL))
172 		return (_RPC_NETPATH);	/* Default */
173 
174 	for (i = 0; _rpctypelist[i].name; i++)
175 		if (strcasecmp(nettype, _rpctypelist[i].name) == 0)
176 			return (_rpctypelist[i].type);
177 	return (_rpctypelist[i].type);
178 }
179 
180 /*
181  * For the given nettype (tcp or udp only), return the first structure found.
182  * This should be freed by calling freenetconfigent()
183  */
184 struct netconfig *
185 __rpc_getconfip(char *nettype)
186 {
187 	char *netid;
188 	char *netid_tcp;
189 	char *netid_udp;
190 	static char *netid_tcp_main = NULL;
191 	static char *netid_udp_main = NULL;
192 	static pthread_key_t tcp_key = PTHREAD_ONCE_KEY_NP;
193 	static pthread_key_t udp_key = PTHREAD_ONCE_KEY_NP;
194 	int main_thread;
195 
196 	if ((main_thread = thr_main())) {
197 		netid_udp = netid_udp_main;
198 		netid_tcp = netid_tcp_main;
199 	} else {
200 		(void) pthread_key_create_once_np(&tcp_key, free);
201 		netid_tcp = pthread_getspecific(tcp_key);
202 		(void) pthread_key_create_once_np(&udp_key, free);
203 		netid_udp = pthread_getspecific(udp_key);
204 	}
205 	if (!netid_udp && !netid_tcp) {
206 		struct netconfig *nconf;
207 		void *confighandle;
208 
209 		if (!(confighandle = setnetconfig()))
210 			return (NULL);
211 		while (nconf = getnetconfig(confighandle)) {
212 			if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
213 				if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
214 					netid_tcp = strdup(nconf->nc_netid);
215 					if (netid_tcp == NULL) {
216 						syslog(LOG_ERR,
217 							"__rpc_getconfip : "
218 							"strdup failed");
219 						return (NULL);
220 					}
221 					if (main_thread)
222 						netid_tcp_main = netid_tcp;
223 					else
224 						(void) pthread_setspecific(
225 							tcp_key,
226 							(void *)netid_tcp);
227 				} else
228 				if (strcmp(nconf->nc_proto, NC_UDP) == 0) {
229 					netid_udp = strdup(nconf->nc_netid);
230 					if (netid_udp == NULL) {
231 						syslog(LOG_ERR,
232 							"__rpc_getconfip : "
233 							"strdup failed");
234 						return (NULL);
235 					}
236 					if (main_thread)
237 						netid_udp_main = netid_udp;
238 					else
239 						(void) pthread_setspecific(
240 							udp_key,
241 							(void *)netid_udp);
242 				}
243 			}
244 		}
245 		(void) endnetconfig(confighandle);
246 	}
247 	if (strcmp(nettype, "udp") == 0)
248 		netid = netid_udp;
249 	else if (strcmp(nettype, "tcp") == 0)
250 		netid = netid_tcp;
251 	else
252 		return (NULL);
253 	if ((netid == NULL) || (netid[0] == NULL))
254 		return (NULL);
255 	return (getnetconfigent(netid));
256 }
257 
258 
259 /*
260  * Returns the type of the nettype, which should then be used with
261  * __rpc_getconf().
262  */
263 void *
264 __rpc_setconf(char *nettype)
265 {
266 	struct handle *handle;
267 
268 	handle = malloc(sizeof (struct handle));
269 	if (handle == NULL)
270 		return (NULL);
271 	switch (handle->nettype = getnettype(nettype)) {
272 	case _RPC_DOOR_NETPATH:
273 	case _RPC_NETPATH:
274 	case _RPC_CIRCUIT_N:
275 	case _RPC_DATAGRAM_N:
276 		if (!(handle->nhandle = setnetpath())) {
277 			free(handle);
278 			return (NULL);
279 		}
280 		handle->nflag = TRUE;
281 		break;
282 	case _RPC_VISIBLE:
283 	case _RPC_CIRCUIT_V:
284 	case _RPC_DATAGRAM_V:
285 	case _RPC_TCP:
286 	case _RPC_UDP:
287 	case _RPC_LOCAL:
288 	case _RPC_DOOR_LOCAL:
289 		if (!(handle->nhandle = setnetconfig())) {
290 			free(handle);
291 			return (NULL);
292 		}
293 		handle->nflag = FALSE;
294 		break;
295 	default:
296 		free(handle);
297 		return (NULL);
298 	}
299 
300 	return (handle);
301 }
302 
303 /*
304  * Returns the next netconfig struct for the given "net" type.
305  * __rpc_setconf() should have been called previously.
306  */
307 struct netconfig *
308 __rpc_getconf(void *vhandle)
309 {
310 	struct handle *handle;
311 	struct netconfig *nconf;
312 
313 	handle = (struct handle *)vhandle;
314 	if (handle == NULL)
315 		return (NULL);
316 	for (;;) {
317 		if (handle->nflag)
318 			nconf = getnetpath(handle->nhandle);
319 		else
320 			nconf = getnetconfig(handle->nhandle);
321 		if (nconf == NULL)
322 			break;
323 		if ((nconf->nc_semantics != NC_TPI_CLTS) &&
324 		    (nconf->nc_semantics != NC_TPI_COTS) &&
325 		    (nconf->nc_semantics != NC_TPI_COTS_ORD))
326 			continue;
327 		switch (handle->nettype) {
328 		case _RPC_VISIBLE:
329 			if (!(nconf->nc_flag & NC_VISIBLE))
330 				continue;
331 			/*FALLTHROUGH*/
332 		case _RPC_DOOR_NETPATH:
333 			/*FALLTHROUGH*/
334 		case _RPC_NETPATH:	/* Be happy */
335 			break;
336 		case _RPC_CIRCUIT_V:
337 			if (!(nconf->nc_flag & NC_VISIBLE))
338 				continue;
339 			/*FALLTHROUGH*/
340 		case _RPC_CIRCUIT_N:
341 			if ((nconf->nc_semantics != NC_TPI_COTS) &&
342 			    (nconf->nc_semantics != NC_TPI_COTS_ORD))
343 				continue;
344 			break;
345 		case _RPC_DATAGRAM_V:
346 			if (!(nconf->nc_flag & NC_VISIBLE))
347 				continue;
348 			/*FALLTHROUGH*/
349 		case _RPC_DATAGRAM_N:
350 			if (nconf->nc_semantics != NC_TPI_CLTS)
351 				continue;
352 			break;
353 		case _RPC_TCP:
354 			if (((nconf->nc_semantics != NC_TPI_COTS) &&
355 			    (nconf->nc_semantics != NC_TPI_COTS_ORD)) ||
356 			    (strcmp(nconf->nc_protofmly, NC_INET) &&
357 			    strcmp(nconf->nc_protofmly, NC_INET6)) ||
358 			    strcmp(nconf->nc_proto, NC_TCP))
359 				continue;
360 			break;
361 		case _RPC_UDP:
362 			if ((nconf->nc_semantics != NC_TPI_CLTS) ||
363 			    (strcmp(nconf->nc_protofmly, NC_INET) &&
364 			    strcmp(nconf->nc_protofmly, NC_INET6)) ||
365 			    strcmp(nconf->nc_proto, NC_UDP))
366 				continue;
367 			break;
368 		case _RPC_LOCAL:
369 		case _RPC_DOOR_LOCAL:
370 			if (!(nconf->nc_flag & NC_VISIBLE))
371 				continue;
372 			if (strcmp(nconf->nc_protofmly, NC_LOOPBACK))
373 				continue;
374 			break;
375 		}
376 		break;
377 	}
378 	return (nconf);
379 }
380 
381 void
382 __rpc_endconf(void *vhandle)
383 {
384 	struct handle *handle;
385 
386 	handle = (struct handle *)vhandle;
387 	if (handle == NULL)
388 		return;
389 	if (handle->nflag) {
390 		(void) endnetpath(handle->nhandle);
391 	} else {
392 		(void) endnetconfig(handle->nhandle);
393 	}
394 	free(handle);
395 }
396 
397 /*
398  * Used to ping the NULL procedure for clnt handle.
399  * Returns NULL if fails, else a non-NULL pointer.
400  */
401 void *
402 rpc_nullproc(CLIENT *clnt)
403 {
404 	struct timeval TIMEOUT = {25, 0};
405 
406 	if (clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void, NULL,
407 			(xdrproc_t)xdr_void, NULL, TIMEOUT) != RPC_SUCCESS)
408 		return (NULL);
409 	return ((void *)clnt);
410 }
411 
412 /*
413  * Given a fd, find the transport device it is using and return the
414  * netconf entry corresponding to it.
415  * Note: It assumes servtpe parameter is 0 when uninitialized.
416  *	That is true for xprt->xp_type field.
417  */
418 struct netconfig *
419 __rpcfd_to_nconf(int fd, int servtype)
420 {
421 	struct stat statbuf;
422 	void *hndl;
423 	struct netconfig *nconf, *newnconf = NULL;
424 	major_t fdmajor;
425 	struct t_info tinfo;
426 
427 	if (fstat(fd, &statbuf) == -1)
428 		return (NULL);
429 
430 	fdmajor = major(statbuf.st_rdev);
431 	if (servtype == 0) {
432 		if (t_getinfo(fd, &tinfo) == -1) {
433 			char errorstr[100];
434 
435 			__tli_sys_strerror(errorstr, sizeof (errorstr),
436 					t_errno, errno);
437 			(void) syslog(LOG_ERR, "__rpcfd_to_nconf : %s : %s",
438 					"could not get transport information",
439 					errorstr);
440 			return (NULL);
441 		}
442 		servtype = tinfo.servtype;
443 	}
444 
445 	hndl = setnetconfig();
446 	if (hndl == NULL)
447 		return (NULL);
448 	/*
449 	 * Go through all transports listed in /etc/netconfig looking for
450 	 *	transport device in use on fd.
451 	 * - Match on service type first
452 	 * - if that succeeds, match on major numbers (used for new local
453 	 *	transport code that is self cloning)
454 	 * - if that fails, assume transport device uses clone driver
455 	 *	and try match the fdmajor with minor number of device path
456 	 *	which will be the major number of transport device since it
457 	 *	uses the clone driver.
458 	 */
459 
460 	while (nconf = getnetconfig(hndl)) {
461 		if (__rpc_matchserv(servtype, nconf->nc_semantics) == TRUE) {
462 			if (!stat(nconf->nc_device, &statbuf)) {
463 				if (fdmajor == major(statbuf.st_rdev))
464 					break; /* self cloning driver ? */
465 				if (fdmajor == minor(statbuf.st_rdev))
466 					break; /* clone driver! */
467 			}
468 		}
469 	}
470 	if (nconf)
471 		newnconf = getnetconfigent(nconf->nc_netid);
472 	(void) endnetconfig(hndl);
473 	return (newnconf);
474 }
475 
476 int
477 __rpc_matchserv(int servtype, unsigned int nc_semantics)
478 {
479 	switch (servtype) {
480 	case T_COTS:
481 		if (nc_semantics == NC_TPI_COTS)
482 			return (TRUE);
483 		break;
484 
485 	case T_COTS_ORD:
486 		if (nc_semantics == NC_TPI_COTS_ORD)
487 			return (TRUE);
488 		break;
489 
490 	case T_CLTS:
491 		if (nc_semantics == NC_TPI_CLTS)
492 			return (TRUE);
493 		break;
494 
495 	default:
496 		/* FALSE! */
497 		break;
498 
499 	}
500 	return (FALSE);
501 }
502 
503 /*
504  * Routines for RPC/Doors support.
505  */
506 
507 extern bool_t __inet_netdir_is_my_host(const char *);
508 
509 bool_t
510 __rpc_is_local_host(const char *host)
511 {
512 	char	buf[MAXHOSTNAMELEN + 1];
513 
514 	if (host == NULL || strcmp(host, "localhost") == 0 ||
515 			strcmp(host, HOST_SELF) == 0 ||
516 			strcmp(host, HOST_SELF_CONNECT) == 0 ||
517 			strlen(host) == 0)
518 		return (TRUE);
519 	if (sysinfo(SI_HOSTNAME, buf, sizeof (buf)) < 0)
520 		return (FALSE);
521 	if (strcmp(host, buf) == 0)
522 		return (TRUE);
523 	return (__inet_netdir_is_my_host(host));
524 }
525 
526 bool_t
527 __rpc_try_doors(const char *nettype, bool_t *try_others)
528 {
529 	switch (getnettype(nettype)) {
530 	case _RPC_DOOR:
531 		*try_others = FALSE;
532 		return (TRUE);
533 	case _RPC_DOOR_LOCAL:
534 	case _RPC_DOOR_NETPATH:
535 		*try_others = TRUE;
536 		return (TRUE);
537 	default:
538 		*try_others = TRUE;
539 		return (FALSE);
540 	}
541 }
542