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