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