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