? src/libvirtd.c ? src/remote_internal.c ? src/remote_internal.h ? src/remote_rpc.x ? src/sunrpc/README ? src/sunrpc/clnt_ext.c ? src/sunrpc/clnt_ext.h ? src/sunrpc/clnt_gnutls.c ? src/sunrpc/clnt_gnutls.h ? src/sunrpc/clnt_tcp2.c ? src/sunrpc/clnt_tcp2.h ? src/sunrpc/create_xid.c ? src/sunrpc/svc_gnutls.c ? src/sunrpc/svc_gnutls.h ? src/sunrpc/svc_tcp2.c ? src/sunrpc/svc_tcp2.h Index: config.h.in =================================================================== RCS file: /data/cvs/libvirt/config.h.in,v retrieving revision 1.7 diff -u -r1.7 config.h.in --- config.h.in 31 Oct 2006 10:25:13 -0000 1.7 +++ config.h.in 30 Jan 2007 17:22:07 -0000 @@ -28,6 +28,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H +/* Define to 1 if you have the `gnutls' library (-lgnutls). */ +#undef HAVE_LIBGNUTLS + /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H Index: configure.in =================================================================== RCS file: /data/cvs/libvirt/configure.in,v retrieving revision 1.52 diff -u -r1.52 configure.in --- configure.in 22 Jan 2007 15:31:00 -0000 1.52 +++ configure.in 30 Jan 2007 17:22:07 -0000 @@ -146,6 +146,11 @@ AC_SUBST(LIBXML_CONFIG) AC_SUBST(LIBXML_MIN_VERSION) +dnl GnuTLS library +AC_CHECK_LIB(gnutls, gnutls_handshake, + [], + [AC_MSG_ERROR([gnutls library not found])]) + dnl virsh libraries AC_CHECK_LIB(curses, initscr, [VIRSH_LIBS="$VIRSH_LIBS -lcurses"], Index: include/libvirt/virterror.h =================================================================== RCS file: /data/cvs/libvirt/include/libvirt/virterror.h,v retrieving revision 1.17 diff -u -r1.17 virterror.h --- include/libvirt/virterror.h 8 Nov 2006 16:55:20 -0000 1.17 +++ include/libvirt/virterror.h 30 Jan 2007 17:22:07 -0000 @@ -46,7 +46,8 @@ VIR_FROM_DOM, /* Error when operating on a domain */ VIR_FROM_RPC, /* Error in the XML-RPC code */ VIR_FROM_PROXY, /* Error in the proxy code */ - VIR_FROM_CONF /* Error in the configuration file handling */ + VIR_FROM_CONF, /* Error in the configuration file handling */ + VIR_FROM_REMOTE /* Error from remote driver */ } virErrorDomain; @@ -113,7 +114,8 @@ VIR_ERR_PARSE_FAILED, /* failed to parse a conf file */ VIR_ERR_CONF_SYNTAX, /* failed to parse the syntax of a conf file */ VIR_ERR_WRITE_FAILED, /* failed to write a conf file */ - VIR_ERR_XML_DETAIL /* detail of an XML error */ + VIR_ERR_XML_DETAIL, /* detail of an XML error */ + VIR_ERR_RPC /* some sort of RPC error */ } virErrorNumber; /** Index: src/.cvsignore =================================================================== RCS file: /data/cvs/libvirt/src/.cvsignore,v retrieving revision 1.2 diff -u -r1.2 .cvsignore --- src/.cvsignore 5 Jul 2006 21:52:52 -0000 1.2 +++ src/.cvsignore 30 Jan 2007 17:22:07 -0000 @@ -5,3 +5,8 @@ *.lo *.la virsh +libvirtd +remote_rpc_clnt.c +remote_rpc_svc.c +remote_rpc_xdr.c +remote_rpc.h \ No newline at end of file Index: src/Makefile.am =================================================================== RCS file: /data/cvs/libvirt/src/Makefile.am,v retrieving revision 1.31 diff -u -r1.31 Makefile.am --- src/Makefile.am 26 Jan 2007 11:54:29 -0000 1.31 +++ src/Makefile.am 30 Jan 2007 17:22:07 -0000 @@ -28,15 +28,41 @@ driver.h \ proxy_internal.c proxy_internal.h \ conf.c conf.h \ - xm_internal.c xm_internal.h + xm_internal.c xm_internal.h \ + sunrpc/create_xid.c remote_rpc_xdr.c \ + remote_rpc_clnt.c remote_rpc.h \ + sunrpc/clnt_ext.h sunrpc/clnt_gnutls.h \ + sunrpc/clnt_tcp2.h sunrpc/clnt_ext.c \ + sunrpc/clnt_gnutls.c sunrpc/clnt_tcp2.c \ + remote_internal.c remote_internal.h bin_PROGRAMS = virsh +sbin_PROGRAMS = libvirtd virsh_SOURCES = virsh.c console.c console.h virsh_LDFLAGS = virsh_DEPENDENCIES = $(DEPS) virsh_LDADD = $(LDADDS) $(VIRSH_LIBS) +libvirtd_SOURCES = \ + remote_rpc_svc.c \ + sunrpc/svc_gnutls.c sunrpc/svc_gnutls.h \ + sunrpc/svc_tcp2.c sunrpc/svc_tcp2.h \ + libvirtd.c +libvirtd_LDFLAGS = +libvirtd_DEPENDENCIES = $(DEPS) +libvirtd_LDADD = $(LDADDS) remote_rpc_xdr.o + +# Build client and server stubs. +# 'rpcgen' program comes with glibc. +# This is convoluted because we need to build the server stubs +# without main() (-m option), but you can't just do that in a +# simple way. +remote_rpc_clnt.c remote_rpc.h remote_rpc_svc.c remote_rpc_xdr.c: remote_rpc.x + rpcgen remote_rpc.x + rm -f remote_rpc_svc.c + rpcgen -m remote_rpc.x > remote_rpc_svc.c + # # target to ease building test programs # Index: src/driver.h =================================================================== RCS file: /data/cvs/libvirt/src/driver.h,v retrieving revision 1.16 diff -u -r1.16 driver.h --- src/driver.h 22 Jan 2007 16:21:27 -0000 1.16 +++ src/driver.h 30 Jan 2007 17:22:07 -0000 @@ -22,7 +22,8 @@ VIR_DRV_XEN_DAEMON = 3, VIR_DRV_TEST = 4, VIR_DRV_XEN_PROXY = 5, - VIR_DRV_XEN_XM = 6 + VIR_DRV_XEN_XM = 6, + VIR_DRV_REMOTE = 7 } virDrvNo; Index: src/internal.h =================================================================== RCS file: /data/cvs/libvirt/src/internal.h,v retrieving revision 1.28 diff -u -r1.28 internal.h --- src/internal.h 23 Jan 2007 14:39:45 -0000 1.28 +++ src/internal.h 30 Jan 2007 17:22:07 -0000 @@ -117,6 +117,13 @@ struct sockaddr_un addr_un; /* the unix address */ struct sockaddr_in addr_in; /* the inet address */ + /* driver private data + * (Ought to replace the above ad-hoc Xen data, IMHO anyway. + * Currently only the 'remote' driver uses this. + * - RWMJ). + */ + void *private; + /* error stuff */ virError err; /* the last error */ virErrorFunc handler; /* associated handlet */ Index: src/libvirt.c =================================================================== RCS file: /data/cvs/libvirt/src/libvirt.c,v retrieving revision 1.53 diff -u -r1.53 libvirt.c --- src/libvirt.c 23 Jan 2007 14:39:45 -0000 1.53 +++ src/libvirt.c 30 Jan 2007 17:22:09 -0000 @@ -30,6 +30,7 @@ #include "xs_internal.h" #include "xm_internal.h" #include "proxy_internal.h" +#include "remote_internal.h" #include "xml.h" #include "test.h" @@ -79,6 +80,7 @@ xenStoreRegister(); xenXMRegister(); testRegister(); + remoteRegister (); return(0); } Index: src/virsh.c =================================================================== RCS file: /data/cvs/libvirt/src/virsh.c,v retrieving revision 1.47 diff -u -r1.47 virsh.c --- src/virsh.c 28 Jan 2007 19:47:36 -0000 1.47 +++ src/virsh.c 30 Jan 2007 17:22:10 -0000 @@ -2760,7 +2760,7 @@ end = end ? : argc; /* standard (non-command) options */ - while ((arg = getopt_long(end, argv, "d:hqtcv", opt, &idx)) != -1) { + while ((arg = getopt_long(end, argv, "d:hqtc:v", opt, &idx)) != -1) { switch (arg) { case 'd': ctl->debug = atoi(optarg); Index: src/virterror.c =================================================================== RCS file: /data/cvs/libvirt/src/virterror.c,v retrieving revision 1.19 diff -u -r1.19 virterror.c --- src/virterror.c 8 Nov 2006 16:55:20 -0000 1.19 +++ src/virterror.c 30 Jan 2007 17:22:10 -0000 @@ -268,6 +268,9 @@ case VIR_FROM_RPC: dom = "XML-RPC "; break; + case VIR_FROM_REMOTE: + dom = "Remote "; + break; } if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) { domain = err->dom->name; @@ -582,6 +585,12 @@ else errmsg = "%s"; break; + case VIR_ERR_RPC: + if (info == NULL) + errmsg = _("RPC error"); + else + errmsg = "%s"; + break; } return (errmsg); } --- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000 +++ src/libvirtd.c 2007-01-30 16:58:42.000000000 +0000 @@ -0,0 +1,257 @@ +/* + * libvirtd: This is a small server to be used in conjunction with + * the "remote" driver (remote_internal.c). + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sunrpc/svc_gnutls.h" +#include "sunrpc/svc_tcp2.h" +#include "remote_internal.h" +#include "remote_rpc.h" + +// XXX Replace with variables from a configuration file +// XXX TCP services should be disabled by default +#define listen_tls 1 +#define listen_tcp 1 +#define listen_unix 1 + +// XXX Still to decide where these certificates should be located. +#define KEYFILE "newkey.pem" +#define CERTFILE "newcert.pem" +#define CAFILE "demoCA/cacert.pem" +//#define CRLFILE "crl.pem" + +// This is autogenerated, in remote_rpc_svc.c +extern void libvirtremote_1 (struct svc_req *rqstp, register SVCXPRT *transp); + +/*----------------------------------------------------------------------*/ +/* Server side of the remote procedure calls. */ + +int * +remote_rpc_open_1_svc (char **name, + struct svc_req *req __attribute__((unused))) +{ + static int retcode = 0; + + // Before this is going to work, we will have to assign a cookie + // to each caller to represent their server-side virConnectPtr. + // XXX XXX XXX + //virConnectOpen (name); + + printf ("libvirtd: open: name = %s\n", *name); + return &retcode; +} + +int * +remote_rpc_close_1_svc (void *p __attribute__((unused)), + struct svc_req *req __attribute__((unused))) +{ + static int retcode = 0; + + printf ("libvirtd: close\n"); + return &retcode; +} + +char ** +remote_rpc_type_1_svc (void *p __attribute__((unused)), + struct svc_req *req __attribute__((unused))) +{ + static char *dummy = "remote"; + + printf ("libvirtd: type\n"); + return &dummy; +} + +struct version_ret * +remote_rpc_version_1_svc (void *p __attribute__((unused)), + struct svc_req *req __attribute__((unused))) +{ + static struct version_ret ret = { .retcode = 0, .hvVer = 1000000 }; + + printf ("libvirtd: version\n"); + return &ret; +} + +/*----------------------------------------------------------------------*/ +/* Main function. */ + +static gnutls_certificate_credentials_t x509_cred; +static gnutls_dh_params_t dh_params; + +static void generate_dh_params (void); +static int make_sockets (int *fds, int max_fds, int *nfds_r, + const char *service); + +int +main (int argc, char *argv[]) +{ + if (!listen_tls || !listen_tcp || !listen_unix) { + fprintf (stderr, "libvirtd: you need to enable at least one service in the configuration file\n"); + exit (1); + } + + if (listen_tls) { + /* Initialise GnuTLS. */ + gnutls_global_init (); + + gnutls_certificate_allocate_credentials (&x509_cred); + gnutls_certificate_set_x509_trust_file (x509_cred, CAFILE, + GNUTLS_X509_FMT_PEM); + + // gnutls_certificate_set_x509_crl_file (x509_cred, CRLFILE, + // GNUTLS_X509_FMT_PEM); + + gnutls_certificate_set_x509_key_file (x509_cred, CERTFILE, KEYFILE, + GNUTLS_X509_FMT_PEM); + + generate_dh_params (); + gnutls_certificate_set_dh_params (x509_cred, dh_params); + + int fds[2]; + int nfds = 0; + if (make_sockets (fds, 2, &nfds, LIBVIRTD_GNUTLS_PORT) == -1) + exit (1); + + int i; + for (i = 0; i < nfds; ++i) { + SVCXPRT *transp = svcgnutls_create (fds[i], 0, 0, x509_cred); + if (!transp) { + fprintf (stderr, "libvirtd: cannot create TLS service\n"); + exit (1); + } + + /* Because final arg is 0, this will not register with portmap. */ + if (!svc_register (transp, LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, + libvirtremote_1, 0)) { + fprintf (stderr, "libvirtd: unable to register (LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, 0)\n"); + exit (1); + } + } + } + + if (listen_tcp) { + int fds[2]; + int nfds = 0; + if (make_sockets (fds, 2, &nfds, LIBVIRTD_TCP_PORT) == -1) + exit (1); + + int i; + for (i = 0; i < nfds; ++i) { + SVCXPRT *transp = svctcp2_create (fds[i], 0, 0); + if (!transp) { + fprintf (stderr, "libvirtd: cannot create TCP service\n"); + exit (1); + } + + /* Because final arg is 0, this will not register with portmap. */ + if (!svc_register (transp, LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, + libvirtremote_1, 0)) { + fprintf (stderr, "libvirtd: unable to register (LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, 0)\n"); + exit (1); + } + } + } + + if (listen_unix) { + char *sockname = LIBVIRTD_UNIX_SOCKET; + int sock = RPC_ANYSOCK; + SVCXPRT *transp = svcunix_create (sock, 0, 0, sockname); + if (!transp) { + fprintf (stderr, "libvirtd: cannot create Unix domain socket service\n"); + exit (1); + } + + /* Because final arg is 0, this will not register with portmap. */ + if (!svc_register (transp, LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, + libvirtremote_1, 0)) { + fprintf (stderr, "libvirtd: unable to register (LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, 0)\n"); + exit (1); + } + } + + svc_run (); + fprintf (stderr, "libvirtd: svc_run should not return\n"); + exit (1); +} + +// XXX DH_BITS has to match the value define in svc_gnutls.c +#define DH_BITS 1024 + +static void +generate_dh_params (void) +{ + /* Generate Diffie Hellman parameters - for use with DHE + * kx algorithms. These should be discarded and regenerated + * once a day, once a week or once a month. Depending on the + * security requirements. + */ + gnutls_dh_params_init (&dh_params); + gnutls_dh_params_generate2 (dh_params, DH_BITS); +} + +// See: http://people.redhat.com/drepper/userapi-ipv6.html +static int +make_sockets (int *fds, int max_fds, int *nfds_r, const char *service) +{ + struct addrinfo *ai; + struct addrinfo hints; + memset (&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; + hints.ai_socktype = SOCK_STREAM; + + int e = getaddrinfo (NULL, service, &hints, &ai); + if (e != 0) { + fprintf (stderr, "getaddrinfo: %s\n", gai_strerror (e)); + return -1; + } + + struct addrinfo *runp = ai; + while (runp && *nfds_r < max_fds) { + fds[*nfds_r] = socket (runp->ai_family, runp->ai_socktype, + runp->ai_protocol); + if (fds[*nfds_r] == -1) { + perror ("socket"); + return -1; + } + + int opt = 1; + setsockopt (fds[*nfds_r], SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt); + + if (bind (fds[*nfds_r], runp->ai_addr, runp->ai_addrlen) == -1) { + if (errno != EADDRINUSE) { + perror ("bind"); + return -1; + } + close (fds[*nfds_r]); + } + else { + if (listen (fds[*nfds_r], SOMAXCONN) == -1) { + perror ("listen"); + return -1; + } + ++*nfds_r; + } + runp = runp->ai_next; + } + + freeaddrinfo (ai); + + return 0; +} --- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000 +++ src/remote_internal.c 2007-01-30 16:31:59.000000000 +0000 @@ -0,0 +1,663 @@ +/* + * remote_internal.c: driver to provide access to libvirtd running + * on a remote machine. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones + */ + +/* Architecture and notes: + * + * virConnectOpen ("remote:....") invokes this driver. Depending + * on the exact contents of the ellipsis "...." in the name string + * we will try some method to connect to a libvirtd daemon, running + * on a remote machine (or sometimes running on the local machine). + * + * All other vir* calls made on this connection are forwarded + * to the libvirtd daemon which carries out the requested action. + * So for example if you call virDomainCreateLinux, then the + * domain gets created on the remote machine, and virConnectListDomains + * lists domains running on the remote machine. + * + * Connections can be authenticated and encrypted -- it depends + * on the transport selected by the name string. + * + * The current implementation uses SunRPC layered over one of: + * - GnuTLS (an SSL/TLS library providing enterprise-level + * authentication and encryption) + * - a local Unix domain socket + * - ssh or another external program such as rsh + * - a plain TCP socket (unencrypted, not recommended for production) + * + * See http://et.redhat.com/~rjones/secure_rpc for an insight into + * the thinking that went into the selection of SunRPC. In + * the future we may use a different RPC system - for example + * XML-RPC would be a logical choice - so for now you should regard + * the protocol used as private and subject to change in future + * versions of libvirt without notice. + * + * The name string selects the transport to use and the type of + * virtualisation at the remote end. The general format is: + * + * "remote:: var=value var=value ..." + * + * Some examples: + * + * "remote:unix:/var/run/libvirtd/socket" + * "remote:tls:myxenserver" + * "remote:ssh:myserver name=qemud" + * "remote:ssh:myserver command=/opt/openssh/bin/ssh" + * + * The is one of: tls, unix, ssh, ext or tcp. + * The is protocol specific: + * + * Protocol Path-format + * ----------------------------------------- + * tls hostname[:port] + * unix Path to local socket + * ssh hostname[:port] + * ext Name or path of external program + * tcp hostname[:port] + * + * For tls, the default port is 16514. For tcp, the default port is + * 16509 (but note that tcp is almost never enabled because it is + * insecure - it's only there for testing). + * + * For ssh: The default port for ssh is 22. You should configure ssh + * so that it doesn't ask for a password (eg. using ssh-agent). The + * remote server should have a recent version of the the netcat program + * installed as 'nc', and the remote libvirtd must be configured to + * listen on a Unix domain socket. The following full command is run: + * ssh -p $port $hostname nc -U /var/run/libvirtd/socket + * + * For ext: Only the command you specify is run. It is up to you to + * write this command so that it somehow makes a connection to a + * remote libvirtd, and passes input and output over its stdin/stdout. + * + * The var=value pairs provide optional extra information: + * + * Variable Protocols Meaning + * ----------------------------------------- + * name (all) Name used in remote virConnectOpen + * (default is NULL). + * command ssh Name or path of external program (instead + * of "ssh"). + * + * The value is %-escaped (just like URL encoding), so if you want it + * to contain a literal space use "%20" or "+", if you want it to have + * a literal + character use "%2b", and for a literal % character use "%25". + * + * To provide some forwards compatibility, variables which are not + * understood are ignored (but a warning is printed on stderr). + * + * Several shorthand syntaxes are available: + * + * "remote:/var/run/libvirtd/socket" connect to Unix domain socket + * "remote://server" connect to TLS socket on server + * "remote://server:9000" connect to TLS server port 9000 + * + * For the details of the implementation of SunRPC over GnuTLS, etc. + * please see http://et.redhat.com/~rjones/secure_rpc which contains + * simple code samples which will allow you to understand what's + * going on here. + * + * - Richard Jones + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sunrpc/clnt_ext.h" +#include "sunrpc/clnt_gnutls.h" +#include "sunrpc/clnt_tcp2.h" + +#include "internal.h" +#include "driver.h" +#include "remote_internal.h" +#include "remote_rpc.h" + +#define VERSION 1 /* Doesn't really mean anything. */ +#define DEBUG 1 /* Enable verbose messages on stderr. */ + +#define MAGIC 999 /* private_data->magic if OK */ +#define DEAD 998 /* private_data->magic if dead/closed */ + +struct private_data { + int magic; /* Should be MAGIC or DEAD. */ + int sock; /* Socket. */ + CLIENT *cl; /* SunRPC client. */ +}; + +#define GET_PRIVATE(conn,retcode) \ + struct private_data *private = (struct private_data *) (conn)->private; \ + assert (private); \ + if (private->magic == DEAD) { \ + error (conn, VIR_ERR_INVALID_ARG, \ + "tried to use a closed or uninitialised handle"); \ + return (retcode); \ + } \ + assert (private->magic == MAGIC) + +#define CAFILE "demoCA/cacert.pem" /* XXX */ + +static gnutls_certificate_credentials_t xcred; + +static void +initialise_gnutls (void) +{ + static int initialised = 0; + + if (initialised) return; + + gnutls_global_init (); + + /* X509 stuff */ + gnutls_certificate_allocate_credentials (&xcred); + + /* sets the trusted cas file + */ + gnutls_certificate_set_x509_trust_file (xcred, CAFILE, GNUTLS_X509_FMT_PEM); + + initialised = 1; +} + +enum protocol { + proto_unknown, + proto_tls, + proto_unix, + proto_ssh, + proto_ext, + proto_tcp +}; + +#define whitespace " \t\n" + +static char *unescape (const char *); +static int parse_hostname_port (const char *, char **hostname_r, + char **port_r, const char *default_port); +static void error (virConnectPtr conn, virErrorNumber code, const char *info); + +static int +remote_open (virConnectPtr conn, const char *name, + int flags __attribute__((unused))) +{ + int retcode = -1; /* Return code from this function. */ + enum protocol proto = proto_unknown; + char *path = 0; /* or hostname */ + char *port = 0; + char *remote_name = 0; /* Name to use at remote end of connection. */ + char *command = 0; /* External command. */ + char **cmd_argv = 0; /* External command argv[] array. */ + struct private_data private = { .magic = DEAD }; + /* Private data - copied to conn->private at the + * end of this function. + */ + + initialise_gnutls (); + + if (strncasecmp (name, "remote:", 7) != 0) + return -1; /* Not for me. */ + + /* Split the name at whitespace and parse it. */ + const char *p; + for (p = name; *p;) { + size_t len = strcspn (p, whitespace); + char *token = strndup (p, len); +#if DEBUG + fprintf (stderr, "token = %s\n", token); +#endif + if (p == name) { + /* First parameter is remote:protocol:path or a shortcut. */ + if (strncasecmp (token, "remote://", 9) == 0) { + proto = proto_tls; + if (parse_hostname_port (token+9, &path, &port, LIBVIRTD_GNUTLS_PORT) + == -1) { + error (conn, VIR_ERR_INVALID_ARG, + "remote_open: cannot parse port number"); + goto failed; + } + } else if (strncasecmp (token, "remote:/", 8) == 0) { + proto = proto_unix; + path = strdup (token+7); // include the initial slash + } else if (strncasecmp (token, "remote:tls:", 11) == 0) { + proto = proto_tls; + if (parse_hostname_port (token+11, &path, &port, LIBVIRTD_GNUTLS_PORT) + == -1) { + error (conn, VIR_ERR_INVALID_ARG, + "remote_open: cannot parse port number"); + goto failed; + } + } else if (strncasecmp (token, "remote:unix:", 12) == 0) { + proto = proto_unix; + path = strdup (token+12); + } else if (strncasecmp (token, "remote:ssh:", 11) == 0) { + proto = proto_ssh; + if (parse_hostname_port (token+11, &path, &port, "ssh") == -1) { + error (conn, VIR_ERR_INVALID_ARG, + "remote_open: cannot parse port number"); + goto failed; + } + command = strdup ("ssh"); + } else if (strncasecmp (token, "remote:ext:", 11) == 0) { + proto = proto_ext; + command = strdup (token+11); + } else if (strncasecmp (token, "remote:tcp:", 11) == 0) { + proto = proto_tcp; + if (parse_hostname_port (token+11, &path, &port, LIBVIRTD_TCP_PORT) + == -1) { + error (conn, VIR_ERR_INVALID_ARG, + "remote_open: cannot parse port number"); + goto failed; + } + } else { + error (conn, VIR_ERR_INVALID_ARG, + "remote_open: expecting 'remote:protocol:path' - please read the manual for the remote driver / libvirtd"); + goto failed; + } + } else { + /* Variable=value. Value is URL-escaped. */ + char *value = strchr (token, '='); + if (!value) { + error (conn, VIR_ERR_INVALID_ARG, + "remote_open: expecting 'variable=value'"); + goto failed; + } + *value++ = '\0'; /* So now token = variable, value = value. */ + /* Unescape value - this always makes a copy. */ + value = unescape (value); + + if (strcasecmp (token, "name") == 0) { + if (remote_name) { + free (remote_name); + fprintf (stderr, "libvir: warning: multiple name parameters. All but final will be ignored.\n"); + } + remote_name = value; + } else if (strcasecmp (token, "command") == 0) { + if (command) free (command); + command = value; + } else + /* For forwards compatibility, just warn about variables we + * don't understand. + */ + fprintf (stderr, "libvir: warning: variable '%s' ignored\n", token); + } + + free (token); + + /* Skip to next token. */ + p += len; + p += strspn (p, whitespace); + } + + /* Connect to the remote service. */ + switch (proto) { + case proto_unknown: + abort (); /* Internal error in this function. */ + + case proto_tls: + case proto_tcp: { + // http://people.redhat.com/drepper/userapi-ipv6.html + struct addrinfo *res, *r; + struct addrinfo hints; + memset (&hints, 0, sizeof hints); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG; + int e = getaddrinfo (path, port, &hints, &res); + if (e != 0) { + error (conn, VIR_ERR_INVALID_ARG, gai_strerror (e)); + goto failed; + } + + /* Try to connect to each returned address in turn. */ + for (r = res; r; r = r->ai_next) { + private.sock = RPC_ANYSOCK; + private.cl = + proto == proto_tls + ? clntgnutls_create (r->ai_family, r->ai_addr, r->ai_addrlen, xcred, + LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, + &private.sock, 0, 0) + : clnttcp2_create (r->ai_family, r->ai_addr, r->ai_addrlen, + LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, + &private.sock, 0, 0); + if (private.cl) + goto tcp_connected; + } + + freeaddrinfo (res); + error (conn, VIR_ERR_RPC, + clnt_spcreateerror ("could not create SunRPC client")); + goto failed; + + tcp_connected: + freeaddrinfo (res); + + // NB. All versioning is done by SunRPC so we don't need to worry + // that we are connected to an incompatible daemon. + break; + } + + case proto_unix: { + // 108 is hard-coded into the header files as well. +#define UNIX_PATH_MAX 108 + struct sockaddr_un addr; + memset (&addr, 0, sizeof addr); + addr.sun_family = AF_UNIX; + strncpy (addr.sun_path, path, UNIX_PATH_MAX); + + private.sock = RPC_ANYSOCK; + private.cl = + clntunix_create (&addr, + LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, + &private.sock, 0, 0); + if (!private.cl) { + error (conn, VIR_ERR_RPC, + clnt_spcreateerror ("could not create SunRPC client")); + goto failed; + } + + break; + } + + case proto_ssh: + // Generate the final command argv[] array. + // ssh -p $port $hostname nc -U $socket [NULL] + cmd_argv = malloc (8 * sizeof (char *)); + cmd_argv[0] = strdup (command); + cmd_argv[1] = strdup ("-p"); + cmd_argv[2] = strdup (port); + cmd_argv[3] = strdup (path); + cmd_argv[4] = strdup ("nc"); + cmd_argv[5] = strdup ("-U"); + cmd_argv[6] = strdup (LIBVIRTD_UNIX_SOCKET); + cmd_argv[7] = 0; + /*FALLTHROUGH*/ + case proto_ext: + private.sock = RPC_ANYSOCK; + private.cl = + clntext_create (command, cmd_argv, + LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, + &private.sock, 0, 0); + if (!private.cl) { + error (conn, VIR_ERR_RPC, + clnt_spcreateerror ("could not create SunRPC client")); + goto failed; + } + } + + // Send name (make the actual driver open RPC). + int *retcode_ptr = remote_rpc_open_1 (&remote_name, private.cl); + if (!retcode_ptr || *retcode_ptr == -1) { + error (conn, VIR_ERR_RPC, + clnt_sperror (private.cl, "remote_rpc_open")); + clnt_destroy (private.cl); + goto failed; + } + + conn->private = malloc (sizeof private); + if (!conn->private) { + error (conn, VIR_ERR_NO_MEMORY, "malloc"); + clnt_destroy (private.cl); + goto failed; + } + private.magic = MAGIC; + memcpy (conn->private, &private, sizeof private); + + retcode = 0; /* Success. */ + /*FALLTHROUGH*/ + + failed: + if (path) free (path); + if (port) free (port); + if (remote_name) free (remote_name); + if (command) free (command); + if (cmd_argv) { + char **a = cmd_argv; + while (*a) { free (*a); a++; } + free (cmd_argv); + } + + return retcode; +} + +/* Un-%-escape the argument string. Note that this always makes + * a copy, and that is intentional. + */ +static inline int +xdigit (char c) +{ + switch (c) { + case '0'...'9': return c - '0'; + case 'a'...'f': return c - 'a' + 10; + case 'A'...'F': return c - 'A' + 10; + default: return -1; + } +} + +static char * +unescape (const char *str) +{ + // Returned string will always be same length or shorter than input. + int n = strlen (str); + + char *ret = malloc (n+1); + if (ret == 0) return 0; // although _I_ think we should abort(). + + int i; + char *p = ret; + for (i = 0; i < n; ++i) { + if (str[i] == '+') { + *p++ = ' '; + } else if (str[i] == '%') { + if (i+2 < n) { + int c1 = xdigit (str[i+1]), c2 = xdigit (str[i+2]); + if (c1 == -1 || c2 == -1) { + fprintf (stderr, "remote_open: incorrect %%-hex sequence in name\n"); + return 0; + } + *p++ = xdigit (c1) << 4 | xdigit (c2); + } else { + fprintf (stderr, "remote_open: short %%-hex sequence in name\n"); + return 0; + } + } else + *p++ = str[i]; + } + *p = '\0'; + return ret; +} + +/* Parse a string which may be either "hostname" or "hostname:port". + * The hostname may contain colons (eg. if it's an IPv6 name). + * Note that this always makes a copy of the hostname and port number, + * and that is intentional. + */ +static int +parse_hostname_port (const char *str, char **hostname_r, + char **port_r, const char *default_port) +{ + char *p = strrchr (str, ':'); + if (p) { + *hostname_r = strndup (str, p-str); + *port_r = strdup (p+1); + } else { + // No :port, so just copy the hostname. + *hostname_r = strdup (str); + *port_r = strdup (default_port); + } + return 0; +} + +static int +remote_close (virConnectPtr conn) +{ + GET_PRIVATE (conn, -1); + + int *retcode = remote_rpc_close_1 (NULL, private->cl); + if (retcode == 0) { + error (conn, VIR_ERR_RPC, + clnt_sperror (private->cl, "remote_rpc_open")); + return -1; + } + if (*retcode == -1) return -1; + + // XXX freeres + + // NB. clnt_destroy should close the socket (private->sock) too. + clnt_destroy (private->cl); + // Force errors if anyone tries to reuse the closed connection. + private->magic = DEAD; + + return *retcode; +} + +/* Remote_open and remote_close functions above are the complex ones. The + * rest just shuffle arguments and pass them along to the remote libvirtd. + */ + +// Should we return our local type (ie. "remote"), or the type +// of the remote HV, or the type of the remote HV + some flag? +// I took the view that we should just transparently shuffle. +static const char * +remote_type (virConnectPtr conn) +{ + GET_PRIVATE (conn, NULL); + + char **type = remote_rpc_type_1 (NULL, private->cl); + if (type == 0) { + error (conn, VIR_ERR_RPC, + clnt_sperror (private->cl, "remote_rpc_open")); + return NULL; + } + + // XXX freeres + + return *type; +} + +static int +remote_version (virConnectPtr conn, unsigned long *hvVer) +{ + GET_PRIVATE (conn, -1); + + struct version_ret *ret = remote_rpc_version_1 (NULL, private->cl); + if (ret == 0) { + error (conn, VIR_ERR_RPC, + clnt_sperror (private->cl, "remote_rpc_open")); + return -1; + } + + // XXX freeres + + if (ret->retcode == -1) return -1; + *hvVer = ret->hvVer; + return ret->retcode; +} + +/* + .nodeGetInfo = remote_nodeGetInfo, + .listDomains = remote_listDomains, + .numOfDomains = remote_numOfDomains, + .domainCreateLinux = remote_domainCreateLinux, + .domainLookupByID = remote_domainLookupByID, + .domainLookupByUUID = remote_domainLookupByUUID, + .domainLookupByName = remote_domainLookupByName, + .domainSuspend = remote_domainSuspend, + .domainResume = remote_domainResume, + .domainShutdown = remote_domainShutdown, + .domainReboot = remote_domainReboot, + .domainDestroy = remote_domainDestroy, + .domainGetOSType = remote_domainGetOSType, + .domainGetMaxMemory = remote_domainGetMaxMemory, + .domainSetMaxMemory = remote_domainSetMaxMemory, + .domainSetMemory = remote_domainSetMemory, + .domainGetInfo = remote_domainGetInfo, + .domainSave = remote_domainSave, + .domainRestore = remote_domainRestore, + .domainCoreDump = remote_domainCoreDump, + .domainSetVcpus = remote_domainSetVcpus, + .domainPinVcpu = remote_domainPinVcpu, + .domainGetVcpus = remote_domainGetVcpus, + .domainDumpXML = remote_domainDumpXML, + .listDefinedDomains = remote_listDefinedDomains, + .numOfDefinedDomains = remote_numOfDefinedDomains, + .domainCreate = remote_domainCreate, + .domainDefineXML = remote_domainDefineXML, + .domainUndefine = remote_domainUndefine, + .domainAttachDevice = remote_domainAttachDevice, + .domainDetachDevice = remote_domainDetachDevice, +*/ + +/* Error handling. This error handling is on crack. */ +static void +error (virConnectPtr conn, virErrorNumber code, const char *info) +{ + const char *errmsg; + + errmsg = __virErrorMsg (code, info); + __virRaiseError (conn, NULL, VIR_FROM_REMOTE, + code, VIR_ERR_ERROR, errmsg, info, NULL, 0, 0, + errmsg, info); +} + +static virDriver driver = { + .no = VIR_DRV_REMOTE, + .name = "remote", + .ver = VERSION, + //.init = remote_init, + .open = remote_open, + .close = remote_close, + .type = remote_type, + .version = remote_version, +#if 0 + .nodeGetInfo = remote_nodeGetInfo, + .listDomains = remote_listDomains, + .numOfDomains = remote_numOfDomains, + .domainCreateLinux = remote_domainCreateLinux, + .domainLookupByID = remote_domainLookupByID, + .domainLookupByUUID = remote_domainLookupByUUID, + .domainLookupByName = remote_domainLookupByName, + .domainSuspend = remote_domainSuspend, + .domainResume = remote_domainResume, + .domainShutdown = remote_domainShutdown, + .domainReboot = remote_domainReboot, + .domainDestroy = remote_domainDestroy, + .domainGetOSType = remote_domainGetOSType, + .domainGetMaxMemory = remote_domainGetMaxMemory, + .domainSetMaxMemory = remote_domainSetMaxMemory, + .domainSetMemory = remote_domainSetMemory, + .domainGetInfo = remote_domainGetInfo, + .domainSave = remote_domainSave, + .domainRestore = remote_domainRestore, + .domainCoreDump = remote_domainCoreDump, + .domainSetVcpus = remote_domainSetVcpus, + .domainPinVcpu = remote_domainPinVcpu, + .domainGetVcpus = remote_domainGetVcpus, + .domainDumpXML = remote_domainDumpXML, + .listDefinedDomains = remote_listDefinedDomains, + .numOfDefinedDomains = remote_numOfDefinedDomains, + .domainCreate = remote_domainCreate, + .domainDefineXML = remote_domainDefineXML, + .domainUndefine = remote_domainUndefine, + .domainAttachDevice = remote_domainAttachDevice, + .domainDetachDevice = remote_domainDetachDevice, +#endif +}; + +/* Register driver. */ +void +remoteRegister (void) +{ + virRegisterDriver (&driver); +} --- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000 +++ src/remote_internal.h 2007-01-30 16:21:19.000000000 +0000 @@ -0,0 +1,34 @@ +/* + * remote_internal.h: driver to provide access to libvirtd running + * on a remote machine. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones + */ + +#ifndef __VIR_REMOTE_INTERNAL_H__ +#define __VIR_REMOTE_INTERNAL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +void remoteRegister (void); + +/* The port numbers are strings because you can also use + * service names here. + */ +#define LIBVIRTD_GNUTLS_PORT "16514" +#define LIBVIRTD_TCP_PORT "16509" + //#define LIBVIRTD_UNIX_SOCKET "/var/run/libvirtd/socket" +#define LIBVIRTD_UNIX_SOCKET "/tmp/socket" // Just for testing + +#ifdef __cplusplus +} +#endif + + +#endif /* __VIR_REMOTE_INTERNAL_H__ */ --- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000 +++ src/remote_rpc.x 2007-01-30 13:32:56.000000000 +0000 @@ -0,0 +1,38 @@ +/* -*- C -*- + * remote_rpc.x: Remote procedure call interface for the remote driver. + * Process this file with rpcgen to generate client and server stubs. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones + */ + +/* Structure returned from remote_rpc_version. */ +struct version_ret { + int retcode; + long hvVer; +}; + +program LIBVIRTREMOTE { + version LIBVIRTREMOTE_VERS1 { + /* XXX The open interface should return a cookie to represent + * the virConnectPtr on the server side. Note also that SunRPC + * is stateless so it's unclear when cookies can be garbage + * collected. + */ + int remote_rpc_open (string) = 1; + int remote_rpc_close (void) = 2; + string remote_rpc_type (void) = 3; + version_ret remote_rpc_version (void) = 4; + + /* etc */ + + } = 1; + /* It doesn't really matter what program number we choose here because + * there will only ever be one "program" listening on the assigned + * TCP port number. Nevertheless, choose one from the Sun private + * space. + */ +} = 0x20008080; --- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000 +++ src/sunrpc/README 2007-01-30 16:15:43.000000000 +0000 @@ -0,0 +1,25 @@ +This directory contains modified SunRPC transports. They are based on +the standard transports from glibc 2.5 (in the sunrpc/ directory +there). + +clnt_ext.c + - Fork an external program, eg. ssh. + (Original: clnt_unix.c) + +clnt_gnutls.c + - Modified for IPv6 and GnuTLS support. + (Original: clnt_tcp.c) + +clnt_tcp2.c + - Modified for IPv6 support. + (Original: clnt_tcp.c) + +svc_gnutls.c + - Modified for IPv6 and GnuTLS support. + (Original: svc_tcp.c) + +svc_tcp2.c + - Modified for IPv6 support. + (Original: svc_tcp.c) + +$Id$ \ No newline at end of file --- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000 +++ src/sunrpc/clnt_ext.c 2007-01-30 11:48:09.000000000 +0000 @@ -0,0 +1,645 @@ +/* + * clnt_ext.c: SunRPC over an external program. This is a modified version + * of clnt_unix.c from glibc which is written to run over a forked + * external program. + * + * Note that there is no corresponding svc_ext.c. It is expected that + * this client will talk to a remote Unix domain socket (ie. svc_unix.c). + * + * Modifications from glibc-2.5 base by Richard Jones . + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_IN_LIBIO +# include +#endif + +#include "clnt_ext.h" + +extern u_long _create_xid (void); + +#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 rpc_err ct_error; + char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */ + u_int ct_mpos; /* pos after marshal */ + XDR ct_xdrs; + int ct_pid; /* Child PID. */ + }; + +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 const struct clnt_ops unix_ops = +{ + clntunix_call, + clntunix_abort, + clntunix_geterr, + clntunix_freeres, + clntunix_destroy, + clntunix_control +}; + +CLIENT * +clntext_create (char *filename, char *argv[], + 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 rpc_msg call_msg; + int len, sv[2]; + + h = (CLIENT *) mem_alloc (sizeof (*h)); + if (h == NULL || ct == NULL) + { + struct rpc_createerr *ce = &get_rpc_createerr (); + (void) fprintf (stderr, "clntext_create: out of memory\n"); + ce->cf_stat = RPC_SYSTEMERROR; + ce->cf_error.re_errno = ENOMEM; + goto fooy; + } + + /* Fork off the external process. Use socketpair to create a private + * (unnamed) Unix domain socket to the child process so we don't have + * to faff around with two file descriptors (a la 'pipe(2)'). + */ + if (*sockp < 0) + { + if (socketpair (PF_UNIX, SOCK_STREAM, 0, sv) == -1) + { + struct rpc_createerr *ce = &get_rpc_createerr (); + (void) fprintf (stderr, "clntext_create: socketpair\n"); + ce->cf_stat = RPC_SYSTEMERROR; + ce->cf_error.re_errno = errno; + goto fooy; + } + + ct->ct_pid = fork (); + if (ct->ct_pid == -1) { + struct rpc_createerr *ce = &get_rpc_createerr (); + (void) fprintf (stderr, "clntext_create: fork\n"); + ce->cf_stat = RPC_SYSTEMERROR; + ce->cf_error.re_errno = errno; + goto fooy; + } else if (ct->ct_pid == 0) { /* Child. */ + close (sv[0]); + // Connect socket (sv[1]) to stdin/stdout. + close (0); + dup (sv[1]); + close (1); + dup (sv[1]); + close (sv[1]); + + // Run the external process. + + if (!argv) { + argv = malloc (2 * sizeof (char *)); + argv[0] = filename; + argv[1] = 0; + } + execvp (filename, argv); + perror (filename); + _exit (1); + } + + /* Parent continues here. */ + close (sv[1]); + *sockp = sv[0]; + 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; + + /* + * Initialize call message + */ + call_msg.rm_xid = _create_xid (); + 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 = (struct clnt_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 && ct->ct_wait.tv_sec == 0 + && ct->ct_wait.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 (ct->ct_wait.tv_sec == 0 && ct->ct_wait.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_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)); + + // Wait for child to finish. Print any errors but don't stop. + int status; + if (waitpid (ct->ct_pid, &status, 0) == -1) { + perror ("waitpid"); + return; + } + if (WIFEXITED (status)) { + int s = WEXITSTATUS (status); + if (s != 0) + fprintf (stderr, "clntext_destroy: warning: external command exited with non-zero exit status %d\n", s); + } else if (WIFSIGNALED (status)) { + int s = WTERMSIG (status); + fprintf (stderr, "clntext_destroy: warning: external command died on signal %d\n", s); + } else if (WIFSTOPPED (status)) { + int s = WSTOPSIG (status); + fprintf (stderr, "clntext_destroy: warning: external command stopped on signal %d\n", s); + } +} + +static int +__msgread (int sock, void *data, size_t cnt) +{ + struct iovec iov; + struct msghdr msg; +#ifdef SCM_CREDENTIALS + static char cm[CMSG_SPACE(sizeof (struct ucred))]; +#endif + int len; + + iov.iov_base = data; + iov.iov_len = cnt; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; +#ifdef SCM_CREDENTIALS + msg.msg_control = (caddr_t) &cm; + msg.msg_controllen = CMSG_SPACE(sizeof (struct ucred)); +#endif + msg.msg_flags = 0; + +#ifdef SO_PASSCRED + { + int on = 1; + if (setsockopt (sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof (on))) + return -1; + } +#endif + + restart: + len = recvmsg (sock, &msg, 0); + if (len >= 0) + { + if (msg.msg_flags & MSG_CTRUNC || len == 0) + return 0; + else + return len; + } + if (errno == EINTR) + goto restart; + return -1; +} + +static int +__msgwrite (int sock, void *data, size_t cnt) +{ +#ifndef SCM_CREDENTIALS + /* We cannot implement this reliably. */ + __set_errno (ENOSYS); + return -1; +#else + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg = alloca (CMSG_SPACE(sizeof (struct ucred))); + struct ucred cred; + int len; + + /* 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. */ + cred.pid = getpid (); + cred.uid = geteuid (); + cred.gid = getegid (); + + memcpy (CMSG_DATA(cmsg), &cred, sizeof (struct ucred)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = sizeof(*cmsg) + sizeof(struct ucred); + + iov.iov_base = data; + iov.iov_len = cnt; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = cmsg; + msg.msg_controllen = CMSG_ALIGN(cmsg->cmsg_len); + msg.msg_flags = 0; + + restart: + len = sendmsg (sock, &msg, 0); + if (len >= 0) + return len; + if (errno == EINTR) + goto restart; + return -1; + +#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; +} --- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000 +++ src/sunrpc/clnt_ext.h 2007-01-30 11:47:24.000000000 +0000 @@ -0,0 +1,18 @@ +/* + * clnt_ext.h: Interface to the SunRPC over external program. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones + */ + +#ifndef __CLNT_EXT_H__ +#define __CLNT_EXT_H__ + +extern CLIENT *clntext_create (char *filename, char *argv[], + u_long prog, u_long vers, + int *sockp, u_int sendsz, u_int recvsz); + +#endif /* __CLNT_EXT_H__ */ --- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000 +++ src/sunrpc/clnt_gnutls.c 2007-01-30 17:03:31.000000000 +0000 @@ -0,0 +1,603 @@ +/* + * clnt_gnutls.c: SunRPC over GnuTLS client. This is a modified version + * of clnt_tcp.c from glibc which is written to run over GnuTLS. + * + * Modifications from glibc-2.5 base by Richard Jones . + */ + +/* @(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * 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 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro"; +#endif + +/* + * clnt_tcp.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 +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_IN_LIBIO +# include +#endif +#include + +#include "clnt_gnutls.h" + +extern u_long _create_xid (void); + +#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 rpc_err ct_error; + char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */ + u_int ct_mpos; /* pos after marshal */ + XDR ct_xdrs; + gnutls_session_t ct_session; /* GnuTLS session. */ + }; + +static int readtcp (char *, char *, int); +static int writetcp (char *, char *, int); + +static enum clnt_stat clnttcp_call (CLIENT *, u_long, xdrproc_t, caddr_t, + xdrproc_t, caddr_t, struct timeval); +static void clnttcp_abort (void); +static void clnttcp_geterr (CLIENT *, struct rpc_err *); +static bool_t clnttcp_freeres (CLIENT *, xdrproc_t, caddr_t); +static bool_t clnttcp_control (CLIENT *, int, char *); +static void clnttcp_destroy (CLIENT *); + +static const struct clnt_ops tcp_ops = +{ + clnttcp_call, + clnttcp_abort, + clnttcp_geterr, + clnttcp_freeres, + clnttcp_destroy, + clnttcp_control +}; + +static int start_gnutls_session (gnutls_certificate_credentials_t xcred, + struct ct_data *ct, int sock); +/* + * 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 * +clntgnutls_create (int pf, struct sockaddr *raddr, int raddr_size, + gnutls_certificate_credentials_t xcred, + u_long prog, u_long vers, + int *sockp, u_int sendsz, u_int recvsz) +{ + CLIENT *h; + struct ct_data *ct; + struct rpc_msg call_msg; + + h = (CLIENT *) mem_alloc (sizeof (*h)); + ct = (struct ct_data *) mem_alloc (sizeof (*ct)); + if (h == NULL || ct == NULL) + { + struct rpc_createerr *ce = &get_rpc_createerr (); + (void) fprintf (stderr, "clnttcp_create: out of memory\n"); + ce->cf_stat = RPC_SYSTEMERROR; + ce->cf_error.re_errno = ENOMEM; + goto fooy; + } + +#if 0 + // RWMJ: Note this will never be supported for portmap because the + // portmap protocol depends pretty fundamentally on IPv4. Don't + /// use portmapper anyway -- it's silly. + /* + * If no port number given ask the pmap for one + */ + if (port == 0) + { + u_short port; + if ((port = pmap_getport (raddr, prog, vers, IPPROTO_TCP)) == 0) + { + mem_free ((caddr_t) ct, sizeof (struct ct_data)); + mem_free ((caddr_t) h, sizeof (CLIENT)); + return ((CLIENT *) NULL); + } + raddr->sin_port = htons (port); + } +#endif + + /* + * If no socket given, open one + */ + if (*sockp < 0) + { + *sockp = socket (pf, SOCK_STREAM, 0); + // RWMJ: Why? + //(void) bindresvport (*sockp, (struct sockaddr_in *) 0); + if ((*sockp < 0) + || (connect (*sockp, raddr, raddr_size) < 0)) + { + struct rpc_createerr *ce = &get_rpc_createerr (); + ce->cf_stat = RPC_SYSTEMERROR; + ce->cf_error.re_errno = errno; + if (*sockp >= 0) + (void) 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; + + /* + * Initialize call message + */ + call_msg.rm_xid = _create_xid (); + 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) + { + (void) 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, readtcp, writetcp); + h->cl_ops = (struct clnt_ops *) &tcp_ops; + h->cl_private = (caddr_t) ct; + h->cl_auth = authnone_create (); + + /* Start GnuTLS on this socket. */ + if (start_gnutls_session (xcred, ct, *sockp) == -1) { + if (ct->ct_closeit) + close (*sockp); + goto fooy; + } + + 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 int +start_gnutls_session (gnutls_certificate_credentials_t xcred, + struct ct_data *ct, int sock) +{ + const int cert_type_priority[3] = { + GNUTLS_CRT_X509, + GNUTLS_CRT_OPENPGP, + 0 + }; + + /* Initialize TLS session + */ + gnutls_init (&ct->ct_session, GNUTLS_CLIENT); + + /* Use default priorities */ + gnutls_set_default_priority (ct->ct_session); + gnutls_certificate_type_set_priority (ct->ct_session, cert_type_priority); + + /* put the x509 credentials to the current session + */ + gnutls_credentials_set (ct->ct_session, GNUTLS_CRD_CERTIFICATE, xcred); + + gnutls_transport_set_ptr (ct->ct_session, + (gnutls_transport_ptr_t) (long) sock); + + /* Perform the TLS handshake + */ + int ret = gnutls_handshake (ct->ct_session); + + if (ret < 0) + { + fprintf (stderr, "clnt_gnutls: TLS handshake failed\n"); + gnutls_perror (ret); + exit (1); + } + + /* XXX You need to verify the peer's certificate matches its name. */ + printf ("XXX need to verify peer's certificate matches its name.\n"); + +#if 0 + /* Print session info. */ + print_info (session); +#endif + + return 0; +} + +static enum clnt_stat +clnttcp_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 && ct->ct_wait.tv_sec == 0 + && ct->ct_wait.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 (ct->ct_wait.tv_sec == 0 && ct->ct_wait.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 ((u_int32_t) reply_msg.rm_xid == (u_int32_t) 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 +clnttcp_geterr (h, errp) + CLIENT *h; + struct rpc_err *errp; +{ + struct ct_data *ct = + (struct ct_data *) h->cl_private; + + *errp = ct->ct_error; +} + +static bool_t +clnttcp_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 +clnttcp_abort () +{ +} + +static bool_t +clnttcp_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; + ct->ct_waitset = TRUE; + break; + case CLGET_TIMEOUT: + *(struct timeval *) info = ct->ct_wait; + 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 clnttcp_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 +clnttcp_destroy (CLIENT *h) +{ + struct ct_data *ct = + (struct ct_data *) h->cl_private; + + gnutls_bye (ct->ct_session, GNUTLS_SHUT_RDWR); + gnutls_deinit (ct->ct_session); + + 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)); +} + +/* + * Interface between xdr serializer and tcp connection. + * Behaves like the system calls, read & write, but keeps some error state + * around for the rpc level. + */ +static int +readtcp (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 = gnutls_record_recv (ct->ct_session, 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 +writetcp (char *ctptr, char *buf, int len) +{ + struct ct_data *ct = (struct ct_data*)ctptr; + + if (gnutls_record_send (ct->ct_session, buf, len) < 0) + { + ct->ct_error.re_errno = errno; + ct->ct_error.re_status = RPC_CANTSEND; + return -1; + } + + return len; +} --- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000 +++ src/sunrpc/clnt_gnutls.h 2007-01-30 11:59:17.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * clnt_gnutls.h: Interface to the SunRPC over GnuTLS. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones + */ + +#ifndef __CLNT_GNUTLS_H__ +#define __CLNT_GNUTLS_H__ + +extern CLIENT *clntgnutls_create (int pf, + struct sockaddr *raddr, int raddr_size, + gnutls_certificate_credentials_t xcred, + u_long prog, u_long vers, + int *sockp, u_int sendsz, u_int recvsz); + +#endif /* __CLNT_GNUTLS_H__ */ --- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000 +++ src/sunrpc/clnt_tcp2.c 2007-01-30 16:08:20.000000000 +0000 @@ -0,0 +1,544 @@ +/* + * clnt_tcp2.c: SunRPC TCP client. This is a very slightly modified + * version of clnt_tcp.c from glibc which removes some of the IPv4 + * assumptions, so this version can be used over IPv4 or IPv6 sockets. + * + * Modifications from glibc-2.5 base by Richard Jones . + */ + +/* @(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * 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 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro"; +#endif + +/* + * clnt_tcp.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 +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_IN_LIBIO +# include +#endif + +#include "clnt_tcp2.h" + +extern u_long _create_xid (void); + +#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 rpc_err ct_error; + char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */ + u_int ct_mpos; /* pos after marshal */ + XDR ct_xdrs; + }; + +static int readtcp (char *, char *, int); +static int writetcp (char *, char *, int); + +static enum clnt_stat clnttcp_call (CLIENT *, u_long, xdrproc_t, caddr_t, + xdrproc_t, caddr_t, struct timeval); +static void clnttcp_abort (void); +static void clnttcp_geterr (CLIENT *, struct rpc_err *); +static bool_t clnttcp_freeres (CLIENT *, xdrproc_t, caddr_t); +static bool_t clnttcp_control (CLIENT *, int, char *); +static void clnttcp_destroy (CLIENT *); + +static const struct clnt_ops tcp_ops = +{ + clnttcp_call, + clnttcp_abort, + clnttcp_geterr, + clnttcp_freeres, + clnttcp_destroy, + clnttcp_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 * +clnttcp2_create (int af, struct sockaddr *raddr, int raddr_size, + u_long prog, u_long vers, + int *sockp, u_int sendsz, u_int recvsz) +{ + CLIENT *h; + struct ct_data *ct; + struct rpc_msg call_msg; + + h = (CLIENT *) mem_alloc (sizeof (*h)); + ct = (struct ct_data *) mem_alloc (sizeof (*ct)); + if (h == NULL || ct == NULL) + { + struct rpc_createerr *ce = &get_rpc_createerr (); + (void) fprintf (stderr, "clnttcp_create: out of memory\n"); + ce->cf_stat = RPC_SYSTEMERROR; + ce->cf_error.re_errno = ENOMEM; + goto fooy; + } + +#if 0 + // RWMJ: Note this will never be supported for portmap because the + // portmap protocol depends pretty fundamentally on IPv4. Don't + /// use portmapper anyway -- it's silly. + /* + * If no port number given ask the pmap for one + */ + if (port == 0) + { + u_short port; + if ((port = pmap_getport (raddr, prog, vers, IPPROTO_TCP)) == 0) + { + mem_free ((caddr_t) ct, sizeof (struct ct_data)); + mem_free ((caddr_t) h, sizeof (CLIENT)); + return ((CLIENT *) NULL); + } + raddr->sin_port = htons (port); + } +#endif + + /* + * If no socket given, open one + */ + if (*sockp < 0) + { + *sockp = socket (af, SOCK_STREAM, 0); + // RWMJ: Why? + //(void) bindresvport (*sockp, (struct sockaddr_in *) 0); + if ((*sockp < 0) + || (connect (*sockp, raddr, raddr_size) < 0)) + { + struct rpc_createerr *ce = &get_rpc_createerr (); + ce->cf_stat = RPC_SYSTEMERROR; + ce->cf_error.re_errno = errno; + if (*sockp >= 0) + (void) 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; + + /* + * Initialize call message + */ + call_msg.rm_xid = _create_xid (); + 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) + { + (void) 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, readtcp, writetcp); + h->cl_ops = (struct clnt_ops *) &tcp_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 +clnttcp_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 && ct->ct_wait.tv_sec == 0 + && ct->ct_wait.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 (ct->ct_wait.tv_sec == 0 && ct->ct_wait.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 ((u_int32_t) reply_msg.rm_xid == (u_int32_t) 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 +clnttcp_geterr (h, errp) + CLIENT *h; + struct rpc_err *errp; +{ + struct ct_data *ct = + (struct ct_data *) h->cl_private; + + *errp = ct->ct_error; +} + +static bool_t +clnttcp_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 +clnttcp_abort () +{ +} + +static bool_t +clnttcp_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; + ct->ct_waitset = TRUE; + break; + case CLGET_TIMEOUT: + *(struct timeval *) info = ct->ct_wait; + 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 clnttcp_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 +clnttcp_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)); +} + +/* + * Interface between xdr serializer and tcp connection. + * Behaves like the system calls, read & write, but keeps some error state + * around for the rpc level. + */ +static int +readtcp (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 = read (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 +writetcp (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 = write (ct->ct_sock, buf, cnt)) == -1) + { + ct->ct_error.re_errno = errno; + ct->ct_error.re_status = RPC_CANTSEND; + return -1; + } + } + return len; +} --- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000 +++ src/sunrpc/clnt_tcp2.h 2007-01-30 15:26:09.000000000 +0000 @@ -0,0 +1,18 @@ +/* + * clnt_tcp2.h: Interface to the SunRPC over TCP. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones + */ + +#ifndef __CLNT_TCP2_H__ +#define __CLNT_TCP2_H__ + +extern CLIENT *clnttcp2_create (int af, struct sockaddr *raddr, int raddr_size, + u_long prog, u_long vers, + int *sockp, u_int sendsz, u_int recvsz); + +#endif /* __CLNT_TCP2_H__ */ --- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000 +++ src/sunrpc/create_xid.c 2007-01-30 13:21:07.000000000 +0000 @@ -0,0 +1,55 @@ +/* + * create_xid.c: Makes unique, unguessable identifiers. This is modified + * from the original source found in glibc-2.5. The original version + * is thread safe (but the rest of SunRPC isn't). This version is + * not thread safe. + * + * Modifications from glibc-2.5 base by Richard Jones . + */ + +/* Copyright (c) 1998, 2000 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Thorsten Kukuk , 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include +#include + +static int is_initialized; +static struct drand48_data __rpc_lrand48_data; + +unsigned long +_create_xid (void) +{ + long int res; + + if (!is_initialized) + { + struct timeval now; + + gettimeofday (&now, (struct timezone *) 0); + srand48_r (now.tv_sec ^ now.tv_usec, &__rpc_lrand48_data); + is_initialized = 1; + } + + lrand48_r (&__rpc_lrand48_data, &res); + + return res; +} --- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000 +++ src/sunrpc/svc_gnutls.c 2007-01-30 17:03:35.000000000 +0000 @@ -0,0 +1,477 @@ +/* + * svc_gnutls.c: SunRPC over GnuTLS server. This is a modified version + * of svc_tcp.c from glibc which is written to run over GnuTLS. + * + * Modifications from glibc-2.5 base by Richard Jones . + */ + +/* @(#)svc_tcp.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * 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 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * svc_tcp.c, Server side for TCP/IP based RPC. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * Actually implements two flavors of transporter - + * a tcp rendezvouser (a listener and connection establisher) + * and a record/tcp stream. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_IN_LIBIO +# include +# include +#endif + +#include +#include "svc_gnutls.h" + +/* + * Ops vector for TCP/IP based rpc service handle + */ +static bool_t svctcp_recv (SVCXPRT *, struct rpc_msg *); +static enum xprt_stat svctcp_stat (SVCXPRT *); +static bool_t svctcp_getargs (SVCXPRT *, xdrproc_t, caddr_t); +static bool_t svctcp_reply (SVCXPRT *, struct rpc_msg *); +static bool_t svctcp_freeargs (SVCXPRT *, xdrproc_t, caddr_t); +static void svctcp_destroy (SVCXPRT *); + +static const struct xp_ops svctcp_op = +{ + svctcp_recv, + svctcp_stat, + svctcp_getargs, + svctcp_reply, + svctcp_freeargs, + svctcp_destroy +}; + +/* + * Ops vector for TCP/IP rendezvous handler + */ +static bool_t rendezvous_request (SVCXPRT *, struct rpc_msg *); +static enum xprt_stat rendezvous_stat (SVCXPRT *); +static void svctcp_rendezvous_abort (void) __attribute__ ((__noreturn__)); + +/* This function makes sure abort() relocation goes through PLT + and thus can be lazy bound. */ +static void +svctcp_rendezvous_abort (void) +{ + abort (); +}; + +static const struct xp_ops svctcp_rendezvous_op = +{ + rendezvous_request, + rendezvous_stat, + (bool_t (*) (SVCXPRT *, xdrproc_t, caddr_t)) svctcp_rendezvous_abort, + (bool_t (*) (SVCXPRT *, struct rpc_msg *)) svctcp_rendezvous_abort, + (bool_t (*) (SVCXPRT *, xdrproc_t, caddr_t)) svctcp_rendezvous_abort, + svctcp_destroy +}; + +static int readtcp (char*, char *, int); +static int writetcp (char *, char *, int); +static SVCXPRT *makefd_xprt (int, u_int, u_int, gnutls_session_t); + +struct tcp_rendezvous + { /* kept in xprt->xp_p1 */ + u_int sendsize; + u_int recvsize; + gnutls_certificate_credentials_t x509_cred; + }; + +struct tcp_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 = svcgnutls_create(sock, send_buf_size, recv_buf_size, + * gnutls_certificate_credentials_t x509_cred); + * + * Creates, registers, and returns a (rpc) tcp 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 svctcp_create + * binds it to an arbitrary port. The routine then starts a tcp + * 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 tcp 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 * +svcgnutls_create (int sock, u_int sendsize, u_int recvsize, + gnutls_certificate_credentials_t x509_cred) +{ + bool_t madesock = FALSE; + SVCXPRT *xprt; + struct tcp_rendezvous *r; + struct sockaddr_in addr; + socklen_t len = sizeof (struct sockaddr_in); + + if (sock == RPC_ANYSOCK) + { + if ((sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + { + perror ("svc_gnutls.c - tcp socket creation problem"); + return (SVCXPRT *) NULL; + } + madesock = TRUE; + } + bzero ((char *) &addr, sizeof (addr)); + addr.sin_family = AF_INET; + if (bindresvport (sock, &addr)) + { + addr.sin_port = 0; + (void) bind (sock, (struct sockaddr *) &addr, len); + } + if ((getsockname (sock, (struct sockaddr *) &addr, &len) != 0) || + (listen (sock, SOMAXCONN) != 0)) + { + perror ("svc_gnutls.c - cannot getsockname or listen"); + if (madesock) + (void) close (sock); + return (SVCXPRT *) NULL; + } + r = (struct tcp_rendezvous *) mem_alloc (sizeof (*r)); + xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT)); + if (r == NULL || xprt == NULL) + { + (void) fprintf (stderr, "svcgnutls_create: out of memory\n"); + mem_free (r, sizeof (*r)); + mem_free (xprt, sizeof (SVCXPRT)); + return NULL; + } + r->sendsize = sendsize; + r->recvsize = recvsize; + r->x509_cred = x509_cred; + xprt->xp_p2 = NULL; + xprt->xp_p1 = (caddr_t) r; + xprt->xp_verf = _null_auth; + xprt->xp_ops = &svctcp_rendezvous_op; + xprt->xp_port = ntohs (addr.sin_port); + xprt->xp_sock = sock; + xprt_register (xprt); + return xprt; +} + +#if 0 +/* + * Like svtcp_create(), except the routine takes any *open* UNIX file + * descriptor as its first input. + */ +SVCXPRT * +svcfd_create (int fd, u_int sendsize, u_int recvsize) +{ + return makefd_xprt (fd, sendsize, recvsize); +} +#endif + +static SVCXPRT * +makefd_xprt (int fd, u_int sendsize, u_int recvsize, + gnutls_session_t session) +{ + SVCXPRT *xprt; + struct tcp_conn *cd; + + xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT)); + cd = (struct tcp_conn *) mem_alloc (sizeof (struct tcp_conn)); + if (xprt == (SVCXPRT *) NULL || cd == NULL) + { + (void) fprintf (stderr, "svc_gnutls: makefd_xprt: out of memory\n"); + mem_free (xprt, sizeof (SVCXPRT)); + mem_free (cd, sizeof (struct tcp_conn)); + return NULL; + } + cd->strm_stat = XPRT_IDLE; + xdrrec_create (&(cd->xdrs), sendsize, recvsize, + (caddr_t) xprt, readtcp, writetcp); + xprt->xp_p2 = (caddr_t) session; + xprt->xp_p1 = (caddr_t) cd; + xprt->xp_verf.oa_base = cd->verf_body; + xprt->xp_addrlen = 0; + xprt->xp_ops = &svctcp_op; /* truly deals with calls */ + xprt->xp_port = 0; /* this is a connection, not a rendezvouser */ + xprt->xp_sock = fd; + xprt_register (xprt); + return xprt; +} + +// XXX DH_BITS must be defined the same in the server main loop too. +#define DH_BITS 1024 + +static gnutls_session_t +initialize_tls_session (gnutls_certificate_credentials_t x509_cred) +{ + gnutls_session_t session; + + gnutls_init (&session, GNUTLS_SERVER); + + /* avoid calling all the priority functions, since the defaults + * are adequate. + */ + gnutls_set_default_priority (session); + + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred); + + /* request client certificate if any. + */ + gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST); + + gnutls_dh_set_prime_bits (session, DH_BITS); + + return session; +} + +static bool_t +rendezvous_request (SVCXPRT *xprt, + struct rpc_msg *errmsg __attribute__((unused))) +{ + int sock; + struct tcp_rendezvous *r; + struct sockaddr_in addr; + socklen_t len; + + r = (struct tcp_rendezvous *) xprt->xp_p1; + gnutls_session_t session = initialize_tls_session (r->x509_cred); + +again: + len = sizeof (struct sockaddr_in); + if ((sock = accept (xprt->xp_sock, (struct sockaddr *) &addr, &len)) < 0) + { + if (errno == EINTR) + goto again; + return FALSE; + } + + gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) (long) sock); + int ret = gnutls_handshake (session); + if (ret < 0) + { + close (sock); + gnutls_deinit (session); + fprintf (stderr, "svc_gnutls: TLS handshake failed (%s)\n\n", + gnutls_strerror (ret)); + return FALSE; + } + + // XXX verify peer + fprintf (stderr, "XXX you must verify the peer\n"); + + /* + * make a new transporter (re-uses xprt) + */ + xprt = makefd_xprt (sock, r->sendsize, r->recvsize, session); +#if 0 + memcpy (&xprt->xp_raddr, &addr, sizeof (addr)); + xprt->xp_addrlen = len; +#endif + return FALSE; /* there is never an rpc msg to be processed */ +} + +static enum xprt_stat +rendezvous_stat (SVCXPRT *xprt __attribute__((unused))) +{ + return XPRT_IDLE; +} + +static void +svctcp_destroy (SVCXPRT *xprt) +{ + struct tcp_conn *cd = (struct tcp_conn *) xprt->xp_p1; + gnutls_session_t session = (gnutls_session_t) xprt->xp_p2; + + gnutls_bye (session, GNUTLS_SHUT_WR); + + xprt_unregister (xprt); + (void) 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)); + } + + gnutls_deinit (session); + + mem_free ((caddr_t) cd, sizeof (struct tcp_conn)); + mem_free ((caddr_t) xprt, sizeof (SVCXPRT)); +} + + +/* + * reads data from the tcp connection. + * any error is fatal and the connection is closed. + * (And a read of zero bytes is a half closed stream => error.) + */ +static int +readtcp (char *xprtptr, char *buf, int len) +{ + SVCXPRT *xprt = (SVCXPRT *)xprtptr; + gnutls_session_t session = (gnutls_session_t) xprt->xp_p2; + int sock = xprt->xp_sock; + int milliseconds = 35 * 1000; + struct pollfd pollfd; + + do + { + pollfd.fd = sock; + pollfd.events = POLLIN; + switch (poll (&pollfd, 1, milliseconds)) + { + case -1: + if (errno == EINTR) + continue; + /*FALLTHROUGH*/ + case 0: + goto fatal_err; + default: + if ((pollfd.revents & POLLERR) || (pollfd.revents & POLLHUP) + || (pollfd.revents & POLLNVAL)) + goto fatal_err; + break; + } + } + while ((pollfd.revents & POLLIN) == 0); + + if ((len = gnutls_record_recv (session, buf, len)) > 0) + return len; + + fatal_err: + ((struct tcp_conn *) (xprt->xp_p1))->strm_stat = XPRT_DIED; + return -1; +} + +/* + * writes data to the tcp connection. + * Any error is fatal and the connection is closed. + */ +static int +writetcp (char *xprtptr, char * buf, int len) +{ + SVCXPRT *xprt = (SVCXPRT *)xprtptr; + gnutls_session_t session = (gnutls_session_t) xprt->xp_p2; + + if (gnutls_record_send (session, buf, len) < 0) { + ((struct tcp_conn *) (xprt->xp_p1))->strm_stat = XPRT_DIED; + return -1; + } + + return len; +} + +static enum xprt_stat +svctcp_stat (SVCXPRT *xprt) +{ + struct tcp_conn *cd = + (struct tcp_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 +svctcp_recv (SVCXPRT *xprt, struct rpc_msg *msg) +{ + struct tcp_conn *cd = (struct tcp_conn *) (xprt->xp_p1); + XDR *xdrs = &(cd->xdrs); + + xdrs->x_op = XDR_DECODE; + (void) xdrrec_skiprecord (xdrs); + if (xdr_callmsg (xdrs, msg)) + { + cd->x_id = msg->rm_xid; + return TRUE; + } + cd->strm_stat = XPRT_DIED; /* XXXX */ + return FALSE; +} + +static bool_t +svctcp_getargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) +{ + return ((*xdr_args) (&(((struct tcp_conn *) + (xprt->xp_p1))->xdrs), args_ptr)); +} + +static bool_t +svctcp_freeargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) +{ + XDR *xdrs = &(((struct tcp_conn *) (xprt->xp_p1))->xdrs); + + xdrs->x_op = XDR_FREE; + return ((*xdr_args) (xdrs, args_ptr)); +} + +static bool_t +svctcp_reply (SVCXPRT *xprt, struct rpc_msg *msg) +{ + struct tcp_conn *cd = (struct tcp_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; +} --- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000 +++ src/sunrpc/svc_gnutls.h 2007-01-29 17:03:24.000000000 +0000 @@ -0,0 +1,17 @@ +/* + * svc_gnutls.h: Interface to the SunRPC over GnuTLS server. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones + */ + +#ifndef __SVC_GNUTLS_H__ +#define __SVC_GNUTLS_H__ + +extern SVCXPRT *svcgnutls_create (int sock, u_int sendsize, u_int recvsize, + gnutls_certificate_credentials_t x509_cred); + +#endif /* __SVC_GNUTLS_H__ */ --- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000 +++ src/sunrpc/svc_tcp2.c 2007-01-30 16:16:32.000000000 +0000 @@ -0,0 +1,426 @@ +/* + * svc_tcp2.c: SunRPC TCP server. This is a very slightly modified + * version of svc_tcp.c from glibc which removes some of the IPv4 + * assumptions, so this version can be used over IPv4 or IPv6 sockets. + * + * Modifications from glibc-2.5 base by Richard Jones . + */ + +/* @(#)svc_tcp.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * 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 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * svc_tcp.c, Server side for TCP/IP based RPC. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * Actually implements two flavors of transporter - + * a tcp rendezvouser (a listener and connection establisher) + * and a record/tcp stream. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_IN_LIBIO +# include +# include +#endif + +#include "svc_tcp2.h" + +/* + * Ops vector for TCP/IP based rpc service handle + */ +static bool_t svctcp_recv (SVCXPRT *, struct rpc_msg *); +static enum xprt_stat svctcp_stat (SVCXPRT *); +static bool_t svctcp_getargs (SVCXPRT *, xdrproc_t, caddr_t); +static bool_t svctcp_reply (SVCXPRT *, struct rpc_msg *); +static bool_t svctcp_freeargs (SVCXPRT *, xdrproc_t, caddr_t); +static void svctcp_destroy (SVCXPRT *); + +static const struct xp_ops svctcp_op = +{ + svctcp_recv, + svctcp_stat, + svctcp_getargs, + svctcp_reply, + svctcp_freeargs, + svctcp_destroy +}; + +/* + * Ops vector for TCP/IP rendezvous handler + */ +static bool_t rendezvous_request (SVCXPRT *, struct rpc_msg *); +static enum xprt_stat rendezvous_stat (SVCXPRT *); +static void svctcp_rendezvous_abort (void) __attribute__ ((__noreturn__)); + +/* This function makes sure abort() relocation goes through PLT + and thus can be lazy bound. */ +static void +svctcp_rendezvous_abort (void) +{ + abort (); +}; + +static const struct xp_ops svctcp_rendezvous_op = +{ + rendezvous_request, + rendezvous_stat, + (bool_t (*) (SVCXPRT *, xdrproc_t, caddr_t)) svctcp_rendezvous_abort, + (bool_t (*) (SVCXPRT *, struct rpc_msg *)) svctcp_rendezvous_abort, + (bool_t (*) (SVCXPRT *, xdrproc_t, caddr_t)) svctcp_rendezvous_abort, + svctcp_destroy +}; + +static int readtcp (char*, char *, int); +static int writetcp (char *, char *, int); +static SVCXPRT *makefd_xprt (int, u_int, u_int); + +struct tcp_rendezvous + { /* kept in xprt->xp_p1 */ + u_int sendsize; + u_int recvsize; + }; + +struct tcp_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 = svctcp_create(sock, send_buf_size, recv_buf_size); + * + * Creates, registers, and returns a (rpc) tcp 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 svctcp_create + * binds it to an arbitrary port. The routine then starts a tcp + * 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 tcp 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 * +svctcp2_create (int sock, u_int sendsize, u_int recvsize) +{ + bool_t madesock = FALSE; + SVCXPRT *xprt; + struct tcp_rendezvous *r; + struct sockaddr_in addr; + socklen_t len = sizeof (struct sockaddr_in); + + if (sock == RPC_ANYSOCK) + { + if ((sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + { + perror ("svc_tcp.c - tcp socket creation problem"); + return (SVCXPRT *) NULL; + } + madesock = TRUE; + + bzero ((char *) &addr, sizeof (addr)); + addr.sin_family = AF_INET; + if (bindresvport (sock, &addr)) + { + addr.sin_port = 0; + (void) bind (sock, (struct sockaddr *) &addr, len); + } + if ((getsockname (sock, (struct sockaddr *) &addr, &len) != 0) || + (listen (sock, SOMAXCONN) != 0)) + { + perror ("svc_tcp.c - cannot getsockname or listen"); + if (madesock) + (void) close (sock); + return (SVCXPRT *) NULL; + } + } + r = (struct tcp_rendezvous *) mem_alloc (sizeof (*r)); + xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT)); + if (r == NULL || xprt == NULL) + { + (void) fprintf (stderr, "svctcp_create: out of memory\n"); + mem_free (r, sizeof (*r)); + mem_free (xprt, sizeof (SVCXPRT)); + return NULL; + } + r->sendsize = sendsize; + r->recvsize = recvsize; + xprt->xp_p2 = NULL; + xprt->xp_p1 = (caddr_t) r; + xprt->xp_verf = _null_auth; + xprt->xp_ops = &svctcp_rendezvous_op; + xprt->xp_port = ntohs (addr.sin_port); + xprt->xp_sock = sock; + xprt_register (xprt); + return xprt; +} + +#if 0 +/* + * Like svtcp_create(), except the routine takes any *open* UNIX file + * descriptor as its first input. + */ +SVCXPRT * +svcfd_create (int fd, u_int sendsize, u_int recvsize) +{ + return makefd_xprt (fd, sendsize, recvsize); +} +#endif + +static SVCXPRT * +makefd_xprt (int fd, u_int sendsize, u_int recvsize) +{ + SVCXPRT *xprt; + struct tcp_conn *cd; + + xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT)); + cd = (struct tcp_conn *) mem_alloc (sizeof (struct tcp_conn)); + if (xprt == (SVCXPRT *) NULL || cd == NULL) + { + (void) fprintf (stderr, "svc_tcp: makefd_xprt: out of memory\n"); + mem_free (xprt, sizeof (SVCXPRT)); + mem_free (cd, sizeof (struct tcp_conn)); + return NULL; + } + cd->strm_stat = XPRT_IDLE; + xdrrec_create (&(cd->xdrs), sendsize, recvsize, + (caddr_t) xprt, readtcp, writetcp); + 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 = &svctcp_op; /* truly deals with calls */ + xprt->xp_port = 0; /* this is a connection, not a rendezvouser */ + xprt->xp_sock = fd; + xprt_register (xprt); + return xprt; +} + +static bool_t +rendezvous_request (SVCXPRT *xprt, + struct rpc_msg *errmsg __attribute__((unused))) +{ + int sock; + struct tcp_rendezvous *r; + struct sockaddr_in addr; + socklen_t len; + + r = (struct tcp_rendezvous *) xprt->xp_p1; +again: + len = sizeof (struct sockaddr_in); + 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) + */ + xprt = makefd_xprt (sock, r->sendsize, r->recvsize); +#if 0 + memcpy (&xprt->xp_raddr, &addr, sizeof (addr)); + xprt->xp_addrlen = len; +#endif + return FALSE; /* there is never an rpc msg to be processed */ +} + +static enum xprt_stat +rendezvous_stat (SVCXPRT *xprt __attribute__((unused))) +{ + return XPRT_IDLE; +} + +static void +svctcp_destroy (SVCXPRT *xprt) +{ + struct tcp_conn *cd = (struct tcp_conn *) xprt->xp_p1; + + xprt_unregister (xprt); + (void) 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 tcp_conn)); + mem_free ((caddr_t) xprt, sizeof (SVCXPRT)); +} + + +/* + * reads data from the tcp connection. + * any error is fatal and the connection is closed. + * (And a read of zero bytes is a half closed stream => error.) + */ +static int +readtcp (char *xprtptr, char *buf, int len) +{ + SVCXPRT *xprt = (SVCXPRT *)xprtptr; + int sock = xprt->xp_sock; + int milliseconds = 35 * 1000; + struct pollfd pollfd; + + do + { + pollfd.fd = sock; + pollfd.events = POLLIN; + switch (poll (&pollfd, 1, milliseconds)) + { + case -1: + if (errno == EINTR) + continue; + /*FALLTHROUGH*/ + case 0: + goto fatal_err; + default: + if ((pollfd.revents & POLLERR) || (pollfd.revents & POLLHUP) + || (pollfd.revents & POLLNVAL)) + goto fatal_err; + break; + } + } + while ((pollfd.revents & POLLIN) == 0); + + if ((len = read (sock, buf, len)) > 0) + return len; + + fatal_err: + ((struct tcp_conn *) (xprt->xp_p1))->strm_stat = XPRT_DIED; + return -1; +} + +/* + * writes data to the tcp connection. + * Any error is fatal and the connection is closed. + */ +static int +writetcp (char *xprtptr, char * buf, int len) +{ + SVCXPRT *xprt = (SVCXPRT *)xprtptr; + int i, cnt; + + for (cnt = len; cnt > 0; cnt -= i, buf += i) + { + if ((i = write (xprt->xp_sock, buf, cnt)) < 0) + { + ((struct tcp_conn *) (xprt->xp_p1))->strm_stat = XPRT_DIED; + return -1; + } + } + return len; +} + +static enum xprt_stat +svctcp_stat (SVCXPRT *xprt) +{ + struct tcp_conn *cd = + (struct tcp_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 +svctcp_recv (SVCXPRT *xprt, struct rpc_msg *msg) +{ + struct tcp_conn *cd = (struct tcp_conn *) (xprt->xp_p1); + XDR *xdrs = &(cd->xdrs); + + xdrs->x_op = XDR_DECODE; + (void) xdrrec_skiprecord (xdrs); + if (xdr_callmsg (xdrs, msg)) + { + cd->x_id = msg->rm_xid; + return TRUE; + } + cd->strm_stat = XPRT_DIED; /* XXXX */ + return FALSE; +} + +static bool_t +svctcp_getargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) +{ + return ((*xdr_args) (&(((struct tcp_conn *) + (xprt->xp_p1))->xdrs), args_ptr)); +} + +static bool_t +svctcp_freeargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr) +{ + XDR *xdrs = &(((struct tcp_conn *) (xprt->xp_p1))->xdrs); + + xdrs->x_op = XDR_FREE; + return ((*xdr_args) (xdrs, args_ptr)); +} + +static bool_t +svctcp_reply (SVCXPRT *xprt, struct rpc_msg *msg) +{ + struct tcp_conn *cd = (struct tcp_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; +} --- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000 +++ src/sunrpc/svc_tcp2.h 2007-01-30 16:14:22.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * svc_tcp2.h: Interface to the SunRPC over TCP server. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the license of this software. + * + * Richard Jones + */ + +#ifndef __SVC_TCP2_H__ +#define __SVC_TCP2_H__ + +extern SVCXPRT *svctcp2_create (int sock, u_int sendsize, u_int recvsize); + +#endif /* __SVC_TCP2_H__ */