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