[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[Libvir] Preliminary patch to support remote driver / libvirtd



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 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 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 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 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 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 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 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 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[] = "@(#)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 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 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[] = "@(#)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 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 redhat com>.
+ */
+
+/* Copyright (c) 1998, 2000 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Thorsten Kukuk <kukuk 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 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[] = "@(#)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 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 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[] = "@(#)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 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__ */

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]