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