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