xref: /titanic_44/usr/src/lib/libnsl/rpc/svc_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 1986-2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1988 AT&T */
28 /*	All Rights Reserved   */
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #if !defined(lint) && defined(SCCSIDS)
33 static char sccsid[] = "@(#)svc_generic.c 1.21 89/02/28 Copyr 1988 Sun Micro";
34 #endif
35 
36 /*
37  * svc_generic.c, Server side for RPC.
38  *
39  */
40 
41 #include "mt.h"
42 #include <sys/socket.h>
43 #include <netinet/in.h>
44 #include <netinet/tcp.h>
45 #include <netinet/udp.h>
46 #include <inttypes.h>
47 #include "rpc_mt.h"
48 #include <stdio.h>
49 #include <rpc/rpc.h>
50 #include <sys/types.h>
51 #include <rpc/trace.h>
52 #include <errno.h>
53 #include <syslog.h>
54 #include <rpc/nettype.h>
55 #include <malloc.h>
56 #include <string.h>
57 #include <stropts.h>
58 
59 
60 extern int __svc_vc_setflag();
61 
62 extern SVCXPRT *svc_dg_create_private();
63 extern SVCXPRT *svc_vc_create_private();
64 extern SVCXPRT *svc_fd_create_private();
65 
66 extern bool_t __svc_add_to_xlist();
67 extern void __svc_free_xlist();
68 
69 /*
70  * The highest level interface for server creation.
71  * It tries for all the nettokens in that particular class of token
72  * and returns the number of handles it can create and/or find.
73  *
74  * It creates a link list of all the handles it could create.
75  * If svc_create() is called multiple times, it uses the handle
76  * created earlier instead of creating a new handle every time.
77  */
78 
79 /* VARIABLES PROTECTED BY xprtlist_lock: xprtlist */
80 
81 SVCXPRT_LIST *_svc_xprtlist = NULL;
82 extern mutex_t xprtlist_lock;
83 
84 void
85 __svc_free_xprtlist()
86 {
87 	__svc_free_xlist(&_svc_xprtlist, &xprtlist_lock);
88 }
89 
90 int
91 svc_create(dispatch, prognum, versnum, nettype)
92 	void (*dispatch)();	/* Dispatch function */
93 	rpcprog_t prognum;		/* Program number */
94 	rpcvers_t versnum;		/* Version number */
95 	const char *nettype;		/* Networktype token */
96 {
97 	SVCXPRT_LIST *l;
98 	int num = 0;
99 	SVCXPRT *xprt;
100 	struct netconfig *nconf;
101 	void *handle;
102 	bool_t try_others;
103 	int __rpc_try_doors();
104 
105 	trace3(TR_svc_create, 0, prognum, versnum);
106 	/*
107 	 * Check if service should register over doors transport.
108 	 */
109 	if (__rpc_try_doors(nettype, &try_others)) {
110 		if (svc_door_create(dispatch, prognum, versnum, 0) == NULL)
111 			(void) syslog(LOG_ERR,
112 				"svc_create: could not register over doors");
113 		else
114 			num++;
115 	}
116 	if (!try_others)
117 		return (num);
118 	if ((handle = __rpc_setconf((char *)nettype)) == NULL) {
119 		(void) syslog(LOG_ERR, "svc_create: unknown protocol");
120 		trace3(TR_svc_create, 1, prognum, versnum);
121 		return (0);
122 	}
123 	while (nconf = __rpc_getconf(handle)) {
124 		mutex_lock(&xprtlist_lock);
125 		for (l = _svc_xprtlist; l; l = l->next) {
126 			if (strcmp(l->xprt->xp_netid, nconf->nc_netid) == 0) {
127 				/* Found an old one, use it */
128 				(void) rpcb_unset(prognum, versnum, nconf);
129 				if (svc_reg(l->xprt, prognum, versnum,
130 					dispatch, nconf) == FALSE)
131 					(void) syslog(LOG_ERR,
132 		"svc_create: could not register prog %d vers %d on %s",
133 					prognum, versnum, nconf->nc_netid);
134 				else
135 					num++;
136 				break;
137 			}
138 		}
139 		mutex_unlock(&xprtlist_lock);
140 		if (l == NULL) {
141 			/* It was not found. Now create a new one */
142 			xprt = svc_tp_create(dispatch, prognum, versnum, nconf);
143 			if (xprt) {
144 				if (!__svc_add_to_xlist(&_svc_xprtlist, xprt,
145 							&xprtlist_lock)) {
146 					(void) syslog(LOG_ERR,
147 						"svc_create: no memory");
148 					trace3(TR_svc_create, 1, prognum,
149 						versnum);
150 					return (0);
151 				}
152 				num++;
153 			}
154 		}
155 	}
156 	__rpc_endconf(handle);
157 	/*
158 	 * In case of num == 0; the error messages are generated by the
159 	 * underlying layers; and hence not needed here.
160 	 */
161 	trace3(TR_svc_create, 1, prognum, versnum);
162 	return (num);
163 }
164 
165 /*
166  * The high level interface to svc_tli_create().
167  * It tries to create a server for "nconf" and registers the service
168  * with the rpcbind. It calls svc_tli_create();
169  */
170 SVCXPRT *
171 svc_tp_create(dispatch, prognum, versnum, nconf)
172 	void (*dispatch)();	/* Dispatch function */
173 	rpcprog_t prognum;	/* Program number */
174 	rpcvers_t versnum;	/* Version number */
175 	const struct netconfig *nconf; /* Netconfig structure for the network */
176 {
177 	SVCXPRT *xprt;
178 
179 	trace3(TR_svc_tp_create, 0, prognum, versnum);
180 	if (nconf == (struct netconfig *)NULL) {
181 		(void) syslog(LOG_ERR,
182 	"svc_tp_create: invalid netconfig structure for prog %d vers %d",
183 				prognum, versnum);
184 		trace3(TR_svc_tp_create, 1, prognum, versnum);
185 		return ((SVCXPRT *)NULL);
186 	}
187 	xprt = svc_tli_create(RPC_ANYFD, nconf, (struct t_bind *)NULL, 0, 0);
188 	if (xprt == (SVCXPRT *)NULL) {
189 		trace3(TR_svc_tp_create, 1, prognum, versnum);
190 		return ((SVCXPRT *)NULL);
191 	}
192 	(void) rpcb_unset(prognum, versnum, (struct netconfig *)nconf);
193 	if (svc_reg(xprt, prognum, versnum, dispatch, nconf) == FALSE) {
194 		(void) syslog(LOG_ERR,
195 		"svc_tp_create: Could not register prog %d vers %d on %s",
196 				prognum, versnum, nconf->nc_netid);
197 		SVC_DESTROY(xprt);
198 		trace3(TR_svc_tp_create, 1, prognum, versnum);
199 		return ((SVCXPRT *)NULL);
200 	}
201 	trace3(TR_svc_tp_create, 1, prognum, versnum);
202 	return (xprt);
203 }
204 
205 /*
206  * If fd is RPC_ANYFD, then it opens a fd for the given transport
207  * provider (nconf cannot be NULL then). If the t_state is T_UNBND and
208  * bindaddr is NON-NULL, it performs a t_bind using the bindaddr. For
209  * NULL bindadr and Connection oriented transports, the value of qlen
210  * is set arbitrarily.
211  *
212  * If sendsz or recvsz are zero, their default values are chosen.
213  */
214 SVCXPRT *
215 svc_tli_create(fd, nconf, bindaddr, sendsz, recvsz)
216 	int fd;				/* Connection end point */
217 	const struct netconfig *nconf;	/* Netconfig struct for nettoken */
218 	const struct t_bind *bindaddr;	/* Local bind address */
219 	uint_t sendsz;			/* Max sendsize */
220 	uint_t recvsz;			/* Max recvsize */
221 {
222 	SVCXPRT *xprt = NULL;		/* service handle */
223 	struct t_info tinfo;		/* transport info */
224 	struct t_bind *tres = NULL;	/* bind info */
225 	bool_t madefd = FALSE;		/* whether fd opened here  */
226 	int state;			/* state of the transport provider */
227 
228 	trace4(TR_svc_tli_create, 0, fd, sendsz, recvsz);
229 	if (fd == RPC_ANYFD) {
230 		if (nconf == (struct netconfig *)NULL) {
231 			(void) syslog(LOG_ERR,
232 			    "svc_tli_create: invalid netconfig");
233 			trace2(TR_svc_tli_create, 1, fd);
234 			return ((SVCXPRT *)NULL);
235 		}
236 		fd = t_open(nconf->nc_device, O_RDWR, &tinfo);
237 		if (fd == -1) {
238 			char errorstr[100];
239 
240 			__tli_sys_strerror(errorstr, sizeof (errorstr),
241 					t_errno, errno);
242 			(void) syslog(LOG_ERR,
243 			"svc_tli_create: could not open connection for %s: %s",
244 					nconf->nc_netid, errorstr);
245 			trace2(TR_svc_tli_create, 1, fd);
246 			return ((SVCXPRT *)NULL);
247 		}
248 		madefd = TRUE;
249 		state = T_UNBND;
250 	} else {
251 		/*
252 		 * It is an open descriptor. Sync it & get the transport info.
253 		 */
254 		if ((state = t_sync(fd)) == -1) {
255 			char errorstr[100];
256 
257 			__tli_sys_strerror(errorstr, sizeof (errorstr),
258 					t_errno, errno);
259 			(void) syslog(LOG_ERR,
260 		"svc_tli_create: could not do t_sync: %s",
261 					errorstr);
262 			trace2(TR_svc_tli_create, 1, fd);
263 			return ((SVCXPRT *)NULL);
264 		}
265 		if (t_getinfo(fd, &tinfo) == -1) {
266 			char errorstr[100];
267 
268 			__tli_sys_strerror(errorstr, sizeof (errorstr),
269 					t_errno, errno);
270 			(void) syslog(LOG_ERR,
271 		"svc_tli_create: could not get transport information: %s",
272 				    errorstr);
273 			trace2(TR_svc_tli_create, 1, fd);
274 			return ((SVCXPRT *)NULL);
275 		}
276 		/* Enable options of returning the ip's for udp */
277 		if (nconf) {
278 		    int ret = 0;
279 		    if (strcmp(nconf->nc_netid, "udp6") == 0) {
280 			ret = __rpc_tli_set_options(fd, IPPROTO_IPV6,
281 					IPV6_RECVPKTINFO, 1);
282 			if (ret < 0) {
283 			    char errorstr[100];
284 
285 			    __tli_sys_strerror(errorstr, sizeof (errorstr),
286 					t_errno, errno);
287 			    (void) syslog(LOG_ERR,
288 				"svc_tli_create: IPV6_RECVPKTINFO(1): %s",
289 					errorstr);
290 			    trace2(TR_svc_tli_create, 1, fd);
291 			    return ((SVCXPRT *)NULL);
292 			}
293 		    } else if (strcmp(nconf->nc_netid, "udp") == 0) {
294 			ret = __rpc_tli_set_options(fd, IPPROTO_IP,
295 					IP_RECVDSTADDR, 1);
296 			if (ret < 0) {
297 			    char errorstr[100];
298 
299 			    __tli_sys_strerror(errorstr, sizeof (errorstr),
300 					t_errno, errno);
301 			    (void) syslog(LOG_ERR,
302 				"svc_tli_create: IP_RECVDSTADDR(1): %s",
303 					errorstr);
304 			    trace2(TR_svc_tli_create, 1, fd);
305 			    return ((SVCXPRT *)NULL);
306 			}
307 		    }
308 		}
309 	}
310 
311 	/*
312 	 * If the fd is unbound, try to bind it.
313 	 * In any case, try to get its bound info in tres
314 	 */
315 /* LINTED pointer alignment */
316 	tres = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
317 	if (tres == NULL) {
318 		(void) syslog(LOG_ERR, "svc_tli_create: No memory!");
319 		goto freedata;
320 	}
321 
322 	switch (state) {
323 		bool_t tcp, exclbind;
324 	case T_UNBND:
325 		/*
326 		 * {TCP,UDP}_EXCLBIND has the following properties
327 		 *    - an fd bound to port P via IPv4 will prevent an IPv6
328 		 *    bind to port P (and vice versa)
329 		 *    - an fd bound to a wildcard IP address for port P will
330 		 *    prevent a more specific IP address bind to port P
331 		 *    (see {tcp,udp}.c for details)
332 		 *
333 		 * We use the latter property to prevent hijacking of RPC
334 		 * services that reside at non-privileged ports.
335 		 */
336 		tcp = nconf ? (strcmp(nconf->nc_proto, NC_TCP) == 0) : 0;
337 		if (nconf &&
338 		    (tcp || (strcmp(nconf->nc_proto, NC_UDP) == 0)) &&
339 		    rpc_control(__RPC_SVC_EXCLBIND_GET, &exclbind)) {
340 			if (exclbind) {
341 				if (__rpc_tli_set_options(fd,
342 					tcp ? IPPROTO_TCP : IPPROTO_UDP,
343 					tcp ? TCP_EXCLBIND : UDP_EXCLBIND,
344 							1) < 0) {
345 					syslog(LOG_ERR,
346 			    "svc_tli_create: can't set EXCLBIND [netid='%s']",
347 					    nconf->nc_netid);
348 					goto freedata;
349 				}
350 			}
351 		}
352 		if (bindaddr) {
353 			if (t_bind(fd, (struct t_bind *)bindaddr,
354 								tres) == -1) {
355 				char errorstr[100];
356 
357 				__tli_sys_strerror(errorstr, sizeof (errorstr),
358 						t_errno, errno);
359 				(void) syslog(LOG_ERR,
360 					"svc_tli_create: could not bind: %s",
361 					    errorstr);
362 				goto freedata;
363 			}
364 			/*
365 			 * Should compare the addresses only if addr.len
366 			 * was non-zero
367 			 */
368 			if (bindaddr->addr.len &&
369 				(memcmp(bindaddr->addr.buf, tres->addr.buf,
370 					(int)tres->addr.len) != 0)) {
371 				(void) syslog(LOG_ERR,
372 		"svc_tli_create: could not bind to requested address: %s",
373 						"address mismatch");
374 				goto freedata;
375 			}
376 		} else {
377 			tres->qlen = 64; /* Chosen Arbitrarily */
378 			tres->addr.len = 0;
379 			if (t_bind(fd, tres, tres) == -1) {
380 				char errorstr[100];
381 
382 				__tli_sys_strerror(errorstr, sizeof (errorstr),
383 						t_errno, errno);
384 				(void) syslog(LOG_ERR,
385 					"svc_tli_create: could not bind: %s",
386 						errorstr);
387 				goto freedata;
388 			}
389 		}
390 
391 		/* Enable options of returning the ip's for udp */
392 		if (nconf) {
393 		    int ret = 0;
394 		    if (strcmp(nconf->nc_netid, "udp6") == 0) {
395 			ret = __rpc_tli_set_options(fd, IPPROTO_IPV6,
396 					IPV6_RECVPKTINFO, 1);
397 			if (ret < 0) {
398 			    char errorstr[100];
399 
400 			    __tli_sys_strerror(errorstr, sizeof (errorstr),
401 					t_errno, errno);
402 			    (void) syslog(LOG_ERR,
403 				"svc_tli_create: IPV6_RECVPKTINFO(2): %s",
404 					errorstr);
405 			    goto freedata;
406 			}
407 		    } else if (strcmp(nconf->nc_netid, "udp") == 0) {
408 			ret = __rpc_tli_set_options(fd, IPPROTO_IP,
409 					IP_RECVDSTADDR, 1);
410 			if (ret < 0) {
411 			    char errorstr[100];
412 
413 			    __tli_sys_strerror(errorstr, sizeof (errorstr),
414 					t_errno, errno);
415 			    (void) syslog(LOG_ERR,
416 				"svc_tli_create: IP_RECVDSTADDR(2): %s",
417 					errorstr);
418 			    goto freedata;
419 			}
420 		    }
421 		}
422 		break;
423 
424 	case T_IDLE:
425 		if (bindaddr) {
426 			/* Copy the entire stuff in tres */
427 			if (tres->addr.maxlen < bindaddr->addr.len) {
428 				(void) syslog(LOG_ERR,
429 				"svc_tli_create: illegal netbuf length");
430 				goto freedata;
431 			}
432 			tres->addr.len = bindaddr->addr.len;
433 			(void) memcpy(tres->addr.buf, bindaddr->addr.buf,
434 					(int)tres->addr.len);
435 		} else
436 			if (t_getname(fd, &(tres->addr), LOCALNAME) == -1)
437 				tres->addr.len = 0;
438 		break;
439 	case T_INREL:
440 		(void) t_rcvrel(fd);
441 		(void) t_sndrel(fd);
442 		(void) syslog(LOG_ERR,
443 			"svc_tli_create: other side wants to\
444 release connection");
445 		goto freedata;
446 
447 	case T_INCON:
448 		/* Do nothing here. Assume this is handled in rendezvous */
449 		break;
450 	case T_DATAXFER:
451 		/*
452 		 * This takes care of the case where a fd
453 		 * is passed on which a connection has already
454 		 * been accepted.
455 		 */
456 		if (t_getname(fd, &(tres->addr), LOCALNAME) == -1)
457 			tres->addr.len = 0;
458 		break;
459 	default:
460 		(void) syslog(LOG_ERR,
461 		    "svc_tli_create: connection in a wierd state (%d)", state);
462 		goto freedata;
463 	}
464 
465 	/*
466 	 * call transport specific function.
467 	 */
468 	switch (tinfo.servtype) {
469 		case T_COTS_ORD:
470 		case T_COTS:
471 			if (state == T_DATAXFER)
472 				xprt = svc_fd_create_private(fd, sendsz,
473 						recvsz);
474 			else
475 				xprt = svc_vc_create_private(fd, sendsz,
476 						recvsz);
477 			if (!nconf || !xprt)
478 				break;
479 			if ((tinfo.servtype == T_COTS_ORD) &&
480 				(state != T_DATAXFER) &&
481 				(strcmp(nconf->nc_protofmly, "inet") == 0))
482 				(void) __svc_vc_setflag(xprt, TRUE);
483 			break;
484 		case T_CLTS:
485 			xprt = svc_dg_create_private(fd, sendsz, recvsz);
486 			break;
487 		default:
488 			(void) syslog(LOG_ERR,
489 			    "svc_tli_create: bad service type");
490 			goto freedata;
491 	}
492 	if (xprt == (SVCXPRT *)NULL)
493 		/*
494 		 * The error messages here are spitted out by the lower layers:
495 		 * svc_vc_create(), svc_fd_create() and svc_dg_create().
496 		 */
497 		goto freedata;
498 
499 	/* fill in the other xprt information */
500 
501 	/* Assign the local bind address */
502 	xprt->xp_ltaddr = tres->addr;
503 	/* Fill in type of service */
504 	xprt->xp_type = tinfo.servtype;
505 	tres->addr.buf = NULL;
506 	(void) t_free((char *)tres, T_BIND);
507 	tres = NULL;
508 
509 	xprt->xp_rtaddr.len = 0;
510 	xprt->xp_rtaddr.maxlen = __rpc_get_a_size(tinfo.addr);
511 
512 	/* Allocate space for the remote bind info */
513 	if ((xprt->xp_rtaddr.buf = mem_alloc(xprt->xp_rtaddr.maxlen)) == NULL) {
514 		(void) syslog(LOG_ERR, "svc_tli_create: No memory!");
515 		goto freedata;
516 	}
517 
518 	if (nconf) {
519 		xprt->xp_netid = strdup(nconf->nc_netid);
520 		if (xprt->xp_netid == NULL) {
521 			if (xprt->xp_rtaddr.buf)
522 				free(xprt->xp_rtaddr.buf);
523 			syslog(LOG_ERR, "svc_tli_create: strdup failed!");
524 			goto freedata;
525 		}
526 		xprt->xp_tp = strdup(nconf->nc_device);
527 		if (xprt->xp_tp == NULL) {
528 			if (xprt->xp_rtaddr.buf)
529 				free(xprt->xp_rtaddr.buf);
530 			if (xprt->xp_netid)
531 				free(xprt->xp_netid);
532 			syslog(LOG_ERR, "svc_tli_create: strdup failed!");
533 			goto freedata;
534 		}
535 	}
536 
537 /*
538  *	if (madefd && (tinfo.servtype == T_CLTS))
539  *		(void) ioctl(fd, I_POP, (char *)NULL);
540  */
541 	xprt_register(xprt);
542 	trace2(TR_svc_tli_create, 1, fd);
543 	return (xprt);
544 
545 freedata:
546 	if (madefd)
547 		(void) t_close(fd);
548 	if (tres)
549 		(void) t_free((char *)tres, T_BIND);
550 	if (xprt) {
551 		if (!madefd) /* so that svc_destroy doesnt close fd */
552 			xprt->xp_fd = RPC_ANYFD;
553 		SVC_DESTROY(xprt);
554 	}
555 	trace2(TR_svc_tli_create, 1, fd);
556 	return ((SVCXPRT *)NULL);
557 }
558