diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..263c648
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,83 @@
+*~
+*.o
+*.lo
+*.la
+.libs
+.deps
+ABOUT-NLS
+Makefile
+INSTALL
+aclocal.m4
+autom4te.cache
+stamp-h.in
+Makefile.in
+configure
+config.cache
+config.h
+config.h.in
+config.log
+config.guess
+config.status
+config.sub
+depcomp
+install-sh
+missing
+stamp-h
+stamp-h1
+libtool
+ltconfig
+ltmain.sh
+update.log
+libvirt.pc
+libvirt.spec
+COPYING
+m4
+config.rpath
+mkinstalldirs
+src/remote_rpc_clnt.c
+src/remote_rpc_svc.c
+src/remote_rpc_xdr.c
+src/remote_rpc.h
+src/libvirtd
+src/virsh
+po/stamp-po
+po/remove-potcdate.sin
+po/quot.sed
+po/insert-header.sin
+po/*.gmo
+po/en@quot.header
+po/en@boldquot.header
+po/boldquot.sed
+po/Rules-quot
+po/POTFILES
+po/Makevars.template
+po/Makefile.in.in
+po/Makefile.in
+po/Makefile
+docs/devhelp/Makefile
+docs/devhelp/Makefile.in
+docs/devhelp/libvirt.devhelp
+docs/examples/.memdump
+docs/examples/info1
+docs/examples/suspend
+include/libvirt/libvirt.h
+include/libvirt/Makefile
+include/libvirt/Makefile.in
+proxy/libvirt_proxy
+python/Makefile
+python/Makefile.in
+python/libvirt.py
+python/libvirt-export.c
+python/libvirtclass.txt
+python/libvirt-py.[ch]
+python/libvirtclass.py
+python/gen_prog
+tests/Makefile
+tests/Makefile.in
+tests/xmlrpctest
+tests/sexpr2xmltest
+tests/xml2sexprtest
+tests/virshtest
+tests/conftest
+tests/reconnect
+tests/xmconfigtest
diff --git a/config.h.in b/config.h.in
index fac4097..1b8f680 100644
--- a/config.h.in
+++ b/config.h.in
@@ -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
 
diff --git a/configure.in b/configure.in
index 7947ed4..d865175 100644
--- a/configure.in
+++ b/configure.in
@@ -146,6 +146,11 @@ AC_SUBST(LIBXML_LIBS)
 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"],
diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
index e2d5369..0218b3d 100644
--- a/include/libvirt/virterror.h
+++ b/include/libvirt/virterror.h
@@ -46,7 +46,8 @@ typedef enum {
     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 @@ typedef enum {
     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;
 
 /**
diff --git a/src/.cvsignore b/src/.cvsignore
index 8a7a0c4..e703ee7 100644
--- a/src/.cvsignore
+++ b/src/.cvsignore
@@ -5,3 +5,8 @@ Makefile.in
 *.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
diff --git a/src/Makefile.am b/src/Makefile.am
index 26b31ce..5198f90 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,7 @@
 ## Process this file with automake to produce Makefile.in
 
+AM_CFLAGS = -g
+
 INCLUDES = -I$(top_builddir)/include -I@top_srcdir@/include @LIBXML_CFLAGS@ \
 	   -DBINDIR=\""$(libexecdir)"\" -DLOCALEBASEDIR=\""$(datadir)/locale"\" \
            -DGETTEXT_PACKAGE=\"$(PACKAGE)\"
@@ -28,15 +30,41 @@ libvirt_la_SOURCES =						\
 		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
 #
diff --git a/src/driver.h b/src/driver.h
index 4e708a3..10493f4 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -22,7 +22,8 @@ typedef enum {
     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;
 
 
diff --git a/src/internal.h b/src/internal.h
index a8938cd..1c24102 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -117,6 +117,13 @@ struct _virConnect {
     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 */
diff --git a/src/libvirt.c b/src/libvirt.c
index b06bf43..46ce492 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -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"
 
@@ -73,6 +74,7 @@ virInitialize(void)
     /*
      * Note that the order is important the first ones have a higher priority
      */
+    remoteRegister ();
     xenHypervisorRegister();
     xenProxyRegister();
     xenDaemonRegister();
diff --git a/src/libvirt_sym.version b/src/libvirt_sym.version
index 2d009da..e9ea837 100644
--- a/src/libvirt_sym.version
+++ b/src/libvirt_sym.version
@@ -55,7 +55,11 @@
 	virDomainPinVcpu;
 	virDomainGetVcpus;
 
-    virDomainAttachDevice;
-    virDomainDetachDevice;
+	virDomainAttachDevice;
+	virDomainDetachDevice;
+
+	virConfReadFile;
+	virConfGetValue;
+	virConfFree;
     local: *;
 };
diff --git a/src/libvirtd.c b/src/libvirtd.c
new file mode 100644
index 0000000..d5b8b2c
--- /dev/null
+++ b/src/libvirtd.c
@@ -0,0 +1,493 @@
+/*
+ * 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 <getopt.h>
+#include <assert.h>
+#include <fnmatch.h>
+#include <gnutls/gnutls.h>
+
+#include "sunrpc/svc_gnutls.h"
+#include "sunrpc/svc_tcp2.h"
+#include "remote_internal.h"
+#include "remote_rpc.h"
+#include "conf.h"
+
+// Defaults.
+int listen_tls = 1;
+int listen_tcp = 0;
+int listen_unix = 1;
+const char *tls_port = LIBVIRTD_TLS_PORT;
+const char *tcp_port = LIBVIRTD_TCP_PORT;
+const char *unix_socket = LIBVIRTD_UNIX_SOCKET;
+
+int tls_no_verify_certificate = 0;
+int tls_no_verify_address = 0;
+const char **tls_allowed_clients = 0;
+
+// XXX Still to decide where these certificates should be located.
+const char *key_file = "newkey.pem";
+const char *cert_file = "newcert.pem";
+const char *ca_file = "demoCA/cacert.pem";
+const char *crl_file = "";
+
+// 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 *my_malloc (size_t);
+static char *my_strdup (const char *);
+static void generate_dh_params (void);
+static int make_sockets (int *fds, int max_fds, int *nfds_r,
+                         const char *service);
+static int check_allowed_client (void *, const char *addr);
+
+static int debug = 0, quiet = 0;
+
+int
+main (int argc, char *argv[])
+{
+    char arg;
+    const char *conffile = LIBVIRTD_CONFIGURATION_FILE;
+
+    // Read the command line.
+    while ((arg = getopt (argc, argv, "df:q")) != -1) {
+        switch (arg)
+            {
+            case 'f':
+                conffile = optarg;
+                break;
+
+            case 'q':
+                quiet = 1;
+                break;
+
+            case 'd':
+                debug = 1;
+                break;
+
+            case '?':
+                fprintf (stderr,
+                         "libvirtd [-dq] [-f conffile]\n"
+                         "  -d            Debug mode (don't fork into bg)\n"
+                         "  -q            Quiet\n"
+                         "  -f conffile   Use configuration file (default: %s)\n"
+                         , conffile
+                         );
+                exit (1);
+            }
+    }
+
+    // Read the configuration file.  If it's not there, proceed with defaults.
+    virConfPtr conf = virConfReadFile (conffile);
+    if (conf) {
+        virConfValuePtr p;
+
+#define CHECK_TYPE(name,typ) if (p && p->type != (typ)) { \
+            fprintf (stderr, "libvirtd: %s: %s: expected type " #typ "\n", \
+                     conffile, (name));                                  \
+            exit (1); \
+        }
+
+        p = virConfGetValue (conf, "listen_tls");
+        CHECK_TYPE ("listen_tls", VIR_CONF_LONG);
+        listen_tls = p ? p->l : listen_tls;
+
+        p = virConfGetValue (conf, "listen_tcp");
+        CHECK_TYPE ("listen_tcp", VIR_CONF_LONG);
+        listen_tcp = p ? p->l : listen_tcp;
+
+        p = virConfGetValue (conf, "listen_unix");
+        CHECK_TYPE ("listen_unix", VIR_CONF_LONG);
+        listen_unix = p ? p->l : listen_unix;
+
+        p = virConfGetValue (conf, "tls_port");
+        CHECK_TYPE ("tls_port", VIR_CONF_STRING);
+        tls_port = p ? my_strdup (p->str) : tls_port;
+
+        p = virConfGetValue (conf, "tcp_port");
+        CHECK_TYPE ("tcp_port", VIR_CONF_STRING);
+        tcp_port = p ? my_strdup (p->str) : tcp_port;
+
+        p = virConfGetValue (conf, "unix_socket");
+        CHECK_TYPE ("unix_socket", VIR_CONF_STRING);
+        unix_socket = p ? my_strdup (p->str) : unix_socket;
+
+        p = virConfGetValue (conf, "tls_no_verify_certificate");
+        CHECK_TYPE ("tls_no_verify_certificate", VIR_CONF_LONG);
+        tls_no_verify_certificate = p ? p->l : tls_no_verify_certificate;
+
+        p = virConfGetValue (conf, "tls_no_verify_address");
+        CHECK_TYPE ("tls_no_verify_address", VIR_CONF_LONG);
+        tls_no_verify_address = p ? p->l : tls_no_verify_address;
+
+        p = virConfGetValue (conf, "tls_allowed_clients");
+        if (p)
+            {
+                switch (p->type)
+                    {
+                    case VIR_CONF_STRING:
+                        tls_allowed_clients = my_malloc (2 * sizeof (char *));
+                        tls_allowed_clients[0] = my_strdup (p->str);
+                        tls_allowed_clients[1] = 0;
+                        break;
+
+                    case VIR_CONF_LIST: {
+                        int i, len = 0;
+                        virConfValuePtr pp;
+                        for (pp = p->list; pp; pp = p->next)
+                            len++;
+                        tls_allowed_clients =
+                            my_malloc ((1+len) * sizeof (char *));
+                        for (i = 0, pp = p->list; pp; ++i, pp = p->next) {
+                            if (pp->type != VIR_CONF_STRING) {
+                                fprintf (stderr, "libvirtd: %s: tls_allowed_clients: should be a string or list of strings\n", conffile);
+                                exit (1);
+                            }
+                            tls_allowed_clients[i] = my_strdup (pp->str);
+                        }
+                        tls_allowed_clients[i] = 0;
+                        break;
+                    }
+
+                    default:
+                        fprintf (stderr, "libvirtd: %s: tls_allowed_clients: should be a string or list of strings\n", conffile);
+                        exit (1);
+                    }
+            }
+
+        p = virConfGetValue (conf, "key_file");
+        CHECK_TYPE ("key_file", VIR_CONF_STRING);
+        key_file = p ? my_strdup (p->str) : key_file;
+
+        p = virConfGetValue (conf, "cert_file");
+        CHECK_TYPE ("cert_file", VIR_CONF_STRING);
+        cert_file = p ? my_strdup (p->str) : cert_file;
+
+        p = virConfGetValue (conf, "ca_file");
+        CHECK_TYPE ("ca_file", VIR_CONF_STRING);
+        ca_file = p ? my_strdup (p->str) : ca_file;
+
+        p = virConfGetValue (conf, "crl_file");
+        CHECK_TYPE ("crl_file", VIR_CONF_STRING);
+        crl_file = p ? my_strdup (p->str) : crl_file;
+
+        virConfFree (conf);
+    }
+
+    // This may not be an error.  cf. /etc/exports
+    if (!listen_tls && !listen_tcp && !listen_unix) {
+        fprintf (stderr, "libvirtd: all services disabled, so exiting\n");
+        exit (0);
+    }
+
+    if (listen_tls) {
+        /* Initialise GnuTLS. */
+        gnutls_global_init ();
+
+        gnutls_certificate_allocate_credentials (&x509_cred);
+        if (ca_file && ca_file[0] != '\0')
+            gnutls_certificate_set_x509_trust_file (x509_cred, ca_file,
+                                                    GNUTLS_X509_FMT_PEM);
+
+        if (crl_file && crl_file[0] != '\0')
+            gnutls_certificate_set_x509_crl_file (x509_cred, crl_file,
+                                                  GNUTLS_X509_FMT_PEM);
+
+        if (cert_file && cert_file[0] != '\0' &&
+            key_file && key_file[0] != '\0')
+            gnutls_certificate_set_x509_key_file (x509_cred, cert_file, key_file,
+                                                  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, tls_port) == -1)
+            exit (1);
+
+        int i;
+        for (i = 0; i < nfds; ++i) {
+            SVCXPRT *transp = svcgnutls_create (fds[i], 0, 0, x509_cred,
+                                                tls_no_verify_certificate,
+                                                tls_no_verify_address,
+                                                NULL, check_allowed_client);
+            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 (!quiet)
+            fprintf (stderr, "libvirtd: TLS service listening on port %s\n",
+                     tls_port);
+    }
+
+    if (listen_tcp) {
+        int fds[2];
+        int nfds = 0;
+        if (make_sockets (fds, 2, &nfds, 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 (!quiet)
+            fprintf (stderr, "libvirtd: unsafe TCP service listening on port %s (only use this for testing)\n",
+                     tcp_port);
+    }
+
+    if (listen_unix) {
+        /* XXX Not sure if this is the right thing to do. */
+        if (unlink (unix_socket) == -1 && errno != ENOENT) {
+            perror (unix_socket);
+            exit (1);
+        }
+
+        int sock = RPC_ANYSOCK;
+        SVCXPRT *transp = svcunix_create (sock, 0, 0, (char *) unix_socket);
+        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);
+        }
+
+        if (!quiet)
+            fprintf (stderr, "libvirtd: Unix service listening on socket %s\n",
+                     unix_socket);
+    }
+
+    /* Daemonize. */
+    if (!debug && daemon (0, 0) == -1) {
+        perror ("daemon");
+        exit (2);
+    }
+
+    svc_run ();
+    abort ();                   /* svc_run should never return. */
+}
+
+// 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;
+}
+
+/* Check the IP address matches one on the list of wildcards
+ * tls_allowed_clients.
+ */
+static int
+check_allowed_client (void *vp __attribute__((unused)), const char *addr)
+{
+    const char **wildcards = tls_allowed_clients;
+
+    /* User has not set up a list of tls_allowed_clients. */
+    if (!wildcards) return 1;
+
+    if (debug)
+        fprintf (stderr,
+                 "check_allowed_client: trying to match addr %s\n", addr);
+
+    while (*wildcards) {
+        if (fnmatch (*wildcards, addr, 0) == 0)
+            return 1;
+        wildcards++;
+    }
+
+    if (debug)
+        fprintf (stderr,
+                 "check_allowed_client: addr %s did not match\n", addr);
+
+    return 0;
+}
+
+/* Private malloc and strdup functions which always succeed.  For
+ * justification, see http://et.redhat.com/~rjones/  These are only
+ * for use while the daemon is starting up.  Once started we don't
+ * want memory allocations to abort, since that's a DoS.
+ */
+static void *
+my_malloc (size_t n)
+{
+    void *ptr = malloc (n);
+    if (ptr == 0) abort ();
+    return ptr;
+}
+
+static char *
+my_strdup (const char *str)
+{
+    char *str2 = strdup (str);
+    if (str2 == 0) abort ();
+    return str2;
+}
+
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/src/remote_internal.c b/src/remote_internal.c
new file mode 100644
index 0000000..df4d61c
--- /dev/null
+++ b/src/remote_internal.c
@@ -0,0 +1,802 @@
+/*
+ * 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 in this driver looks for remote URLs and will
+ * try some method to connect to a libvirtd running on a remote
+ * machine.
+ *
+ * Local URLs are the standard URLs used by other drivers -
+ * for example "qemud:///system".
+ *
+ * Remote URLs contain either a server name or a remote transport
+ * name.  For example: "qemud://server.example.com/system" contains
+ * a server name (server.example.com), and "qemud+unix:///system"
+ * contains a transport (unix).  Commonly remote URLs contain
+ * both, for example: "qemud+tls://server.example.com/system"
+ * (transport tls, server name server.example.com).
+ *
+ * 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.
+ *
+ * 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 URL (name parameter to virConnectOpen) is a URL of the
+ * following general form:
+ *
+ *  "driver+transport://server:port/path?var=value&var=value&..."
+ *
+ * Transport, server and port are all optional (except that you must
+ * have either transport or server as explained above).  You may
+ * have zero or more variables (or omit the query string entirely).
+ * Driver is the usual libvirt driver, as used on the remote
+ * machine, and path is specific to driver.
+ *
+ * Some examples:
+ *
+ *  "xend://server/"              Remote xend, using TLS, port 16514
+ *  "qemud+unix:///session"       Communicate over Unix domain socket
+ *                                  to a local libvirtd.
+ *  "qemud+ssh://server/session?command=/opt/openssh/bin/ssh"
+ *                                Communicate over ssh to a remote
+ *                                  libvirtd on server.  Control qemu
+ *                                  on remote.  ssh command is located
+ *                                  in a non-standard place.
+ *  "qemud+tcp://server:5000/session"
+ *                                Unencrypted TCP, port 5000.
+ *  "xend+ext:///?command=my_shell_script"
+ *                                Run my_shell_script which uses its own
+ *                                method (eg. rsh, ssh, ...) to talk to
+ *                                the remote libvirtd, controlling a
+ *                                remote xend.
+ *
+ * [To emphasise - in ALL instances communication is with a remote
+ * libvirtd, even if that remote libvirtd itself talks to another
+ * daemon such as xend or qemud].
+ *
+ * The transport is one of: tls, unix, ssh, ext or tcp.  If no
+ * transport is given, the default is to use tls.
+ *
+ * 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 [-l $username] $hostname $netcat -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    Transport     Meaning
+ *   -----------------------------------------
+ *   command     ssh,ext       Name or path of external program.
+ *                               For ssh this defaults to "ssh".
+ *                               For ext you must supply it.
+ *   name        (all)         Optionally the name used in remote
+ *                               virConnectOpen.  The default is to
+ *                               construct the name by removing
+ *                               transport, server name, port and
+ *                               variables from the remote URL to
+ *                               form a local URL.  But if this
+ *                               doesn't give the desired result you
+ *                               may specify the exact name here.
+ *   socket      unix,ssh,ext  Name of the Unix domain socket.  The
+ *                               default is in <remote_internal.h>.
+ *   netcat      ssh           Name of the netcat program on the
+ *                               remote server.  Default is "nc".
+ *   no_verify   tls           If set to a non-zero value, this will
+ *                               not check the peer's certificate
+ *                               (it will still print a warning).
+ *                               The default is to always check.
+ *
+ * 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).
+ *
+ * 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 <libxml/uri.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)
+
+static void error (virConnectPtr conn, virErrorNumber code, const char *info);
+
+#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 transport {
+    trans_tls,
+    trans_unix,
+    trans_ssh,
+    trans_ext,
+    trans_tcp
+};
+
+struct query_var {
+    int ignore;
+    char *name, *value;
+};
+
+static char *get_transport_from_scheme (char *scheme);
+static struct query_var **query_split (virConnectPtr conn, const char *query, int *n_r);
+static char *query_join (virConnectPtr conn, struct query_var **vars, int *n_r);
+static void free_query_vars (struct query_var **vars, int n);
+
+static int
+remote_open (virConnectPtr conn, const char *uri_str,
+             int flags ATTRIBUTE_UNUSED)
+{
+    initialise_gnutls ();
+
+    if (!uri_str) return -1;	/* Decline. */
+
+    /* We have to parse the URL every time to discover whether
+     * it contains a transport or remote server name.  There's no
+     * way to get around this.
+     */
+    xmlURIPtr uri = xmlParseURI (uri_str);
+    if (!uri || !uri->scheme)
+        return -1;			/* Decline - not a URL. */
+
+    char *transport_str = get_transport_from_scheme (uri->scheme);
+    if (!uri->server && !transport_str)
+        return -1;			/* Decline - not a remote URL. */
+
+    /* What transport? */
+    enum transport transport;
+    if (!transport_str || strcasecmp (transport_str, "tls") == 0)
+        transport = trans_tls;
+    else if (strcasecmp (transport_str, "unix") == 0)
+        transport = trans_unix;
+    else if (strcasecmp (transport_str, "ssh") == 0)
+        transport = trans_ssh;
+    else if (strcasecmp (transport_str, "ext") == 0)
+        transport = trans_ext;
+    else if (strcasecmp (transport_str, "tcp") == 0)
+        transport = trans_tcp;
+    else {
+        error (conn, VIR_ERR_INVALID_ARG,
+               "remote_open: transport in URL not recognised "
+               "(should be tls|unix|ssh|ext|tcp)");
+        return -1;
+    }
+
+    /* Return code from this function, and the private data. */
+    int retcode = -1;
+    struct private_data private = { .magic = DEAD };
+    char *name = 0, *command = 0, *sockname = 0, *netcat = 0, *username = 0;
+    int no_verify = 0;
+    char **cmd_argv = 0;
+
+    /* Remote server defaults to "localhost" if not specified. */
+    char *server = strdup (uri->server ? uri->server : "localhost");
+    if (!server) {
+    out_of_memory:
+        error (conn, VIR_ERR_NO_MEMORY, "remote_open");
+        goto failed;
+    }
+    char *port;
+    if (uri->port != 0) {
+        if (asprintf (&port, "%d", uri->port) == -1) goto out_of_memory;
+    } else if (transport == trans_tls) {
+        port = strdup (LIBVIRTD_TLS_PORT);
+        if (!port) goto out_of_memory;
+    } else if (transport == trans_tcp) {
+        port = strdup (LIBVIRTD_TCP_PORT);
+        if (!port) goto out_of_memory;
+    } else if (transport == trans_ssh) {
+        port = strdup ("22");
+        if (!port) goto out_of_memory;
+        if (uri->user) {
+            username = strdup (uri->user);
+            if (!username) goto out_of_memory;
+        }
+    } else
+        port = NULL;		/* Port not used for unix, ext. */
+
+    /* Get the variables from the query string.
+     * Then we need to reconstruct the query string (because
+     * feasibly it might contain variables needed by the real driver,
+     * although that won't be the case for now).
+     */
+    int i, n;
+    struct query_var **vars = query_split (conn, uri->query, &n);
+    if (n < 0) goto failed;
+
+    for (i = 0; i < n; ++i) {
+        if (strcasecmp (vars[i]->name, "name") == 0) {
+            name = strdup (vars[i]->value);
+            if (!name) goto out_of_memory;
+            vars[i]->ignore = 1;
+        } else if (strcasecmp (vars[i]->name, "command") == 0) {
+            command = strdup (vars[i]->value);
+            if (!command) goto out_of_memory;
+            vars[i]->ignore = 1;
+        } else if (strcasecmp (vars[i]->name, "socket") == 0) {
+            sockname = strdup (vars[i]->value);
+            if (!sockname) goto out_of_memory;
+            vars[i]->ignore = 1;
+        } else if (strcasecmp (vars[i]->name, "netcat") == 0) {
+            netcat = strdup (vars[i]->value);
+            if (!netcat) goto out_of_memory;
+            vars[i]->ignore = 1;
+        } else if (strcasecmp (vars[i]->name, "no_verify") == 0) {
+            no_verify = atoi (vars[i]->value);
+            vars[i]->ignore = 1;
+        } else
+            fprintf (stderr,
+                     "remote_open: passing through variable '%s' to remote end\n",
+                     vars[i]->name);
+    }
+
+    if (uri->query) xmlFree (uri->query);
+    uri->query = query_join (conn, vars, &n);
+    if (n < 0) goto failed;
+    free_query_vars (vars, n);
+
+    /* For ext transport, command is required. */
+    if (transport == trans_ext && !command) {
+        error (conn, VIR_ERR_INVALID_ARG, "remote_open: for 'ext' transport, command is required");
+        goto failed;
+    }
+
+    /* Construct the original name. */
+    if (!name) {
+        /* Remove the transport (if any) from the scheme. */
+        if (transport_str) {
+            assert (transport_str[-1] == '+');
+            transport_str[-1] = '\0';
+        }
+        /* Remove the server name and port number. */
+        if (uri->server) xmlFree (uri->server);
+        uri->server = 0;
+        uri->port = 0;
+
+        name = (char *) xmlSaveUri (uri);
+    }
+
+    assert (name);
+#if DEBUG
+    fprintf (stderr, "proceeding with name = %s\n", name);
+#endif
+
+    /* Connect to the remote service. */
+    switch (transport) {
+    case trans_tls:
+    case trans_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 (server, 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 =
+                transport == trans_tls
+                ? clntgnutls_create (r->ai_family, r->ai_addr, r->ai_addrlen, xcred,
+                                     no_verify, server,
+                                     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 trans_unix: {
+        sockname = sockname ? : strdup (LIBVIRTD_UNIX_SOCKET);
+
+        // 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, sockname, 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 trans_ssh: {
+        int j, nr_args = username ? 10 : 8;
+
+        command = command ? : strdup ("ssh");
+
+        // Generate the final command argv[] array.
+        //   ssh -p $port [-l $username] $hostname $netcat -U $sockname [NULL]
+        cmd_argv = malloc (nr_args * sizeof (char *));
+        j = 0;
+        cmd_argv[j++] = strdup (command);
+        cmd_argv[j++] = strdup ("-p");
+        cmd_argv[j++] = strdup (port);
+        if (username) {
+            cmd_argv[j++] = strdup ("-l");
+            cmd_argv[j++] = strdup (username);
+        }
+        cmd_argv[j++] = strdup (server);
+        cmd_argv[j++] = strdup (netcat ? netcat : "nc");
+        cmd_argv[j++] = strdup ("-U");
+        cmd_argv[j++] = strdup (sockname ? sockname : LIBVIRTD_UNIX_SOCKET);
+        cmd_argv[j++] = 0;
+        assert (j == nr_args);
+        /*FALLTHROUGH*/
+    }
+    case trans_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 (&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);
+
+    /* Successful. */
+    retcode = 0;
+    /*FALLTHROUGH*/
+
+    /* Free up the URL and strings. */
+ failed:
+    xmlFreeURI (uri);
+    if (name) free (name);
+    if (command) free (command);
+    if (sockname) free (sockname);
+    if (netcat) free (netcat);
+    if (username) free (username);
+    if (server) free (server);
+    if (port) free (port);
+    if (cmd_argv) {
+        char **cmd_argv_ptr = cmd_argv;
+        while (*cmd_argv_ptr) {
+            free (*cmd_argv_ptr);
+            cmd_argv_ptr++;
+        }
+        free (cmd_argv);
+    }
+    return retcode;
+}
+
+/* In a string "driver+transport" return a pointer to "transport". */
+static char *
+get_transport_from_scheme (char *scheme)
+{
+    char *p = strchr (scheme, '+');
+    return p ? p+1 : 0;
+}
+
+/* Split a query string ("var=variable&var=variable&..."). */
+static struct query_var **
+get_next_var (virConnectPtr conn,
+              const char *query, int len, struct query_var **vars, int *n_r)
+{
+    char *str = strndup (query, len);
+    if (!str) {
+        error (conn, VIR_ERR_NO_MEMORY, "get_next_var");
+        free_query_vars (vars, *n_r); return 0;
+    }
+    char *p = strchr (str, '=');
+    if (!p) {
+        error (conn, VIR_ERR_INVALID_ARG,
+               "get_next_var: expecting variable=value in query string");
+        free_query_vars (vars, *n_r); return 0;
+    }
+
+    struct query_var *var = malloc (sizeof (struct query_var));
+    if (!var) {
+        error (conn, VIR_ERR_NO_MEMORY, "get_next_var");
+        free (p); free_query_vars (vars, *n_r); return 0;
+    }
+    *p = '\0';
+    var->name = str;
+    var->value = p+1;
+    var->ignore = 0;
+    (*n_r)++;
+    struct query_var **new_vars;
+    new_vars = realloc (vars, *n_r * sizeof (struct query_var *));
+    if (!new_vars) {
+        error (conn, VIR_ERR_NO_MEMORY, "get_next_var");
+        free (p); free (var);
+        free_query_vars (vars, *n_r-1);
+        return 0;
+    }
+    vars = new_vars;
+    vars[*n_r-1] = var;
+    return vars;
+}
+
+static struct query_var **
+query_split (virConnectPtr conn, const char *query, int *n_r)
+{
+    *n_r = 0;
+    if (!query || query[0] == '\0') return 0;
+
+    struct query_var **vars = 0;
+    char *p;
+    while ((p = strchr (query, '&'))) {
+        vars = get_next_var (conn, query, p-query, vars, n_r);
+        if (!vars) { *n_r = -1; return 0; }
+        query = p+1;
+    }
+    vars = get_next_var (conn, query, strlen (query), vars, n_r);
+    if (!vars) { *n_r = -1; return 0; }
+    return vars;
+}
+
+static char *
+query_join (virConnectPtr conn, struct query_var **vars, int *n_r)
+{
+    int i, len = 0;
+
+    if (vars == 0) return 0;
+
+    for (i = 0; i < *n_r; ++i)
+        if (!vars[i]->ignore)
+            len += strlen (vars[i]->name) + strlen (vars[i]->value) + 2;
+
+    char *str = malloc (len);
+    if (!str) {
+        error (conn, VIR_ERR_NO_MEMORY, "query_join");
+        free_query_vars (vars, *n_r); *n_r = -1; return 0;
+    }
+
+    len = 0;
+    for (i = 0; i < *n_r; ++i)
+        if (!vars[i]->ignore) {
+            if (len > 0) {
+                strcpy (str+len, "&");
+                len++;
+            }
+            strcpy (str+len, vars[i]->name);
+            len += strlen (vars[i]->name);
+            strcpy (str+len, "=");
+            len ++;
+            strcpy (str+len, vars[i]->value);
+            len += strlen (vars[i]->value);
+        }
+
+    return str;
+}
+
+static void
+free_query_vars (struct query_var **vars, int n)
+{
+    int i;
+    for (i = 0; i < n; ++i) {
+        free (vars[i]->name);
+        free (vars[i]);
+    }
+    free (vars);
+}
+
+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);
+}
+
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/src/remote_internal.h b/src/remote_internal.h
new file mode 100644
index 0000000..1f04ae1
--- /dev/null
+++ b/src/remote_internal.h
@@ -0,0 +1,45 @@
+/*
+ * 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
+
+extern void remoteRegister (void);
+
+#define LIBVIRTD_TLS_PORT "16514"
+#define LIBVIRTD_TCP_PORT "16509"
+//#define LIBVIRTD_UNIX_SOCKET "/var/run/libvirtd/socket"
+#define LIBVIRTD_UNIX_SOCKET "/tmp/socket" // Just for testing
+#define LIBVIRTD_CONFIGURATION_FILE "/etc/libvirtd.conf"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __VIR_REMOTE_INTERNAL_H__ */
+
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/src/remote_rpc.x b/src/remote_rpc.x
new file mode 100644
index 0000000..a6f685e
--- /dev/null
+++ b/src/remote_rpc.x
@@ -0,0 +1,52 @@
+/* -*- 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;
+
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/src/sunrpc/README b/src/sunrpc/README
new file mode 100644
index 0000000..4db813a
--- /dev/null
+++ b/src/sunrpc/README
@@ -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
diff --git a/src/sunrpc/clnt_ext.c b/src/sunrpc/clnt_ext.c
new file mode 100644
index 0000000..5a88dc7
--- /dev/null
+++ b/src/sunrpc/clnt_ext.c
@@ -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;
+}
diff --git a/src/sunrpc/clnt_ext.h b/src/sunrpc/clnt_ext.h
new file mode 100644
index 0000000..895b935
--- /dev/null
+++ b/src/sunrpc/clnt_ext.h
@@ -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__ */
diff --git a/src/sunrpc/clnt_gnutls.c b/src/sunrpc/clnt_gnutls.c
new file mode 100644
index 0000000..acf5fa4
--- /dev/null
+++ b/src/sunrpc/clnt_gnutls.c
@@ -0,0 +1,697 @@
+/*
+ * 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 <gnutls/x509.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,
+				 int no_verify, const char *hostname);
+/*
+ * 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,
+		   int no_verify, const char *hostname,
+		   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, no_verify, hostname) == -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 verify_certificate (gnutls_session_t session, const char *hostname);
+
+static int
+start_gnutls_session (gnutls_certificate_credentials_t xcred,
+		      struct ct_data *ct, int sock,
+		      int no_verify, const char *hostname)
+{
+  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);
+      return -1;
+    }
+
+  /* Verify certificate. */
+  if (verify_certificate (ct->ct_session, hostname) == -1)
+    {
+      fprintf (stderr, "clnt_gnutls: failed to verify peer's certificate\n");
+      if (!no_verify) return -1;
+    }
+
+  /* XXX A bug here causes a segfault.  While we were checking the
+   * server's certificate above, the server may have been checking
+   * our certificate, and may have decided to close the connection
+   * on us.  Unfortunately there is no simple way to know if the
+   * server has / will do this, and since we proceed, the first time
+   * we make a call (clnttcp_call) on the closed connection, we will
+   * get a segfault.
+   */
+
+#if 0
+  /* Print session info. */
+  print_info (session);
+#endif
+
+  return 0;
+}
+
+static int
+verify_certificate (gnutls_session_t session, const char *hostname)
+{
+  int ret;
+  unsigned int status;
+  const gnutls_datum_t *certs;
+  unsigned int nCerts, i;
+  time_t now;
+  
+  if ((ret = gnutls_certificate_verify_peers2 (session, &status)) < 0) {
+    fprintf(stderr, "clnt_gnutls: verify failed %s\n", gnutls_strerror(ret));
+    return -1;
+  }
+  
+  if ((now = time(NULL)) == ((time_t)-1)) {
+    return -1;
+  }
+  
+  if (status != 0) {
+    if (status & GNUTLS_CERT_INVALID)
+      fprintf (stderr, "clnt_gnutls: The certificate is not trusted.\n");
+    
+    if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+      fprintf (stderr, "clnt_gnutls: The certificate hasn't got a known issuer.\n");
+    
+    if (status & GNUTLS_CERT_REVOKED)
+      fprintf (stderr, "clnt_gnutls: The certificate has been revoked.\n");
+    
+    if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
+      fprintf (stderr, "clnt_gnutls: The certificate uses an insecure algorithm\n");
+    
+    return -1;
+  }
+
+  if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509)
+    return -1;
+  
+  if (!(certs = gnutls_certificate_get_peers(session, &nCerts)))
+    return -1;
+  
+  for (i = 0 ; i < nCerts ; i++) {
+    gnutls_x509_crt_t cert;
+    //printf ("Checking chain %d\n", i);
+    if (gnutls_x509_crt_init (&cert) < 0)
+      return -1;
+    
+    if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) {
+      gnutls_x509_crt_deinit (cert);
+      return -1;
+    }
+    
+    if (gnutls_x509_crt_get_expiration_time (cert) < now) {
+      fprintf (stderr, "clnt_gnutls: The certificate has expired\n");
+      gnutls_x509_crt_deinit (cert);
+      return -1;
+    }
+    
+    if (gnutls_x509_crt_get_activation_time (cert) > now) {
+      fprintf (stderr, "clnt_gnutls: The certificate is not yet activated\n");
+      gnutls_x509_crt_deinit (cert);
+      return -1;
+    }
+    
+    if (i == 0) {
+      if (!gnutls_x509_crt_check_hostname (cert, hostname)) {
+	fprintf (stderr, "clnt_gnutls: The certificate's owner does not match hostname '%s'\n",
+		 hostname);
+	gnutls_x509_crt_deinit (cert);
+	return -1;
+      }
+    }
+  }
+
+  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;
+}
diff --git a/src/sunrpc/clnt_gnutls.h b/src/sunrpc/clnt_gnutls.h
new file mode 100644
index 0000000..08035ec
--- /dev/null
+++ b/src/sunrpc/clnt_gnutls.h
@@ -0,0 +1,21 @@
+/*
+ * 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,
+				  int no_verify, const char *hostname,
+				  u_long prog, u_long vers,
+				  int *sockp, u_int sendsz, u_int recvsz);
+
+#endif /* __CLNT_GNUTLS_H__ */
diff --git a/src/sunrpc/clnt_tcp2.c b/src/sunrpc/clnt_tcp2.c
new file mode 100644
index 0000000..87ac30a
--- /dev/null
+++ b/src/sunrpc/clnt_tcp2.c
@@ -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;
+}
diff --git a/src/sunrpc/clnt_tcp2.h b/src/sunrpc/clnt_tcp2.h
new file mode 100644
index 0000000..a82d4b8
--- /dev/null
+++ b/src/sunrpc/clnt_tcp2.h
@@ -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__ */
diff --git a/src/sunrpc/create_xid.c b/src/sunrpc/create_xid.c
new file mode 100644
index 0000000..44ec268
--- /dev/null
+++ b/src/sunrpc/create_xid.c
@@ -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;
+}
diff --git a/src/sunrpc/svc_gnutls.c b/src/sunrpc/svc_gnutls.c
new file mode 100644
index 0000000..f46a109
--- /dev/null
+++ b/src/sunrpc/svc_gnutls.c
@@ -0,0 +1,623 @@
+/* 
+ * 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 <stdlib.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 <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#ifdef USE_IN_LIBIO
+# include <wchar.h>
+# include <libio/iolibio.h>
+#endif
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.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;
+    int no_verify_certificate;
+    int no_verify_address;
+    void *check_allowed_client_data;
+    int (*check_allowed_client) (void *, const char *);
+  };
+
+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,
+ *                                 etc.);
+ *
+ * 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,
+		  int no_verify_certificate, int no_verify_address,
+		  void *check_allowed_client_data,
+		  int (*check_allowed_client) (void *, const char *))
+{
+  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;
+  r->no_verify_certificate = no_verify_certificate;
+  r->no_verify_address = no_verify_address;
+  r->check_allowed_client_data = check_allowed_client_data;
+  r->check_allowed_client = check_allowed_client;
+  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 int verify_certificate (gnutls_session_t session, const char *addr);
+
+static bool_t
+rendezvous_request (SVCXPRT *xprt,
+		    struct rpc_msg *errmsg __attribute__((unused)))
+{
+  int sock;
+  struct tcp_rendezvous *r;
+  struct sockaddr_storage raddr;
+  socklen_t raddrlen;
+
+  r = (struct tcp_rendezvous *) xprt->xp_p1;
+  gnutls_session_t session = initialize_tls_session (r->x509_cred);
+
+again:
+  raddrlen = sizeof raddr;
+  if ((sock =
+       accept (xprt->xp_sock, (struct sockaddr *) &raddr, &raddrlen)) < 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)
+    {
+      fprintf (stderr, "svc_gnutls: TLS handshake failed (%s)\n\n",
+              gnutls_strerror (ret));
+    tls_failed:
+      // Possible DoS attack with an untrusted remote host?
+      //gnutls_bye (session, GNUTLS_SHUT_RDWR);
+      close (sock);
+      gnutls_deinit (session);
+      return FALSE;
+    }
+
+  /* Convert IP address to printable string (eg. "127.0.0.1") */
+  char addr[NI_MAXHOST];
+  (void) getnameinfo ((struct sockaddr *) &raddr, raddrlen,
+		      addr, sizeof addr, NULL, 0,
+		      NI_NUMERICHOST);
+
+  /* Verify client certificate. */
+  if (verify_certificate (session, addr) == -1)
+    {
+      fprintf (stderr, "svc_gnutls: failed to verify peer's certificate\n");
+      if (!r->no_verify_certificate) goto tls_failed;
+    }
+
+  if (!r->check_allowed_client (r->check_allowed_client_data, addr))
+    {
+      fprintf (stderr, "svc_gnutls: client is not on the list of allowed clients\n");
+      if (!r->no_verify_address) goto tls_failed;
+    }
+
+  /*
+   * make a new transporter (re-uses xprt)
+   */
+  xprt = makefd_xprt (sock, r->sendsize, r->recvsize, session);
+#if 0				/* This assumed IPv4 - commented out. */
+  memcpy (&xprt->xp_raddr, &addr, sizeof (addr));
+  xprt->xp_addrlen = len;
+#endif
+  return FALSE;		/* there is never an rpc msg to be processed */
+}
+
+static int my_gnutls_x509_crt_check_address (gnutls_x509_crt_t cert, const char *addr);
+
+static int
+verify_certificate (gnutls_session_t session, const char *addr)
+{
+  int ret;
+  unsigned int status;
+  const gnutls_datum_t *certs;
+  unsigned int nCerts, i;
+  time_t now;
+
+  if ((ret = gnutls_certificate_verify_peers2 (session, &status)) < 0){
+    fprintf (stderr, "svc_gnutls: verify failed: %s\n", gnutls_strerror (ret));
+    return -1;
+  }
+
+  if ((now = time(NULL)) == ((time_t)-1)) {
+    return -1;
+  }
+
+  if (status != 0) {
+    if (status & GNUTLS_CERT_INVALID)
+      fprintf (stderr, "svc_gnutls: The certificate is not trusted.\n");
+
+    if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+      fprintf (stderr, "svc_gnutls: The certificate hasn't got a known issuer.\n");
+
+    if (status & GNUTLS_CERT_REVOKED)
+      fprintf (stderr, "svc_gnutls: The certificate has been revoked.\n");
+
+    if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
+      fprintf (stderr, "svc_gnutls: The certificate uses an insecure algorithm\n");
+
+    return -1;
+  }
+
+  if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509)
+    return -1;
+
+  if (!(certs = gnutls_certificate_get_peers(session, &nCerts)))
+    return -1;
+
+  for (i = 0 ; i < nCerts ; i++) {
+    gnutls_x509_crt_t cert;
+
+    if (gnutls_x509_crt_init (&cert) < 0)
+      return -1;
+    
+    if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) {
+      gnutls_x509_crt_deinit (cert);
+      return -1;
+    }
+    
+    if (gnutls_x509_crt_get_expiration_time (cert) < now) {
+      fprintf(stderr, "svc_gnutls: The certificate has expired\n");
+      gnutls_x509_crt_deinit (cert);
+      return -1;
+    }
+    
+    if (gnutls_x509_crt_get_activation_time (cert) > now) {
+      fprintf(stderr, "svc_gnutls: The certificate is not yet activated\n");
+      gnutls_x509_crt_deinit (cert);
+      return -1;
+    }
+    
+    if (i == 0) {
+      if (!my_gnutls_x509_crt_check_address (cert, addr)) {
+	fprintf (stderr, "svc_gnutls: The certificate's owner does not match address '%s'\n",
+		 addr);
+	gnutls_x509_crt_deinit (cert);
+	return -1;
+      }
+    }
+  }
+
+  return 0;
+}
+
+/* Modified version of gnutls_x509_crt_check_hostname which checks
+ * a numeric IP address against iPAddress in the certificate.  It
+ * currently only allows exact matches (ie. not wildcards).  See
+ * RFC 2818 sections 3.1 & 3.2.
+ */
+static int
+my_gnutls_x509_crt_check_address (gnutls_x509_crt_t cert, const char *addr)
+{
+  char name[256]; // This is hard-coded in original too.
+  size_t namesize;
+  int i, ret = 0, found_iPAddress = 0;
+  for (i = 0; ret >= 0; ++i)
+    {
+      namesize = sizeof name;
+      ret =
+	gnutls_x509_crt_get_subject_alt_name (cert, i, name, &namesize, NULL);
+      if (ret == GNUTLS_SAN_IPADDRESS)
+	{
+	  found_iPAddress = 1;
+	  if (strcmp (name, addr) == 0) return 1;
+	}
+    }
+
+  if (!found_iPAddress)
+    fprintf (stderr, "svc_gnutls: client certificate does not contain subjectAltName iPAddress extension\n");
+
+  return 0; // Not found.
+}
+
+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;
+}
diff --git a/src/sunrpc/svc_gnutls.h b/src/sunrpc/svc_gnutls.h
new file mode 100644
index 0000000..4376343
--- /dev/null
+++ b/src/sunrpc/svc_gnutls.h
@@ -0,0 +1,21 @@
+/*
+ * 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,
+				  int no_verify_certificate,
+				  int no_verify_address,
+				  void *check_allowed_client_data,
+				  int (*check_allowed_client) (void *, const char *));
+
+#endif /* __SVC_GNUTLS_H__ */
diff --git a/src/sunrpc/svc_tcp2.c b/src/sunrpc/svc_tcp2.c
new file mode 100644
index 0000000..73d9598
--- /dev/null
+++ b/src/sunrpc/svc_tcp2.c
@@ -0,0 +1,430 @@
+/*
+ * 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;
+  // Actually we never use or store the returned address.  The old code
+  // assumes an IPv4 address, provides a way to store it, and provides
+  // a way for the server to query it.  I have commented out that code.
+  struct sockaddr_storage raddr;
+  socklen_t raddrlen;
+
+  r = (struct tcp_rendezvous *) xprt->xp_p1;
+again:
+  raddrlen = sizeof raddr;
+  if ((sock =
+       accept (xprt->xp_sock, (struct sockaddr *) &raddr, &raddrlen)) < 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;
+}
diff --git a/src/sunrpc/svc_tcp2.h b/src/sunrpc/svc_tcp2.h
new file mode 100644
index 0000000..cffa467
--- /dev/null
+++ b/src/sunrpc/svc_tcp2.h
@@ -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__ */
diff --git a/src/virsh.c b/src/virsh.c
index c5508a2..8c6f195 100644
--- a/src/virsh.c
+++ b/src/virsh.c
@@ -2760,7 +2760,7 @@ vshParseArgv(vshControl * ctl, int argc, char **argv)
     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);
diff --git a/src/virterror.c b/src/virterror.c
index 5d3a99a..156e85b 100644
--- a/src/virterror.c
+++ b/src/virterror.c
@@ -268,6 +268,9 @@ virDefaultErrorFunc(virErrorPtr err)
         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 @@ __virErrorMsg(virErrorNumber error, const char *info)
 	    else
 	        errmsg = "%s";
             break;
+        case VIR_ERR_RPC:
+	    if (info == NULL)
+	        errmsg = _("RPC error");
+	    else
+	        errmsg = "%s";
+	    break;
     }
     return (errmsg);
 }
