xref: /illumos-gate/usr/src/lib/libnsl/rpc/rpc_soc.c (revision 34e48580)
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 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
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 #ifdef PORTMAP
38 /*
39  * rpc_soc.c
40  *
41  * The backward compatibility routines for the earlier implementation
42  * of RPC, where the only transports supported were tcp/ip and udp/ip.
43  * Based on berkeley socket abstraction, now implemented on the top
44  * of TLI/Streams
45  */
46 
47 #include "mt.h"
48 #include "rpc_mt.h"
49 #include <stdio.h>
50 #include <sys/types.h>
51 #include <rpc/rpc.h>
52 #include <netinet/in.h>
53 #include <sys/socket.h>
54 #include <netdb.h>
55 #include <netdir.h>
56 #include <errno.h>
57 #include <sys/syslog.h>
58 #include <rpc/pmap_clnt.h>
59 #include <rpc/pmap_prot.h>
60 #include <rpc/nettype.h>
61 #include <syslog.h>
62 #include <string.h>
63 #include <stdlib.h>
64 #include <unistd.h>
65 
66 int __rpc_bindresvport(int, struct sockaddr_in *, int *, int);
67 int __rpc_bindresvport_ipv6(int, struct sockaddr *, int *, int, char *);
68 void get_myaddress_ipv6(char *, struct sockaddr *);
69 
70 extern mutex_t	rpcsoc_lock;
71 
72 /*
73  * A common clnt create routine
74  */
75 static CLIENT *
76 clnt_com_create(struct sockaddr_in *raddr, rpcprog_t prog, rpcvers_t vers,
77 	int *sockp, uint_t sendsz, uint_t recvsz, char *tp)
78 {
79 	CLIENT *cl;
80 	int madefd = FALSE;
81 	int fd = *sockp;
82 	struct t_info tinfo;
83 	struct netconfig *nconf;
84 	int port;
85 	struct netbuf bindaddr;
86 	extern int __rpc_minfd;
87 	bool_t locked = TRUE;
88 
89 	(void) mutex_lock(&rpcsoc_lock);
90 	if ((nconf = __rpc_getconfip(tp)) == NULL) {
91 		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
92 		(void) mutex_unlock(&rpcsoc_lock);
93 		return (NULL);
94 	}
95 	if (fd == RPC_ANYSOCK) {
96 		fd = t_open(nconf->nc_device, O_RDWR, &tinfo);
97 		if (fd == -1)
98 			goto syserror;
99 		if (fd < __rpc_minfd)
100 			fd = __rpc_raise_fd(fd);
101 		madefd = TRUE;
102 	} else {
103 		if (t_getinfo(fd, &tinfo) == -1)
104 			goto syserror;
105 	}
106 
107 	if (raddr->sin_port == 0) {
108 		uint_t proto;
109 		ushort_t sport;
110 
111 		/* pmap_getport is recursive */
112 		(void) mutex_unlock(&rpcsoc_lock);
113 		proto = strcmp(tp, "udp") == 0 ? IPPROTO_UDP : IPPROTO_TCP;
114 		sport = pmap_getport(raddr, prog, vers, proto);
115 		if (sport == 0) {
116 			locked = FALSE;
117 			goto err;
118 		}
119 		raddr->sin_port = htons(sport);
120 		/* pmap_getport is recursive */
121 		(void) mutex_lock(&rpcsoc_lock);
122 	}
123 
124 	/* Transform sockaddr_in to netbuf */
125 	bindaddr.maxlen = bindaddr.len =  __rpc_get_a_size(tinfo.addr);
126 	bindaddr.buf = (char *)raddr;
127 
128 	(void) __rpc_bindresvport(fd, NULL, &port, 0);
129 	cl = clnt_tli_create(fd, nconf, &bindaddr, prog, vers,
130 				sendsz, recvsz);
131 	if (cl) {
132 		if (madefd == TRUE) {
133 			/*
134 			 * The fd should be closed while destroying the handle.
135 			 */
136 			(void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL);
137 			*sockp = fd;
138 		}
139 		(void) freenetconfigent(nconf);
140 		(void) mutex_unlock(&rpcsoc_lock);
141 		return (cl);
142 	}
143 	goto err;
144 
145 syserror:
146 	rpc_createerr.cf_stat = RPC_SYSTEMERROR;
147 	rpc_createerr.cf_error.re_errno = errno;
148 	rpc_createerr.cf_error.re_terrno = t_errno;
149 
150 err:	if (madefd == TRUE)
151 		(void) t_close(fd);
152 	(void) freenetconfigent(nconf);
153 	if (locked == TRUE)
154 		(void) mutex_unlock(&rpcsoc_lock);
155 	return (NULL);
156 }
157 
158 CLIENT *
159 clntudp_bufcreate(struct sockaddr_in *raddr, rpcprog_t prog, rpcvers_t vers,
160 	struct timeval wait, int *sockp, uint_t sendsz, uint_t recvsz)
161 {
162 	CLIENT *cl;
163 
164 	cl = clnt_com_create(raddr, prog, vers, sockp, sendsz, recvsz, "udp");
165 	if (cl == NULL)
166 		return (NULL);
167 	(void) CLNT_CONTROL(cl, CLSET_RETRY_TIMEOUT, (char *)&wait);
168 	return (cl);
169 }
170 
171 CLIENT *
172 clntudp_create(struct sockaddr_in *raddr, rpcprog_t program, rpcvers_t version,
173 	struct timeval wait, int *sockp)
174 {
175 	return (clntudp_bufcreate(raddr, program, version, wait, sockp,
176 					UDPMSGSIZE, UDPMSGSIZE));
177 }
178 
179 CLIENT *
180 clnttcp_create(struct sockaddr_in *raddr, rpcprog_t prog, rpcvers_t vers,
181 	int *sockp, uint_t sendsz, uint_t recvsz)
182 {
183 	return (clnt_com_create(raddr, prog, vers, sockp, sendsz,
184 			recvsz, "tcp"));
185 }
186 
187 CLIENT *
188 clntraw_create(rpcprog_t prog, rpcvers_t vers)
189 {
190 	return (clnt_raw_create(prog, vers));
191 }
192 
193 /*
194  * A common server create routine
195  */
196 static SVCXPRT *
197 svc_com_create(int fd, uint_t sendsize, uint_t recvsize, char *netid)
198 {
199 	struct netconfig *nconf;
200 	SVCXPRT *svc;
201 	int madefd = FALSE;
202 	int port;
203 	int res;
204 
205 	if ((nconf = __rpc_getconfip(netid)) == NULL) {
206 		(void) syslog(LOG_ERR, "Could not get %s transport", netid);
207 		return (NULL);
208 	}
209 	if (fd == RPC_ANYSOCK) {
210 		fd = t_open(nconf->nc_device, O_RDWR, NULL);
211 		if (fd == -1) {
212 			char errorstr[100];
213 
214 			__tli_sys_strerror(errorstr, sizeof (errorstr),
215 					t_errno, errno);
216 			(void) syslog(LOG_ERR,
217 			"svc%s_create: could not open connection : %s", netid,
218 				    errorstr);
219 			(void) freenetconfigent(nconf);
220 			return (NULL);
221 		}
222 		madefd = TRUE;
223 	}
224 
225 	res = __rpc_bindresvport(fd, NULL, &port, 8);
226 	svc = svc_tli_create(fd, nconf, NULL,
227 				sendsize, recvsize);
228 	(void) freenetconfigent(nconf);
229 	if (svc == NULL) {
230 		if (madefd)
231 			(void) t_close(fd);
232 		return (NULL);
233 	}
234 	if (res == -1)
235 		/* LINTED pointer cast */
236 		port = (((struct sockaddr_in *)svc->xp_ltaddr.buf)->sin_port);
237 	svc->xp_port = ntohs(port);
238 	return (svc);
239 }
240 
241 SVCXPRT *
242 svctcp_create(int fd, uint_t sendsize, uint_t recvsize)
243 {
244 	return (svc_com_create(fd, sendsize, recvsize, "tcp"));
245 }
246 
247 SVCXPRT *
248 svcudp_bufcreate(int fd, uint_t sendsz, uint_t recvsz)
249 {
250 	return (svc_com_create(fd, sendsz, recvsz, "udp"));
251 }
252 
253 SVCXPRT *
254 svcfd_create(int fd, uint_t sendsize, uint_t recvsize)
255 {
256 	return (svc_fd_create(fd, sendsize, recvsize));
257 }
258 
259 
260 SVCXPRT *
261 svcudp_create(int fd)
262 {
263 	return (svc_com_create(fd, UDPMSGSIZE, UDPMSGSIZE, "udp"));
264 }
265 
266 SVCXPRT *
267 svcraw_create(void)
268 {
269 	return (svc_raw_create());
270 }
271 
272 /*
273  * Bind a fd to a privileged IP port.
274  * This is slightly different from the code in netdir_options
275  * because it has a different interface - main thing is that it
276  * needs to know its own address.  We also wanted to set the qlen.
277  * t_getname() can be used for those purposes and perhaps job can be done.
278  */
279 int
280 __rpc_bindresvport_ipv6(int fd, struct sockaddr *sin, int *portp, int qlen,
281 			char *fmly)
282 {
283 	int res;
284 	static in_port_t port, *sinport;
285 	struct sockaddr_in6 myaddr;
286 	int i;
287 	struct t_bind tbindstr, *tres;
288 	struct t_info tinfo;
289 	extern mutex_t portnum_lock;
290 
291 	/* VARIABLES PROTECTED BY portnum_lock: port */
292 
293 #define	STARTPORT 600
294 #define	ENDPORT (IPPORT_RESERVED - 1)
295 #define	NPORTS	(ENDPORT - STARTPORT + 1)
296 
297 	if (sin == 0 && fmly == 0) {
298 		errno = EINVAL;
299 		return (-1);
300 	}
301 	if (geteuid()) {
302 		errno = EACCES;
303 		return (-1);
304 	}
305 	if ((i = t_getstate(fd)) != T_UNBND) {
306 		if (t_errno == TBADF)
307 			errno = EBADF;
308 		if (i != -1)
309 			errno = EISCONN;
310 		return (-1);
311 	}
312 	if (sin == 0) {
313 		sin = (struct sockaddr *)&myaddr;
314 		get_myaddress_ipv6(fmly, sin);
315 	}
316 	if (sin->sa_family == AF_INET) {
317 		/* LINTED pointer cast */
318 		sinport = &((struct sockaddr_in *)sin)->sin_port;
319 	} else if (sin->sa_family == AF_INET6) {
320 		/* LINTED pointer cast */
321 		sinport = &((struct sockaddr_in6 *)sin)->sin6_port;
322 	} else {
323 		errno = EPFNOSUPPORT;
324 		return (-1);
325 	}
326 
327 	/* Transform sockaddr to netbuf */
328 	if (t_getinfo(fd, &tinfo) == -1) {
329 		return (-1);
330 	}
331 	/* LINTED pointer cast */
332 	tres = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
333 	if (tres == NULL)
334 		return (-1);
335 
336 	tbindstr.qlen = qlen;
337 	tbindstr.addr.buf = (char *)sin;
338 	tbindstr.addr.len = tbindstr.addr.maxlen = __rpc_get_a_size(tinfo.addr);
339 	/* LINTED pointer cast */
340 	sin = (struct sockaddr *)tbindstr.addr.buf;
341 
342 	res = -1;
343 	(void) mutex_lock(&portnum_lock);
344 	if (port == 0)
345 		port = (getpid() % NPORTS) + STARTPORT;
346 	for (i = 0; i < NPORTS; i++) {
347 		*sinport = htons(port++);
348 		if (port > ENDPORT)
349 			port = STARTPORT;
350 		res = t_bind(fd, &tbindstr, tres);
351 		if (res == 0) {
352 			if ((tbindstr.addr.len == tres->addr.len) &&
353 				(memcmp(tbindstr.addr.buf, tres->addr.buf,
354 					(int)tres->addr.len) == 0))
355 				break;
356 			(void) t_unbind(fd);
357 			res = -1;
358 		} else if (t_errno != TSYSERR || errno != EADDRINUSE)
359 			break;
360 	}
361 	(void) mutex_unlock(&portnum_lock);
362 
363 	if ((portp != NULL) && (res == 0))
364 		*portp = *sinport;
365 	(void) t_free((char *)tres, T_BIND);
366 	return (res);
367 }
368 
369 int
370 __rpc_bindresvport(int fd, struct sockaddr_in *sin, int *portp, int qlen)
371 {
372 	return (__rpc_bindresvport_ipv6(fd, (struct sockaddr *)sin, portp,
373 					qlen, NC_INET));
374 }
375 
376 /*
377  * Get clients IP address.
378  * don't use gethostbyname, which would invoke yellow pages
379  * Remains only for backward compatibility reasons.
380  * Used mainly by the portmapper so that it can register
381  * with itself. Also used by pmap*() routines
382  */
383 void
384 get_myaddress_ipv6(char *fmly, struct sockaddr *addr)
385 {
386 	if (fmly != 0 && strcmp(fmly, NC_INET6) == 0) {
387 		/* LINTED pointer cast */
388 		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
389 		(void) memset(sin6, 0, sizeof (*sin6));
390 		sin6->sin6_family = AF_INET6;
391 		sin6->sin6_port = htons(PMAPPORT);
392 		if (__can_use_af(AF_INET6)) {
393 			/* Local copy of in6addr_any to avoid -lsocket */
394 			struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
395 			sin6->sin6_addr = in6addr_any;
396 		} else {
397 			struct in_addr in4;
398 			in4.s_addr = INADDR_ANY;
399 			IN6_INADDR_TO_V4MAPPED(&in4, &sin6->sin6_addr);
400 		}
401 	} else {
402 		/* LINTED pointer cast */
403 		struct sockaddr_in	*sin = (struct sockaddr_in *)addr;
404 		(void) memset(sin, 0, sizeof (*sin));
405 		sin->sin_family = AF_INET;
406 		sin->sin_port = htons(PMAPPORT);
407 		sin->sin_addr.s_addr = INADDR_ANY;
408 	}
409 }
410 
411 void
412 get_myaddress(struct sockaddr_in *addr)
413 {
414 	get_myaddress_ipv6(0, (struct sockaddr *)addr);
415 }
416 
417 /*
418  * Get port used by specified service on specified host.
419  * Exists for source compatibility only.
420  * Obsoleted by rpcb_getaddr().
421  */
422 ushort_t
423 getrpcport(char *host, rpcprog_t prognum, rpcvers_t versnum,
424 	rpcprot_t proto)
425 {
426 	struct sockaddr_in addr;
427 	struct hostent *hp;
428 
429 	if ((hp = gethostbyname(host)) == NULL)
430 		return (0);
431 	(void) memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
432 	addr.sin_family = AF_INET;
433 	addr.sin_port =  0;
434 	return (pmap_getport(&addr, prognum, versnum, proto));
435 }
436 
437 /*
438  * For connectionless "udp" transport. Obsoleted by rpc_call().
439  */
440 int
441 callrpc(char *host, rpcprog_t prognum, rpcvers_t versnum, rpcproc_t procnum,
442 	xdrproc_t inproc, char *in, xdrproc_t outproc, char *out)
443 {
444 	return ((int)rpc_call(host, prognum, versnum, procnum, inproc,
445 				in, outproc, out, "udp"));
446 }
447 
448 /*
449  * For connectionless kind of transport. Obsoleted by rpc_reg()
450  */
451 int
452 registerrpc(rpcprog_t prognum, rpcvers_t versnum, rpcproc_t procnum,
453 	char *(*progname)(), xdrproc_t inproc, xdrproc_t outproc)
454 {
455 	return (rpc_reg(prognum, versnum, procnum, progname, inproc,
456 				outproc, "udp"));
457 }
458 
459 /*
460  * All the following clnt_broadcast stuff is convulated; it supports
461  * the earlier calling style of the callback function
462  */
463 static pthread_key_t	clnt_broadcast_key;
464 static resultproc_t	clnt_broadcast_result_main;
465 
466 /*
467  * Need to translate the netbuf address into sockaddr_in address.
468  * Dont care about netid here.
469  */
470 /* ARGSUSED2 */
471 static bool_t
472 rpc_wrap_bcast(char *resultp, struct netbuf *addr, struct netconfig *nconf)
473 {
474 	resultproc_t clnt_broadcast_result;
475 
476 	clnt_broadcast_result = thr_main()? clnt_broadcast_result_main :
477 		(resultproc_t)pthread_getspecific(clnt_broadcast_key);
478 	return ((*clnt_broadcast_result)(resultp,
479 				/* LINTED pointer cast */
480 				(struct sockaddr_in *)addr->buf));
481 }
482 
483 /*
484  * Broadcasts on UDP transport. Obsoleted by rpc_broadcast().
485  */
486 enum clnt_stat
487 clnt_broadcast(rpcprog_t prog, rpcvers_t vers, rpcproc_t proc, xdrproc_t xargs,
488 	caddr_t argsp, xdrproc_t xresults,
489 	caddr_t resultsp, resultproc_t eachresult)
490 {
491 	extern mutex_t tsd_lock;
492 
493 	if (thr_main()) {
494 		clnt_broadcast_result_main = eachresult;
495 	} else {
496 		if (clnt_broadcast_key == 0) {
497 			(void) mutex_lock(&tsd_lock);
498 			if (clnt_broadcast_key == 0)
499 				(void) pthread_key_create(&clnt_broadcast_key,
500 									NULL);
501 			(void) mutex_unlock(&tsd_lock);
502 		}
503 		(void) pthread_setspecific(clnt_broadcast_key,
504 							(void *)eachresult);
505 	}
506 	return (rpc_broadcast(prog, vers, proc, xargs, argsp, xresults,
507 				resultsp, (resultproc_t)rpc_wrap_bcast, "udp"));
508 }
509 
510 /*
511  * Create the client des authentication object. Obsoleted by
512  * authdes_seccreate().
513  */
514 AUTH *
515 authdes_create(char *servername, uint_t window, struct sockaddr_in *syncaddr,
516 	des_block *ckey)
517 {
518 	char *hostname = NULL;
519 
520 	if (syncaddr) {
521 		/*
522 		 * Change addr to hostname, because that is the way
523 		 * new interface takes it.
524 		 */
525 		struct netconfig *nconf;
526 		struct netbuf nb_syncaddr;
527 		struct nd_hostservlist *hlist;
528 		AUTH *nauth;
529 		int fd;
530 		struct t_info tinfo;
531 
532 		if ((nconf = __rpc_getconfip("udp")) == NULL &&
533 		    (nconf = __rpc_getconfip("tcp")) == NULL)
534 			goto fallback;
535 
536 		/* Transform sockaddr_in to netbuf */
537 		if ((fd = t_open(nconf->nc_device, O_RDWR, &tinfo)) == -1) {
538 			(void) freenetconfigent(nconf);
539 			goto fallback;
540 		}
541 		(void) t_close(fd);
542 		nb_syncaddr.maxlen = nb_syncaddr.len =
543 			__rpc_get_a_size(tinfo.addr);
544 		nb_syncaddr.buf = (char *)syncaddr;
545 		if (netdir_getbyaddr(nconf, &hlist, &nb_syncaddr)) {
546 			(void) freenetconfigent(nconf);
547 			goto fallback;
548 		}
549 		if (hlist && hlist->h_cnt > 0 && hlist->h_hostservs)
550 			hostname = hlist->h_hostservs->h_host;
551 		nauth = authdes_seccreate(servername, window, hostname, ckey);
552 		(void) netdir_free((char *)hlist, ND_HOSTSERVLIST);
553 		(void) freenetconfigent(nconf);
554 		return (nauth);
555 	}
556 fallback:
557 	return (authdes_seccreate(servername, window, hostname, ckey));
558 }
559 #endif /* PORTMAP */
560