This patch is just for discussion. It's not in a state to be applied,
even if it were accepted (which is a long-shot at present anyway).
When looking at the patch, a good starting point is to search for
"Architecture and notes" and read from there.
Supports:
* remote driver (just does the "open", "close", "type"
and
"version" calls at present as a proof of concept)
* TLS transport (built using GnuTLS)
* SSH transport (forks external ssh process)
* TCP transport (unencrypted - just for testing)
* Unix domain socket transport
* arbitrary external program / shell script transport
* IPv6-ready on client & server
I've tested all the transports, and in limited tests they all
seem to work. ie. You really can do:
virsh -c remote:tls:server version
Shortcomings in this version:
* in "open" call, name must be non-NULL (this is just a bug)
* doesn't actually invoke libvirt on the server side; just
prints out messages and returns dummy values
* "ssh" not recognised as a service name by getaddrinfo, so
you must always give a port number, ie. remote:ssh:server:22
* /tmp/socket should be cleaned up when the server exits
Potential problems:
* SunRPC is stateless so we need to hand out a cookie to
represent the virConnectPtr handle on the server side.
However if the client dies without explicitly calling
close, we have no way to know, and so the cookie/handle
on the server side lives forever.
* There's some confusion about the level of abstraction. At
the moment I'm abstracting at the driver level, but that may
be wrong and possibly I should be abstracting at the level
of vir* calls. On the other hand, there's not a huge amount
of difference.
* Security:
Is it safe for libvirt to be connecting to arbitrary TCP
sockets?
Is it safe for libvirt to be able to run arbitrary programs?
Rich.
--
Emerging Technologies, Red Hat
http://et.redhat.com/~rjones/
64 Baker Street, London, W1U 7DF Mobile: +44 7866 314 421
"[Negative numbers] darken the very whole doctrines of the equations
and make dark of the things which are in their nature excessively
obvious and simple" (Francis Maseres FRS, mathematician, 1759)
? 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 <inttypes.h> 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 <memory.h> 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 <rjones(a)redhat.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <time.h>
+#include <rpc/rpc.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <errno.h>
+#include <gnutls/gnutls.h>
+
+#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 <rjones(a)redhat.com>
+ */
+
+/* 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:<protocol>:<path> 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 <protocol> is one of: tls, unix, ssh, ext or tcp.
+ * The <path> 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 <rjones(a)redhat.com>
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <rpc/rpc.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netdb.h>
+#include <assert.h>
+#include <gnutls/gnutls.h>
+
+#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 <rjones(a)redhat.com>
+ */
+
+#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 <rjones(a)redhat.com>
+ */
+
+/* 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 <rjones(a)redhat.com>.
+ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * clnt_unix.c, Implements a TCP/IP based, client side RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * TCP based RPC supports 'batched calls'.
+ * A sequence of calls may be batched-up in a send buffer. The rpc call
+ * return immediately to the client even though the call was not necessarily
+ * sent. The batching occurs if the results' xdr routine is NULL (0) AND
+ * the rpc timeout value is zero (see clnt.h, rpc).
+ *
+ * Clients should NOT casually batch calls that in fact return results; that is,
+ * the server side should be aware that a call is batched and not produce any
+ * return message. Batched calls that produce many result messages can
+ * deadlock (netlock) the client and the server....
+ *
+ * Now go hang yourself.
+ */
+
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <rpc/rpc.h>
+#include <sys/uio.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <rpc/pmap_clnt.h>
+#ifdef USE_IN_LIBIO
+# include <wchar.h>
+#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 <rjones(a)redhat.com>
+ */
+
+#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 <rjones(a)redhat.com>.
+ */
+
+/* @(#)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[] = "(a)(#)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 <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <rpc/rpc.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <rpc/pmap_clnt.h>
+#ifdef USE_IN_LIBIO
+# include <wchar.h>
+#endif
+#include <gnutls/gnutls.h>
+
+#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 <rjones(a)redhat.com>
+ */
+
+#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 <rjones(a)redhat.com>.
+ */
+
+/* @(#)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[] = "(a)(#)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 <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <rpc/rpc.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <rpc/pmap_clnt.h>
+#ifdef USE_IN_LIBIO
+# include <wchar.h>
+#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 <rjones(a)redhat.com>
+ */
+
+#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 <rjones(a)redhat.com>.
+ */
+
+/* Copyright (c) 1998, 2000 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Thorsten Kukuk <kukuk(a)vt.uni-paderborn.de>, 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 <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <bits/libc-lock.h>
+#include <rpc/rpc.h>
+
+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 <rjones(a)redhat.com>.
+ */
+
+/* @(#)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[] = "(a)(#)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 <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <libintl.h>
+#include <rpc/rpc.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#ifdef USE_IN_LIBIO
+# include <wchar.h>
+# include <libio/iolibio.h>
+#endif
+
+#include <gnutls/gnutls.h>
+#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 <rjones(a)redhat.com>
+ */
+
+#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 <rjones(a)redhat.com>.
+ */
+
+/* @(#)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[] = "(a)(#)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 <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <libintl.h>
+#include <rpc/rpc.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#ifdef USE_IN_LIBIO
+# include <wchar.h>
+# include <libio/iolibio.h>
+#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 <rjones(a)redhat.com>
+ */
+
+#ifndef __SVC_TCP2_H__
+#define __SVC_TCP2_H__
+
+extern SVCXPRT *svctcp2_create (int sock, u_int sendsize, u_int recvsize);
+
+#endif /* __SVC_TCP2_H__ */