import less(1)
[unleashed/tickless.git] / usr / src / lib / libc / port / nsl / rpc_soc.c
blob84d3d6432e2c05bbc0c4756bf545ec94e20a2b4d
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
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.
35 #pragma ident "%Z%%M% %I% %E% SMI"
38 * rpc_soc.c
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
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 <rpc/clnt_soc.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>
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 *);
70 extern mutex_t rpcsoc_lock;
73 * A common clnt create routine
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)
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;
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);
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;
105 if (raddr->sin_port == 0) {
106 uint_t proto;
107 ushort_t sport;
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;
117 raddr->sin_port = htons(sport);
118 /* pmap_getport is recursive */
119 (void) mutex_lock(&rpcsoc_lock);
122 /* Transform sockaddr_in to netbuf */
123 bindaddr.maxlen = bindaddr.len = __rpc_get_a_size(tinfo.addr);
124 bindaddr.buf = (char *)raddr;
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) {
132 * The fd should be closed while destroying the handle.
134 (void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL);
135 *sockp = fd;
137 (void) freenetconfigent(nconf);
138 (void) mutex_unlock(&rpcsoc_lock);
139 return (cl);
141 goto err;
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;
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);
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)
160 CLIENT *cl;
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);
169 CLIENT *
170 clntudp_create(struct sockaddr_in *raddr, rpcprog_t program, rpcvers_t version,
171 struct timeval wait, int *sockp)
173 return (clntudp_bufcreate(raddr, program, version, wait, sockp,
174 UDPMSGSIZE, UDPMSGSIZE));
177 CLIENT *
178 clnttcp_create(struct sockaddr_in *raddr, rpcprog_t prog, rpcvers_t vers,
179 int *sockp, uint_t sendsz, uint_t recvsz)
181 return (clnt_com_create(raddr, prog, vers, sockp, sendsz,
182 recvsz, "tcp"));
185 CLIENT *
186 clntraw_create(rpcprog_t prog, rpcvers_t vers)
188 return (clnt_raw_create(prog, vers));
192 * A common server create routine
194 static SVCXPRT *
195 svc_com_create(int fd, uint_t sendsize, uint_t recvsize, char *netid)
197 struct netconfig *nconf;
198 SVCXPRT *svc;
199 int madefd = FALSE;
200 int port;
201 int res;
203 if ((nconf = __rpc_getconfip(netid)) == NULL) {
204 (void) syslog(LOG_ERR, "Could not get %s transport", netid);
205 return (NULL);
207 if (fd == RPC_ANYSOCK) {
208 fd = t_open(nconf->nc_device, O_RDWR, NULL);
209 if (fd == -1) {
210 char errorstr[100];
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);
220 madefd = TRUE;
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);
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);
239 SVCXPRT *
240 svctcp_create(int fd, uint_t sendsize, uint_t recvsize)
242 return (svc_com_create(fd, sendsize, recvsize, "tcp"));
245 SVCXPRT *
246 svcudp_bufcreate(int fd, uint_t sendsz, uint_t recvsz)
248 return (svc_com_create(fd, sendsz, recvsz, "udp"));
251 SVCXPRT *
252 svcfd_create(int fd, uint_t sendsize, uint_t recvsize)
254 return (svc_fd_create(fd, sendsize, recvsize));
258 SVCXPRT *
259 svcudp_create(int fd)
261 return (svc_com_create(fd, UDPMSGSIZE, UDPMSGSIZE, "udp"));
264 SVCXPRT *
265 svcraw_create(void)
267 return (svc_raw_create());
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.
278 __rpc_bindresvport_ipv6(int fd, struct sockaddr *sin, int *portp, int qlen,
279 char *fmly)
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;
289 /* VARIABLES PROTECTED BY portnum_lock: port */
291 #define STARTPORT 600
292 #define ENDPORT (IPPORT_RESERVED - 1)
293 #define NPORTS (ENDPORT - STARTPORT + 1)
295 if (sin == 0 && fmly == 0) {
296 errno = EINVAL;
297 return (-1);
299 if (geteuid()) {
300 errno = EACCES;
301 return (-1);
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);
310 if (sin == 0) {
311 sin = (struct sockaddr *)&myaddr;
312 get_myaddress_ipv6(fmly, sin);
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);
325 /* Transform sockaddr to netbuf */
326 if (t_getinfo(fd, &tinfo) == -1) {
327 return (-1);
329 /* LINTED pointer cast */
330 tres = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
331 if (tres == NULL)
332 return (-1);
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;
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;
359 (void) mutex_unlock(&portnum_lock);
361 if ((portp != NULL) && (res == 0))
362 *portp = *sinport;
363 (void) t_free((char *)tres, T_BIND);
364 return (res);
368 __rpc_bindresvport(int fd, struct sockaddr_in *sin, int *portp, int qlen)
370 return (__rpc_bindresvport_ipv6(fd, (struct sockaddr *)sin, portp,
371 qlen, NC_INET));
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
381 void
382 get_myaddress_ipv6(char *fmly, struct sockaddr *addr)
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 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);
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;
407 void
408 get_myaddress(struct sockaddr_in *addr)
410 get_myaddress_ipv6(0, (struct sockaddr *)addr);
414 * Get port used by specified service on specified host.
415 * Exists for source compatibility only.
416 * Obsoleted by rpcb_getaddr().
418 ushort_t
419 getrpcport(char *host, rpcprog_t prognum, rpcvers_t versnum,
420 rpcprot_t proto)
422 struct sockaddr_in addr;
423 struct hostent *hp;
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));
434 * For connectionless "udp" transport. Obsoleted by rpc_call().
437 callrpc(char *host, rpcprog_t prognum, rpcvers_t versnum, rpcproc_t procnum,
438 xdrproc_t inproc, char *in, xdrproc_t outproc, char *out)
440 return ((int)rpc_call(host, prognum, versnum, procnum, inproc,
441 in, outproc, out, "udp"));
445 * For connectionless kind of transport. Obsoleted by rpc_reg()
448 registerrpc(rpcprog_t prognum, rpcvers_t versnum, rpcproc_t procnum,
449 char *(*progname)(), xdrproc_t inproc, xdrproc_t outproc)
451 return (rpc_reg(prognum, versnum, procnum, progname, inproc,
452 outproc, "udp"));
456 * All the following clnt_broadcast stuff is convulated; it supports
457 * the earlier calling style of the callback function
459 static pthread_key_t clnt_broadcast_key = PTHREAD_ONCE_KEY_NP;
460 static resultproc_t clnt_broadcast_result_main;
463 * Need to translate the netbuf address into sockaddr_in address.
464 * Dont care about netid here.
466 /* ARGSUSED2 */
467 static bool_t
468 rpc_wrap_bcast(char *resultp, struct netbuf *addr, struct netconfig *nconf)
470 resultproc_t clnt_broadcast_result;
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));
480 * Broadcasts on UDP transport. Obsoleted by rpc_broadcast().
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)
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);
494 return (rpc_broadcast(prog, vers, proc, xargs, argsp, xresults,
495 resultsp, (resultproc_t)rpc_wrap_bcast, "udp"));
499 * Create the client des authentication object. Obsoleted by
500 * authdes_seccreate().
502 AUTH *
503 authdes_create(char *servername, uint_t window, struct sockaddr_in *syncaddr,
504 des_block *ckey)
506 char *hostname = NULL;
508 if (syncaddr) {
510 * Change addr to hostname, because that is the way
511 * new interface takes it.
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;
520 if ((nconf = __rpc_getconfip("udp")) == NULL &&
521 (nconf = __rpc_getconfip("tcp")) == NULL)
522 goto fallback;
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;
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;
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);
544 fallback:
545 return (authdes_seccreate(servername, window, hostname, ckey));