diff options
author | Ulrich Drepper <drepper@redhat.com> | 1998-07-28 16:26:04 +0000 |
---|---|---|
committer | Ulrich Drepper <drepper@redhat.com> | 1998-07-28 16:26:04 +0000 |
commit | e852e889444a8bf27f3e5075d064e9922b38e7e2 (patch) | |
tree | 75d2d4b1010a26d723daefef7909d1a6355929bc /sunrpc | |
parent | c9243dacea19b7dcf36bb69ca83877d3ea905831 (diff) | |
download | glibc-e852e889444a8bf27f3e5075d064e9922b38e7e2.tar glibc-e852e889444a8bf27f3e5075d064e9922b38e7e2.tar.gz glibc-e852e889444a8bf27f3e5075d064e9922b38e7e2.tar.bz2 glibc-e852e889444a8bf27f3e5075d064e9922b38e7e2.zip |
Update.
1998-07-28 Ulrich Drepper <drepper@cygnus.com>
* math/libm-test.c (tgamma_test): Remove redundant tests.
1998-07-28 16:20 Ulrich Drepper <drepper@cygnus.com>
* sysdeps/generic/glob.c: Correct problems with */foo and GLOB_NOCHECK
where foo does not exist in any of the subdirectories.
Reported by Paul D. Smith <psmith@BayNetworks.COM>.
* posix/globtest.sh: Add test for this bug.
1998-07-28 Mark Kettenis <kettenis@phys.uva.nl>
* io/sys/statfs.h: Fix typos.
* io/sys/statvfs.h: Fix typos.
1998-07-28 Ulrich Drepper <drepper@cygnus.com>
* version.h (VERSION): Bump to 2.0.95.
* math/Makefile (libm-calls): Remove w_gamma, add w_tgamma.
* math/Versions [GLIBC_2.1]: Add tgamma, tgammaf, and tgammal.
* math/libm-test.c: Split old gamma_test and move half of it in new
function tgamma_test.
* math/bits/mathcalls.h: Add declaration of tgamma.
* sysdeps/libm-ieee754/k_standard.c: Change gamma errors into
tgamma errors.
* sysdeps/libm-ieee754/w_gamma.c: Remove lgamma compatibility code
and rename to ...
* sysdeps/libm-ieee754/w_tgamma.c: ... this. New file.
* sysdeps/libm-ieee754/w_gammaf.c: Remove lgammaf compatibility code
and rename to ...
* sysdeps/libm-ieee754/w_tgammaf.c: ... this. New file.
* sysdeps/libm-ieee754/w_gammal.c: Remove lgammal compatibility code
and rename to ...
* sysdeps/libm-ieee754/w_tgammal.c: ... this. New file.
* sysdeps/libm-ieee754/w_lgamma.c: Add gamma as weak alias.
* sysdeps/libm-ieee754/w_lgammaf.c: Likewise.
* sysdeps/libm-ieee754/w_lgammal.c: Likewise.
* stgdio-common/printf-parse.h: Implement handling of j, t, and z
modifiers.
* stdio-common/vfprintf.c: Likewise.
* stdio-common/vfscanf.c: Likewise.
* manual/stdio.texi: Document new printf/scanf modifiers.
* sysdeps/unix/sysv/linux/recvmsg.c: Remove alias __recvmsg.
* sysdeps/unix/sysv/linux/sendmsg.c: Remove alias __sendmsg.
1998-07-28 Thorsten Kukuk <kukuk@vt.uni-paderborn.de>
* sunrpc/Makefile (routines): Add clnt_unix and svc_unix.
* sunrpc/Versions: Add new *unix_create functions.
* sunrpc/clnt_gen.c: Add support for RPC over AF_UNIX.
* sunrpc/clnt_unix.c: New, client side of RPC over AF_UNIX.
* sunrpc/key_call.c: Use RPC over AF_UNIX for communication
with keyserv daemon.
* sunrpc/rpc/clnt.h: Add AF_UNIX based RPC function prototypes.
* sunrpc/rpc/svc.h: Likewise.
* sunrpc/svc_authux.c: Copy internal auth flavor if none is given.
* sunrpc/svc_tcp.c: Fix typos.
* sunrpc/svc_unix.c: New, server side of AF_UNIX based RPC.
* nis/Makefile: Remove currently not working cache functions.
* nis/Versions: Add __nisbind_* functions for rpc.nisd.
* nis/nis_call.c: Rewrite binding to a NIS+ server to reuse
CLIENT handles.
* nis/nis_file.c: Fix memory leaks.
* nis/nis_intern.h: Move internal structs from here ...
* nis/rpcsvc/nislib.h: ... to here for NIS+ server and tools.
* nis/nis_lookup.c: Try at first if last client handle works.
* nis/nis_table.c: Likewise.
* nis/nis_checkpoint.c: Adjust __do_niscall2 parameters.
* nis/nis_mkdir.c: Likewise.
* nis/nis_ping.c: Likewise.
* nis/nis_rmdir.c: Likewise.
* nis/nis_server.c: Likewise.
* nis/nis_util.c: Likewise.
* nis/nis_findserv.c (__nis_findfastest): Little optimization.
1998-07-28 Andreas Jaeger <aj@arthur.rhein-neckar.de>
* stdlib/strtol.c (STRTOL_LONG_MAX): Correct typo in last patch -
define as LONG_MAX.
1998-07-28 09:31 Ulrich Drepper <drepper@cygnus.com>
* nscd/connections.c (gr_send_answer): Deal with missing UIO_MAXIOV.
Correct test whether writev send all data.
* nscd/nscd_getgr_r.c (__nscd_getgr_r): Correct test whether readv
received all data.
1998-07-28 Mark Kettenis <kettenis@phys.uva.nl>
* nscd/nscd_getgr_r.c (__nscd_getgr_r): Deal with missing UIO_MAXIOV.
1998-07-28 Mark Kettenis <kettenis@phys.uva.nl>
* sysdeps/mach/hurd/dl-sysdep.c (open_file): Change assert call to
allow mode to be 0.
(__xstat): New function.
(__fxstat): New function.
(_dl_sysdep_read_whole_file): Removed. The implementation in
`elf/dl-misc.c' now also works for the Hurd.
Diffstat (limited to 'sunrpc')
-rw-r--r-- | sunrpc/Makefile | 3 | ||||
-rw-r--r-- | sunrpc/Versions | 5 | ||||
-rw-r--r-- | sunrpc/clnt_gen.c | 16 | ||||
-rw-r--r-- | sunrpc/clnt_unix.c | 573 | ||||
-rw-r--r-- | sunrpc/key_call.c | 200 | ||||
-rw-r--r-- | sunrpc/rpc/clnt.h | 22 | ||||
-rw-r--r-- | sunrpc/rpc/svc.h | 7 | ||||
-rw-r--r-- | sunrpc/svc_authux.c | 18 | ||||
-rw-r--r-- | sunrpc/svc_tcp.c | 4 | ||||
-rw-r--r-- | sunrpc/svc_unix.c | 496 |
10 files changed, 1313 insertions, 31 deletions
diff --git a/sunrpc/Makefile b/sunrpc/Makefile index 2ab2577f08..2dac9937f3 100644 --- a/sunrpc/Makefile +++ b/sunrpc/Makefile @@ -66,7 +66,8 @@ routines := auth_none auth_unix authuxprot bindrsvprt \ svc_tcp svc_udp xdr xdr_array xdr_float xdr_mem \ xdr_rec xdr_ref xdr_stdio publickey xdr_sizeof \ auth_des authdes_prot des_crypt des_impl des_soft \ - key_call key_prot netname openchild rtime svcauth_des xcrypt + key_call key_prot netname openchild rtime svcauth_des xcrypt\ + clnt_unix svc_unix others := rpcinfo install-bin := rpcgen diff --git a/sunrpc/Versions b/sunrpc/Versions index d956486f9f..98dfbbc79f 100644 --- a/sunrpc/Versions +++ b/sunrpc/Versions @@ -59,7 +59,7 @@ libc { authdes_create; authdes_getucred; authdes_pk_create; # c* - cbc_crypt; + cbc_crypt; clntunix_create; # d* des_setparity; @@ -87,6 +87,9 @@ libc { # r* rtime; + # s* + svcunix_create; svcunixfd_create; + # u* user2netname; diff --git a/sunrpc/clnt_gen.c b/sunrpc/clnt_gen.c index b1b35ca50a..f62618bdac 100644 --- a/sunrpc/clnt_gen.c +++ b/sunrpc/clnt_gen.c @@ -57,11 +57,27 @@ clnt_create (const char *hostname, u_long prog, u_long vers, size_t prtbuflen; char *prttmpbuf; struct sockaddr_in sin; + struct sockaddr_un sun; int sock; struct timeval tv; CLIENT *client; int herr; + if (strcmp (proto, "unix") == 0) + { + __bzero ((char *)&sun, sizeof (sun)); + sun.sun_family = AF_UNIX; + strcpy (sun.sun_path, hostname); + sock = RPC_ANYSOCK; + client = clntunix_create (&sun, prog, vers, &sock, 0, 0); + if (client == NULL) + return NULL; + tv.tv_sec = 25; + tv.tv_usec = 0; + clnt_control (client, CLSET_TIMEOUT, (char *)&tv); + return client; + } + hstbuflen = 1024; hsttmpbuf = __alloca (hstbuflen); while (__gethostbyname_r (hostname, &hostbuf, hsttmpbuf, hstbuflen, diff --git a/sunrpc/clnt_unix.c b/sunrpc/clnt_unix.c new file mode 100644 index 0000000000..848b752e6b --- /dev/null +++ b/sunrpc/clnt_unix.c @@ -0,0 +1,573 @@ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * clnt_unix.c, Implements a TCP/IP based, client side RPC. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * TCP based RPC supports 'batched calls'. + * A sequence of calls may be batched-up in a send buffer. The rpc call + * return immediately to the client even though the call was not necessarily + * sent. The batching occurs if the results' xdr routine is NULL (0) AND + * the rpc timeout value is zero (see clnt.h, rpc). + * + * Clients should NOT casually batch calls that in fact return results; that is, + * the server side should be aware that a call is batched and not produce any + * return message. Batched calls that produce many result messages can + * deadlock (netlock) the client and the server.... + * + * Now go hang yourself. + */ + +#include <netdb.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <rpc/rpc.h> +#include <sys/uio.h> +#include <sys/poll.h> +#include <sys/socket.h> +#include <rpc/pmap_clnt.h> + +#define MCALL_MSG_SIZE 24 + +struct ct_data + { + int ct_sock; + bool_t ct_closeit; + struct timeval ct_wait; + bool_t ct_waitset; /* wait set by clnt_control? */ + struct sockaddr_un ct_addr; + struct rpc_err ct_error; + char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */ + u_int ct_mpos; /* pos after marshal */ + XDR ct_xdrs; + }; + +static int readunix (char *, char *, int); +static int writeunix (char *, char *, int); + +static enum clnt_stat clntunix_call (CLIENT *, u_long, xdrproc_t, caddr_t, + xdrproc_t, caddr_t, struct timeval); +static void clntunix_abort (void); +static void clntunix_geterr (CLIENT *, struct rpc_err *); +static bool_t clntunix_freeres (CLIENT *, xdrproc_t, caddr_t); +static bool_t clntunix_control (CLIENT *, int, char *); +static void clntunix_destroy (CLIENT *); + +static struct clnt_ops unix_ops = +{ + clntunix_call, + clntunix_abort, + clntunix_geterr, + clntunix_freeres, + clntunix_destroy, + clntunix_control +}; + +/* + * Create a client handle for a tcp/ip connection. + * If *sockp<0, *sockp is set to a newly created TCP socket and it is + * connected to raddr. If *sockp non-negative then + * raddr is ignored. The rpc/tcp package does buffering + * similar to stdio, so the client must pick send and receive buffer sizes,]; + * 0 => use the default. + * If raddr->sin_port is 0, then a binder on the remote machine is + * consulted for the right port number. + * NB: *sockp is copied into a private area. + * NB: It is the clients responsibility to close *sockp. + * NB: The rpch->cl_auth is set null authentication. Caller may wish to set this + * something more useful. + */ +CLIENT * +clntunix_create (struct sockaddr_un *raddr, u_long prog, u_long vers, + int *sockp, u_int sendsz, u_int recvsz) +{ + CLIENT *h; + struct ct_data *ct = (struct ct_data *) mem_alloc (sizeof (*ct)); + struct timeval now; + struct rpc_msg call_msg; + int len; + + h = (CLIENT *) mem_alloc (sizeof (*h)); + if (h == NULL) + { + (void) fputs (_("clntunix_create: out of memory\n"), stderr); + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + goto fooy; + } + /* ct = (struct ct_data *) mem_alloc (sizeof (*ct)); */ + if (ct == NULL) + { + (void) fputs (_("clntunix_create: out of memory\n"), stderr); + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + goto fooy; + } + + /* + * If no socket given, open one + */ + if (*sockp < 0) + { + *sockp = __socket (AF_UNIX, SOCK_STREAM, 0); + len = strlen (raddr->sun_path) + sizeof (raddr->sun_family) + 1; + if (*sockp < 0 + || __connect (*sockp, (struct sockaddr *) raddr, len) < 0) + { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + if (*sockp != -1) + __close (*sockp); + goto fooy; + } + ct->ct_closeit = TRUE; + } + else + { + ct->ct_closeit = FALSE; + } + + /* + * Set up private data struct + */ + ct->ct_sock = *sockp; + ct->ct_wait.tv_usec = 0; + ct->ct_waitset = FALSE; + ct->ct_addr = *raddr; + + /* + * Initialize call message + */ + __gettimeofday (&now, (struct timezone *) 0); + call_msg.rm_xid = __getpid () ^ now.tv_sec ^ now.tv_usec; + call_msg.rm_direction = CALL; + call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; + call_msg.rm_call.cb_prog = prog; + call_msg.rm_call.cb_vers = vers; + + /* + * pre-serialize the static part of the call msg and stash it away + */ + xdrmem_create (&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE, XDR_ENCODE); + if (!xdr_callhdr (&(ct->ct_xdrs), &call_msg)) + { + if (ct->ct_closeit) + __close (*sockp); + goto fooy; + } + ct->ct_mpos = XDR_GETPOS (&(ct->ct_xdrs)); + XDR_DESTROY (&(ct->ct_xdrs)); + + /* + * Create a client handle which uses xdrrec for serialization + * and authnone for authentication. + */ + xdrrec_create (&(ct->ct_xdrs), sendsz, recvsz, + (caddr_t) ct, readunix, writeunix); + h->cl_ops = &unix_ops; + h->cl_private = (caddr_t) ct; + h->cl_auth = authnone_create (); + return h; + +fooy: + /* + * Something goofed, free stuff and barf + */ + mem_free ((caddr_t) ct, sizeof (struct ct_data)); + mem_free ((caddr_t) h, sizeof (CLIENT)); + return (CLIENT *) NULL; +} + +static enum clnt_stat +clntunix_call (h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout) + CLIENT *h; + u_long proc; + xdrproc_t xdr_args; + caddr_t args_ptr; + xdrproc_t xdr_results; + caddr_t results_ptr; + struct timeval timeout; +{ + struct ct_data *ct = (struct ct_data *) h->cl_private; + XDR *xdrs = &(ct->ct_xdrs); + struct rpc_msg reply_msg; + u_long x_id; + u_int32_t *msg_x_id = (u_int32_t *) (ct->ct_mcall); /* yuk */ + bool_t shipnow; + int refreshes = 2; + + if (!ct->ct_waitset) + { + ct->ct_wait = timeout; + } + + shipnow = + (xdr_results == (xdrproc_t) 0 && timeout.tv_sec == 0 + && timeout.tv_usec == 0) ? FALSE : TRUE; + +call_again: + xdrs->x_op = XDR_ENCODE; + ct->ct_error.re_status = RPC_SUCCESS; + x_id = ntohl (--(*msg_x_id)); + if ((!XDR_PUTBYTES (xdrs, ct->ct_mcall, ct->ct_mpos)) || + (!XDR_PUTLONG (xdrs, (long *) &proc)) || + (!AUTH_MARSHALL (h->cl_auth, xdrs)) || + (!(*xdr_args) (xdrs, args_ptr))) + { + if (ct->ct_error.re_status == RPC_SUCCESS) + ct->ct_error.re_status = RPC_CANTENCODEARGS; + (void) xdrrec_endofrecord (xdrs, TRUE); + return ct->ct_error.re_status; + } + if (!xdrrec_endofrecord (xdrs, shipnow)) + return ct->ct_error.re_status = RPC_CANTSEND; + if (!shipnow) + return RPC_SUCCESS; + /* + * Hack to provide rpc-based message passing + */ + if (timeout.tv_sec == 0 && timeout.tv_usec == 0) + return ct->ct_error.re_status = RPC_TIMEDOUT; + + + /* + * Keep receiving until we get a valid transaction id + */ + xdrs->x_op = XDR_DECODE; + while (TRUE) + { + reply_msg.acpted_rply.ar_verf = _null_auth; + reply_msg.acpted_rply.ar_results.where = NULL; + reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void; + if (!xdrrec_skiprecord (xdrs)) + return ct->ct_error.re_status; + /* now decode and validate the response header */ + if (!xdr_replymsg (xdrs, &reply_msg)) + { + if (ct->ct_error.re_status == RPC_SUCCESS) + continue; + return ct->ct_error.re_status; + } + if (reply_msg.rm_xid == x_id) + break; + } + + /* + * process header + */ + _seterr_reply (&reply_msg, &(ct->ct_error)); + if (ct->ct_error.re_status == RPC_SUCCESS) + { + if (!AUTH_VALIDATE (h->cl_auth, &reply_msg.acpted_rply.ar_verf)) + { + ct->ct_error.re_status = RPC_AUTHERROR; + ct->ct_error.re_why = AUTH_INVALIDRESP; + } + else if (!(*xdr_results) (xdrs, results_ptr)) + { + if (ct->ct_error.re_status == RPC_SUCCESS) + ct->ct_error.re_status = RPC_CANTDECODERES; + } + /* free verifier ... */ + if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) + { + xdrs->x_op = XDR_FREE; + (void) xdr_opaque_auth (xdrs, &(reply_msg.acpted_rply.ar_verf)); + } + } /* end successful completion */ + else + { + /* maybe our credentials need to be refreshed ... */ + if (refreshes-- && AUTH_REFRESH (h->cl_auth)) + goto call_again; + } /* end of unsuccessful completion */ + return ct->ct_error.re_status; +} + +static void +clntunix_geterr (CLIENT *h, struct rpc_err *errp) +{ + struct ct_data *ct = (struct ct_data *) h->cl_private; + + *errp = ct->ct_error; +} + +static bool_t +clntunix_freeres (cl, xdr_res, res_ptr) + CLIENT *cl; + xdrproc_t xdr_res; + caddr_t res_ptr; +{ + struct ct_data *ct = (struct ct_data *) cl->cl_private; + XDR *xdrs = &(ct->ct_xdrs); + + xdrs->x_op = XDR_FREE; + return (*xdr_res) (xdrs, res_ptr); +} + +static void +clntunix_abort () +{ +} + +static bool_t +clntunix_control (CLIENT *cl, int request, char *info) +{ + struct ct_data *ct = (struct ct_data *) cl->cl_private; + + + switch (request) + { + case CLSET_FD_CLOSE: + ct->ct_closeit = TRUE; + break; + case CLSET_FD_NCLOSE: + ct->ct_closeit = FALSE; + break; + case CLSET_TIMEOUT: + ct->ct_wait = *(struct timeval *) info; + break; + case CLGET_TIMEOUT: + *(struct timeval *) info = ct->ct_wait; + break; + case CLGET_SERVER_ADDR: + *(struct sockaddr_un *) info = ct->ct_addr; + break; + case CLGET_FD: + *(int *)info = ct->ct_sock; + break; + case CLGET_XID: + /* + * use the knowledge that xid is the + * first element in the call structure *. + * This will get the xid of the PREVIOUS call + */ + *(u_long *) info = ntohl (*(u_long *)ct->ct_mcall); + break; + case CLSET_XID: + /* This will set the xid of the NEXT call */ + *(u_long *) ct->ct_mcall = htonl (*(u_long *)info - 1); + /* decrement by 1 as clntunix_call() increments once */ + case CLGET_VERS: + /* + * This RELIES on the information that, in the call body, + * the version number field is the fifth field from the + * begining of the RPC header. MUST be changed if the + * call_struct is changed + */ + *(u_long *) info = ntohl (*(u_long *) (ct->ct_mcall + + 4 * BYTES_PER_XDR_UNIT)); + break; + case CLSET_VERS: + *(u_long *) (ct->ct_mcall + 4 * BYTES_PER_XDR_UNIT) + = htonl (*(u_long *) info); + break; + case CLGET_PROG: + /* + * This RELIES on the information that, in the call body, + * the program number field is the field from the + * begining of the RPC header. MUST be changed if the + * call_struct is changed + */ + *(u_long *) info = ntohl (*(u_long *) (ct->ct_mcall + + 3 * BYTES_PER_XDR_UNIT)); + break; + case CLSET_PROG: + *(u_long *) (ct->ct_mcall + 3 * BYTES_PER_XDR_UNIT) + = htonl(*(u_long *) info); + break; + /* The following are only possible with TI-RPC */ + case CLGET_RETRY_TIMEOUT: + case CLSET_RETRY_TIMEOUT: + case CLGET_SVC_ADDR: + case CLSET_SVC_ADDR: + case CLSET_PUSH_TIMOD: + case CLSET_POP_TIMOD: + default: + return FALSE; + } + return TRUE; +} + + +static void +clntunix_destroy (CLIENT *h) +{ + struct ct_data *ct = + (struct ct_data *) h->cl_private; + + if (ct->ct_closeit) + { + (void) close (ct->ct_sock); + } + XDR_DESTROY (&(ct->ct_xdrs)); + mem_free ((caddr_t) ct, sizeof (struct ct_data)); + mem_free ((caddr_t) h, sizeof (CLIENT)); +} + +struct cmessage { + struct cmsghdr cmsg; + struct cmsgcred cmcred; +}; + +static int +__msgread (int sock, void *buf, size_t cnt) +{ + struct iovec iov[1]; + struct msghdr msg; + struct cmessage cm; + int on = 1; + + iov[0].iov_base = buf; + iov[0].iov_len = cnt; + + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = (caddr_t)&cm; + msg.msg_controllen = sizeof(struct cmessage); + msg.msg_flags = 0; + + setsockopt (sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof (on)); + + return recvmsg (sock, &msg, 0); +} + +static int +__msgwrite (int sock, void *buf, size_t cnt) +{ +#ifndef SCM_CRED + /* We cannot implement this reliably. */ + __set_errno (ENOSYS); +#else + struct iovec iov[1]; + struct msghdr msg; + struct cmessage cm; + int len; + + iov[0].iov_base = buf; + iov[0].iov_len = cnt; + + cm.cmsg.cmsg_type = SCM_CREDS; + cm.cmsg.cmsg_level = SOL_SOCKET; + cm.cmsg.cmsg_len = sizeof (struct cmessage); + /* XXX I'm not sure, if gete?id() is always correct, or if we should use + get?id(). But since keyserv needs geteuid(), we have no other chance. + It would be much better, if the kernel could pass both to the server. */ + cm.cmcred.cmcred_pid = __getpid (); + cm.cmcred.cmcred_uid = __geteuid (); + cm.cmcred.cmcred_gid = __getegid (); + + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = (caddr_t) &cm; + msg.msg_controllen = sizeof (struct cmessage); + msg.msg_flags = 0; + + return sendmsg (sock, &msg, 0); +#endif +} + + +/* + * Interface between xdr serializer and unix connection. + * Behaves like the system calls, read & write, but keeps some error state + * around for the rpc level. + */ +static int +readunix (char *ctptr, char *buf, int len) +{ + struct ct_data *ct = (struct ct_data *) ctptr; + struct pollfd fd; + int milliseconds = ((ct->ct_wait.tv_sec * 1000) + + (ct->ct_wait.tv_usec / 1000)); + + if (len == 0) + return 0; + + fd.fd = ct->ct_sock; + fd.events = POLLIN; + while (TRUE) + { + switch (__poll (&fd, 1, milliseconds)) + { + case 0: + ct->ct_error.re_status = RPC_TIMEDOUT; + return -1; + + case -1: + if (errno == EINTR) + continue; + ct->ct_error.re_status = RPC_CANTRECV; + ct->ct_error.re_errno = errno; + return -1; + } + break; + } + switch (len = __msgread (ct->ct_sock, buf, len)) + { + + case 0: + /* premature eof */ + ct->ct_error.re_errno = ECONNRESET; + ct->ct_error.re_status = RPC_CANTRECV; + len = -1; /* it's really an error */ + break; + + case -1: + ct->ct_error.re_errno = errno; + ct->ct_error.re_status = RPC_CANTRECV; + break; + } + return len; +} + +static int +writeunix (char *ctptr, char *buf, int len) +{ + int i, cnt; + struct ct_data *ct = (struct ct_data *) ctptr; + + for (cnt = len; cnt > 0; cnt -= i, buf += i) + { + if ((i = __msgwrite (ct->ct_sock, buf, cnt)) == -1) + { + ct->ct_error.re_errno = errno; + ct->ct_error.re_status = RPC_CANTSEND; + return -1; + } + } + return len; +} diff --git a/sunrpc/key_call.c b/sunrpc/key_call.c index e59a8b7a37..0219eaf5ad 100644 --- a/sunrpc/key_call.c +++ b/sunrpc/key_call.c @@ -38,6 +38,7 @@ #include <stdio.h> #include <errno.h> +#include <fcntl.h> #include <signal.h> #include <unistd.h> #include <string.h> @@ -47,6 +48,7 @@ #include <sys/param.h> #include <sys/socket.h> #include <rpc/key_prot.h> +#include <bits/libc-lock.h> #define KEY_TIMEOUT 5 /* per-try timeout in seconds */ #define KEY_NRETRY 12 /* number of retries */ @@ -268,8 +270,8 @@ des_block *(*__key_gendes_LOCAL) (uid_t, char *) = 0; static int internal_function -key_call (u_long proc, xdrproc_t xdr_arg, char *arg, - xdrproc_t xdr_rslt, char *rslt) +key_call_keyenvoy (u_long proc, xdrproc_t xdr_arg, char *arg, + xdrproc_t xdr_rslt, char *rslt) { XDR xdrargs; XDR xdrrslt; @@ -283,28 +285,6 @@ key_call (u_long proc, xdrproc_t xdr_arg, char *arg, uid_t euid; static char MESSENGER[] = "/usr/etc/keyenvoy"; - if (proc == KEY_ENCRYPT_PK && __key_encryptsession_pk_LOCAL) - { - cryptkeyres *res; - res = (*__key_encryptsession_pk_LOCAL) (__geteuid (), arg); - *(cryptkeyres *) rslt = *res; - return 1; - } - else if (proc == KEY_DECRYPT_PK && __key_decryptsession_pk_LOCAL) - { - cryptkeyres *res; - res = (*__key_decryptsession_pk_LOCAL) (__geteuid (), arg); - *(cryptkeyres *) rslt = *res; - return 1; - } - else if (proc == KEY_GEN && __key_gendes_LOCAL) - { - des_block *res; - res = (*__key_gendes_LOCAL) (__geteuid (), 0); - *(des_block *) rslt = *res; - return 1; - } - success = 1; sigemptyset (&mask); sigaddset (&mask, SIGCHLD); @@ -365,3 +345,175 @@ key_call (u_long proc, xdrproc_t xdr_arg, char *arg, return success; } + +struct key_call_private { + CLIENT *client; /* Client handle */ + pid_t pid; /* process-id at moment of creation */ + uid_t uid; /* user-id at last authorization */ +}; +static struct key_call_private *key_call_private_main = NULL; +__libc_lock_define_initialized (static, keycall_lock) + +/* + * Keep the handle cached. This call may be made quite often. + */ +static CLIENT * +getkeyserv_handle (int vers) +{ + struct key_call_private *kcp = key_call_private_main; + struct timeval wait_time; + int fd; + struct sockaddr_un name; + int namelen = sizeof(struct sockaddr_un); + +#define TOTAL_TIMEOUT 30 /* total timeout talking to keyserver */ +#define TOTAL_TRIES 5 /* Number of tries */ + + if (kcp == (struct key_call_private *)NULL) + { + kcp = (struct key_call_private *)malloc (sizeof (*kcp)); + if (kcp == (struct key_call_private *)NULL) + return (CLIENT *) NULL; + + key_call_private_main = kcp; + kcp->client = NULL; + } + + /* if pid has changed, destroy client and rebuild */ + if (kcp->client != NULL && kcp->pid != __getpid ()) + { + clnt_destroy (kcp->client); + kcp->client = NULL; + } + + if (kcp->client != NULL) + { + /* if other side closed socket, build handle again */ + clnt_control (kcp->client, CLGET_FD, (char *)&fd); + if (getpeername (fd,(struct sockaddr *)&name,&namelen) == -1) + { + auth_destroy (kcp->client->cl_auth); + clnt_destroy (kcp->client); + kcp->client = NULL; + } + } + + if (kcp->client != NULL) + { + /* if uid has changed, build client handle again */ + if (kcp->uid != __geteuid ()) + { + kcp->uid = __geteuid (); + auth_destroy (kcp->client->cl_auth); + kcp->client->cl_auth = + authunix_create ((char *)"", kcp->uid, 0, 0, NULL); + if (kcp->client->cl_auth == NULL) + { + clnt_destroy (kcp->client); + kcp->client = NULL; + return ((CLIENT *) NULL); + } + } + /* Change the version number to the new one */ + clnt_control (kcp->client, CLSET_VERS, (void *)&vers); + return kcp->client; + } + + if ((kcp->client == (CLIENT *) NULL)) + /* Use the AF_UNIX transport */ + kcp->client = clnt_create ("/var/run/keyservsock", KEY_PROG, vers, "unix"); + + if (kcp->client == (CLIENT *) NULL) + return (CLIENT *) NULL; + + kcp->uid = __geteuid (); + kcp->pid = __getpid (); + kcp->client->cl_auth = authunix_create ((char *)"", kcp->uid, 0, 0, NULL); + if (kcp->client->cl_auth == NULL) + { + clnt_destroy (kcp->client); + kcp->client = NULL; + return (CLIENT *) NULL; + } + + wait_time.tv_sec = TOTAL_TIMEOUT/TOTAL_TRIES; + wait_time.tv_usec = 0; + clnt_control (kcp->client, CLSET_RETRY_TIMEOUT, + (char *)&wait_time); + if (clnt_control (kcp->client, CLGET_FD, (char *)&fd)) + fcntl (fd, F_SETFD, 1); /* make it "close on exec" */ + + return kcp->client; +} + +/* returns 0 on failure, 1 on success */ +static int +internal_function +key_call_socket (u_long proc, xdrproc_t xdr_arg, char *arg, + xdrproc_t xdr_rslt, char *rslt) +{ + CLIENT *clnt; + struct timeval wait_time; + int result = 0; + + __libc_lock_lock (keycall_lock); + if ((proc == KEY_ENCRYPT_PK) || (proc == KEY_DECRYPT_PK) || + (proc == KEY_NET_GET) || (proc == KEY_NET_PUT) || + (proc == KEY_GET_CONV)) + clnt = getkeyserv_handle(2); /* talk to version 2 */ + else + clnt = getkeyserv_handle(1); /* talk to version 1 */ + + if (clnt != NULL) + { + wait_time.tv_sec = TOTAL_TIMEOUT; + wait_time.tv_usec = 0; + + if (clnt_call (clnt, proc, xdr_arg, arg, xdr_rslt, rslt, + wait_time) == RPC_SUCCESS) + result = 1; + } + + __libc_lock_unlock (keycall_lock); + + return result; +} + +/* returns 0 on failure, 1 on success */ +static int +internal_function +key_call (u_long proc, xdrproc_t xdr_arg, char *arg, + xdrproc_t xdr_rslt, char *rslt) +{ + static int use_keyenvoy = 0; + + if (proc == KEY_ENCRYPT_PK && __key_encryptsession_pk_LOCAL) + { + cryptkeyres *res; + res = (*__key_encryptsession_pk_LOCAL) (__geteuid (), arg); + *(cryptkeyres *) rslt = *res; + return 1; + } + else if (proc == KEY_DECRYPT_PK && __key_decryptsession_pk_LOCAL) + { + cryptkeyres *res; + res = (*__key_decryptsession_pk_LOCAL) (__geteuid (), arg); + *(cryptkeyres *) rslt = *res; + return 1; + } + else if (proc == KEY_GEN && __key_gendes_LOCAL) + { + des_block *res; + res = (*__key_gendes_LOCAL) (__geteuid (), 0); + *(des_block *) rslt = *res; + return 1; + } + + if (!use_keyenvoy) + { + if (key_call_socket (proc, xdr_arg, arg, xdr_rslt, rslt)) + return 1; + use_keyenvoy = 1; + } + return key_call_keyenvoy (proc, xdr_arg, arg, xdr_rslt, rslt); +} diff --git a/sunrpc/rpc/clnt.h b/sunrpc/rpc/clnt.h index 1570cba4c2..b3852927a3 100644 --- a/sunrpc/rpc/clnt.h +++ b/sunrpc/rpc/clnt.h @@ -41,6 +41,7 @@ #include <sys/types.h> #include <rpc/types.h> #include <rpc/auth.h> +#include <sys/un.h> __BEGIN_DECLS @@ -282,7 +283,8 @@ extern CLIENT *clntraw_create __P ((__const u_long __prog, /* - * Generic client creation routine. Supported protocols are "udp" and "tcp" + * Generic client creation routine. Supported protocols are "udp", "tcp" and + * "unix" * CLIENT * * clnt_create(host, prog, vers, prot) * char *host; -- hostname @@ -341,6 +343,24 @@ extern CLIENT *clntudp_bufcreate __P ((struct sockaddr_in *__raddr, int *__sockp, u_int __sendsz, u_int __recvsz)); + +/* + * AF_UNIX based rpc + * CLIENT * + * clntunix_create(raddr, prog, vers, sockp, sendsz, recvsz) + * struct sockaddr_un *raddr; + * u_long prog; + * u_long version; + * register int *sockp; + * u_int sendsz; + * u_int recvsz; + */ +extern CLIENT *clntunix_create __P ((struct sockaddr_un *__raddr, + u_long __program, u_long __version, + int *__sockp, u_int __sendsz, + u_int __recvsz)); + + extern int callrpc __P ((__const char *__host, __const u_long __prognum, __const u_long __versnum, __const u_long __procnum, __const xdrproc_t __inproc, __const char *__in, diff --git a/sunrpc/rpc/svc.h b/sunrpc/rpc/svc.h index a53bc1015f..4ac2a5cdf0 100644 --- a/sunrpc/rpc/svc.h +++ b/sunrpc/rpc/svc.h @@ -305,6 +305,13 @@ extern SVCXPRT *svctcp_create __P ((int __sock, u_int __sendsize, u_int __recvsize)); +/* + * Unix based rpc. + */ +extern SVCXPRT *svcunix_create __P ((int __sock, u_int __sendsize, + u_int __recvsize, char *__path)); + + __END_DECLS #endif /* rpc/svc.h */ diff --git a/sunrpc/svc_authux.c b/sunrpc/svc_authux.c index 04cff9c53a..627b7e3faf 100644 --- a/sunrpc/svc_authux.c +++ b/sunrpc/svc_authux.c @@ -120,8 +120,22 @@ _svcauth_unix (struct svc_req *rqst, struct rpc_msg *msg) stat = AUTH_BADCRED; goto done; } - rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL; - rqst->rq_xprt->xp_verf.oa_length = 0; + + /* get the verifier */ + if ((u_int)msg->rm_call.cb_verf.oa_length) + { + rqst->rq_xprt->xp_verf.oa_flavor = + msg->rm_call.cb_verf.oa_flavor; + rqst->rq_xprt->xp_verf.oa_base = + msg->rm_call.cb_verf.oa_base; + rqst->rq_xprt->xp_verf.oa_length = + msg->rm_call.cb_verf.oa_length; + } + else + { + rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL; + rqst->rq_xprt->xp_verf.oa_length = 0; + } stat = AUTH_OK; done: XDR_DESTROY (&xdrs); diff --git a/sunrpc/svc_tcp.c b/sunrpc/svc_tcp.c index 75fa8705b6..0c2ce9650a 100644 --- a/sunrpc/svc_tcp.c +++ b/sunrpc/svc_tcp.c @@ -141,7 +141,7 @@ svctcp_create (int sock, u_int sendsize, u_int recvsize) { if ((sock = __socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { - perror (_("svctcp_.c - udp socket creation problem")); + perror (_("svc_tcp.c - tcp socket creation problem")); return (SVCXPRT *) NULL; } madesock = TRUE; @@ -156,7 +156,7 @@ svctcp_create (int sock, u_int sendsize, u_int recvsize) if ((getsockname (sock, (struct sockaddr *) &addr, &len) != 0) || (listen (sock, 2) != 0)) { - perror (_("svctcp_.c - cannot getsockname or listen")); + perror (_("svc_tcp.c - cannot getsockname or listen")); if (madesock) (void) __close (sock); return (SVCXPRT *) NULL; diff --git a/sunrpc/svc_unix.c b/sunrpc/svc_unix.c new file mode 100644 index 0000000000..59ae7a54e7 --- /dev/null +++ b/sunrpc/svc_unix.c @@ -0,0 +1,496 @@ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * svc_unix.c, Server side for TCP/IP based RPC. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * Actually implements two flavors of transporter - + * a unix rendezvouser (a listener and connection establisher) + * and a record/unix stream. + */ + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <rpc/rpc.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <errno.h> +#include <stdlib.h> + +/* + * Ops vector for AF_UNIX based rpc service handle + */ +static bool_t svcunix_recv (SVCXPRT *, struct rpc_msg *); +static enum xprt_stat svcunix_stat (SVCXPRT *); +static bool_t svcunix_getargs (SVCXPRT *, xdrproc_t, caddr_t); +static bool_t svcunix_reply (SVCXPRT *, struct rpc_msg *); +static bool_t svcunix_freeargs (SVCXPRT *, xdrproc_t, caddr_t); +static void svcunix_destroy (SVCXPRT *); + +static const struct xp_ops svcunix_op = +{ + svcunix_recv, + svcunix_stat, + svcunix_getargs, + svcunix_reply, + svcunix_freeargs, + svcunix_destroy +}; + +/* + * Ops vector for AF_UNIX rendezvous handler + */ +static bool_t rendezvous_request (SVCXPRT *, struct rpc_msg *); +static enum xprt_stat rendezvous_stat (SVCXPRT *); + +static const struct xp_ops svcunix_rendezvous_op = +{ + rendezvous_request, + rendezvous_stat, + (bool_t (*) (SVCXPRT *, xdrproc_t, caddr_t)) abort, + (bool_t (*) (SVCXPRT *, struct rpc_msg *)) abort, + (bool_t (*) (SVCXPRT *, xdrproc_t, caddr_t)) abort, + svcunix_destroy +}; + +static int readunix (char*, char *, int); +static int writeunix (char *, char *, int); +static SVCXPRT *makefd_xprt (int, u_int, u_int) internal_function; + +struct unix_rendezvous { /* kept in xprt->xp_p1 */ + u_int sendsize; + u_int recvsize; +}; + +struct unix_conn { /* kept in xprt->xp_p1 */ + enum xprt_stat strm_stat; + u_long x_id; + XDR xdrs; + char verf_body[MAX_AUTH_BYTES]; +}; + +/* + * Usage: + * xprt = svcunix_create(sock, send_buf_size, recv_buf_size); + * + * Creates, registers, and returns a (rpc) unix based transporter. + * Once *xprt is initialized, it is registered as a transporter + * see (svc.h, xprt_register). This routine returns + * a NULL if a problem occurred. + * + * If sock<0 then a socket is created, else sock is used. + * If the socket, sock is not bound to a port then svcunix_create + * binds it to an arbitrary port. The routine then starts a unix + * listener on the socket's associated port. In any (successful) case, + * xprt->xp_sock is the registered socket number and xprt->xp_port is the + * associated port number. + * + * Since unix streams do buffered io similar to stdio, the caller can specify + * how big the send and receive buffers are via the second and third parms; + * 0 => use the system default. + */ +SVCXPRT * +svcunix_create (int sock, u_int sendsize, u_int recvsize, char *path) +{ + bool_t madesock = FALSE; + SVCXPRT *xprt; + struct unix_rendezvous *r; + struct sockaddr_un addr; + socklen_t len = sizeof (struct sockaddr_in); + + if (sock == RPC_ANYSOCK) + { + if ((sock = __socket (AF_UNIX, SOCK_STREAM, 0)) < 0) + { + perror (_("svc_unix.c - AF_UNIX socket creation problem")); + return (SVCXPRT *) NULL; + } + madesock = TRUE; + } + memset (&addr, '\0', sizeof (addr)); + addr.sun_family = AF_UNIX; + len = strlen (addr.sun_path) + 1; + memcpy (addr.sun_path, path, len); + len += sizeof (addr.sun_family); + + bind (sock, (struct sockaddr *) &addr, len); + + if (getsockname (sock, (struct sockaddr *) &addr, &len) != 0 + || listen (sock, 2) != 0) + { + perror (_("svc_unix.c - cannot getsockname or listen")); + if (madesock) + __close (sock); + return (SVCXPRT *) NULL; + } + + r = (struct unix_rendezvous *) mem_alloc (sizeof (*r)); + if (r == NULL) + { + fputs (_("svcunix_create: out of memory\n"), stderr); + return NULL; + } + r->sendsize = sendsize; + r->recvsize = recvsize; + xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT)); + if (xprt == NULL) + { + fputs (_("svcunix_create: out of memory\n"), stderr); + return NULL; + } + xprt->xp_p2 = NULL; + xprt->xp_p1 = (caddr_t) r; + xprt->xp_verf = _null_auth; + xprt->xp_ops = &svcunix_rendezvous_op; + xprt->xp_port = -1; + xprt->xp_sock = sock; + xprt_register (xprt); + return xprt; +} + +/* + * Like svunix_create(), except the routine takes any *open* UNIX file + * descriptor as its first input. + */ +SVCXPRT * +svcunixfd_create (int fd, u_int sendsize, u_int recvsize) +{ + return makefd_xprt (fd, sendsize, recvsize); +} + +static SVCXPRT * +internal_function +makefd_xprt (int fd, u_int sendsize, u_int recvsize) +{ + SVCXPRT *xprt; + struct unix_conn *cd; + + xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT)); + if (xprt == (SVCXPRT *) NULL) + { + (void) fputs (_("svc_unix: makefd_xprt: out of memory\n"), stderr); + goto done; + } + cd = (struct unix_conn *) mem_alloc (sizeof (struct unix_conn)); + if (cd == (struct unix_conn *) NULL) + { + (void) fputs (_("svc_unix: makefd_xprt: out of memory\n"), stderr); + mem_free ((char *) xprt, sizeof (SVCXPRT)); + xprt = (SVCXPRT *) NULL; + goto done; + } + cd->strm_stat = XPRT_IDLE; + xdrrec_create (&(cd->xdrs), sendsize, recvsize, + (caddr_t) xprt, readunix, writeunix); + xprt->xp_p2 = NULL; + xprt->xp_p1 = (caddr_t) cd; + xprt->xp_verf.oa_base = cd->verf_body; + xprt->xp_addrlen = 0; + xprt->xp_ops = &svcunix_op; /* truly deals with calls */ + xprt->xp_port = 0; /* this is a connection, not a rendezvouser */ + xprt->xp_sock = fd; + xprt_register (xprt); +done: + return xprt; +} + +static bool_t +rendezvous_request (SVCXPRT *xprt, struct rpc_msg *errmsg) +{ + int sock; + struct unix_rendezvous *r; + struct sockaddr_un addr; + struct sockaddr_in in_addr; + socklen_t len; + + r = (struct unix_rendezvous *) xprt->xp_p1; +again: + len = sizeof (struct sockaddr_un); + if ((sock = accept (xprt->xp_sock, (struct sockaddr *) &addr, &len)) < 0) + { + if (errno == EINTR) + goto again; + return FALSE; + } + /* + * make a new transporter (re-uses xprt) + */ + memset (&in_addr, '\0', sizeof (in_addr)); + in_addr.sin_family = AF_UNIX; + xprt = makefd_xprt (sock, r->sendsize, r->recvsize); + xprt->xp_raddr = in_addr; + xprt->xp_addrlen = len; + return FALSE; /* there is never an rpc msg to be processed */ +} + +static enum xprt_stat +rendezvous_stat (SVCXPRT *xprt) +{ + return XPRT_IDLE; +} + +static void +svcunix_destroy (SVCXPRT *xprt) +{ + struct unix_conn *cd = (struct unix_conn *) xprt->xp_p1; + + xprt_unregister (xprt); + __close (xprt->xp_sock); + if (xprt->xp_port != 0) + { + /* a rendezvouser socket */ + xprt->xp_port = 0; + } + else + { + /* an actual connection socket */ + XDR_DESTROY (&(cd->xdrs)); + } + mem_free ((caddr_t) cd, sizeof (struct unix_conn)); + mem_free ((caddr_t) xprt, sizeof (SVCXPRT)); +} + +struct cmessage { + struct cmsghdr cmsg; + struct cmsgcred cmcred; +}; + +/* XXX This is not thread safe, but since the main functions in svc.c + and the rpcgen generated *_svc functions for the daemon are also not + thread safe and uses static global variables, it doesn't matter. */ +static struct cmessage cm; + +static int +__msgread (int sock, void *buf, size_t cnt) +{ + struct iovec iov[1]; + struct msghdr msg; + int len, on = 1; + + iov[0].iov_base = buf; + iov[0].iov_len = cnt; + + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = (caddr_t) &cm; + msg.msg_controllen = sizeof (struct cmessage); + msg.msg_flags = 0; + + setsockopt (sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof (on)); + + return recvmsg (sock, &msg, 0); +} + +static int +__msgwrite (int sock, void *buf, size_t cnt) +{ +#ifndef SCM_CRED + /* We cannot implement this reliably. */ + __set_errno (ENOSYS); +#else + struct iovec iov[1]; + struct msghdr msg; + int len; + + iov[0].iov_base = buf; + iov[0].iov_len = cnt; + + cm.cmsg.cmsg_type = SCM_CREDS; + cm.cmsg.cmsg_level = SOL_SOCKET; + cm.cmsg.cmsg_len = sizeof (struct cmessage); + /* XXX I'm not sure, if we really should use gete?id(), or get?id(). + It would be much better, if the kernel could pass both to the + client. */ + cm.cmcred.cmcred_pid = __getpid (); + cm.cmcred.cmcred_uid = __geteuid (); + cm.cmcred.cmcred_gid = __getegid (); + + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = (caddr_t) &cm; + msg.msg_controllen = sizeof (struct cmessage); + msg.msg_flags = 0; + + return sendmsg (sock, &msg, 0); +#endif +} + +/* + * All read operations timeout after 35 seconds. + * A timeout is fatal for the connection. + */ +static struct timeval wait_per_try = {35, 0}; + +/* + * reads data from the unix connection. + * any error is fatal and the connection is closed. + * (And a read of zero bytes is a half closed stream => error.) + */ +static int +readunix (char *xprtptr, char *buf, int len) +{ + SVCXPRT *xprt = (SVCXPRT *) xprtptr; + int sock = xprt->xp_sock; +#ifdef FD_SETSIZE + fd_set readfds; +#else + int mask = 1 << sock; + int readfds; +#endif /* def FD_SETSIZE */ + while (1) + { + struct timeval timeout = wait_per_try; + readfds = svc_fdset; +#ifdef FD_SETSIZE + FD_SET (sock, &readfds); +#else + readfds |= (1 << sock); +#endif /* def FD_SETSIZE */ + if (__select (_rpc_dtablesize (), &readfds, (fd_set *) NULL, + (fd_set *) NULL, &timeout) <= 0) + { + if (errno == EINTR) + continue; + goto fatal_err; + } + +#ifdef FD_SETSIZE + if (FD_ISSET (sock, &readfds)) +#else + if (readfds == mask) +#endif /* def FD_SETSIZE */ + break; + + svc_getreqset (&readfds); + } + + if ((len = __msgread (sock, buf, len)) > 0) + return len; + + fatal_err: + ((struct unix_conn *) (xprt->xp_p1))->strm_stat = XPRT_DIED; + return -1; +} + +/* + * writes data to the unix connection. + * Any error is fatal and the connection is closed. + */ +static int +writeunix (char *xprtptr, char * buf, int len) +{ + SVCXPRT *xprt = (SVCXPRT *) xprtptr; + int i, cnt; + + for (cnt = len; cnt > 0; cnt -= i, buf += i) + { + if ((i = __msgwrite (xprt->xp_sock, buf, cnt)) < 0) + { + ((struct unix_conn *) (xprt->xp_p1))->strm_stat = XPRT_DIED; + return -1; + } + } + return len; +} + +static enum xprt_stat +svcunix_stat (SVCXPRT *xprt) +{ + struct unix_conn *cd = + (struct unix_conn *) (xprt->xp_p1); + + if (cd->strm_stat == XPRT_DIED) + return XPRT_DIED; + if (!xdrrec_eof (&(cd->xdrs))) + return XPRT_MOREREQS; + return XPRT_IDLE; +} + +static bool_t +svcunix_recv (SVCXPRT *xprt, struct rpc_msg *msg) +{ + struct unix_conn *cd = (struct unix_conn *) (xprt->xp_p1); + XDR *xdrs = &(cd->xdrs); + + xdrs->x_op = XDR_DECODE; + xdrrec_skiprecord (xdrs); + if (xdr_callmsg (xdrs, msg)) + { + cd->x_id = msg->rm_xid; + /* set up verifiers */ + msg->rm_call.cb_verf.oa_flavor = AUTH_UNIX; + msg->rm_call.cb_verf.oa_base = (caddr_t) &cm; + msg->rm_call.cb_verf.oa_length = sizeof (cm); + return TRUE; + } + cd->strm_stat = XPRT_DIED; /* XXXX */ + return FALSE; +} + +static bool_t +svcunix_getargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) +{ + return (*xdr_args) (&(((struct unix_conn *) (xprt->xp_p1))->xdrs), + args_ptr); +} + +static bool_t +svcunix_freeargs (xprt, xdr_args, args_ptr) + SVCXPRT *xprt; + xdrproc_t xdr_args; + caddr_t args_ptr; +{ + XDR *xdrs = &(((struct unix_conn *) (xprt->xp_p1))->xdrs); + + xdrs->x_op = XDR_FREE; + return (*xdr_args) (xdrs, args_ptr); +} + +static bool_t +svcunix_reply (xprt, msg) + SVCXPRT *xprt; + struct rpc_msg *msg; +{ + struct unix_conn *cd = (struct unix_conn *) (xprt->xp_p1); + XDR *xdrs = &(cd->xdrs); + bool_t stat; + + xdrs->x_op = XDR_ENCODE; + msg->rm_xid = cd->x_id; + stat = xdr_replymsg (xdrs, msg); + (void) xdrrec_endofrecord (xdrs, TRUE); + return stat; +} |