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

[Libvir] Remote patch, 2007-02-28




http://www.annexia.org/tmp/libvirt-tls-20070228.patch

Not an enormous amount of difference between this and the patch from two days ago. This patch correctly logs/audits new connections and fixes a whole collection of different bugs which used to happen when the server rejected a client connection.

Rich.

--
Emerging Technologies, Red Hat  http://et.redhat.com/~rjones/
64 Baker Street, London, W1U 7DF     Mobile: +44 7866 314 421
 "[Negative numbers] darken the very whole doctrines of the equations
 and make dark of the things which are in their nature excessively
 obvious and simple" (Francis Maseres FRS, mathematician, 1759)
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/configure.in libvirt-remote/configure.in
--- libvirt-cvs/configure.in	2007-02-23 12:50:58.000000000 +0000
+++ libvirt-remote/configure.in	2007-02-28 16:44:27.000000000 +0000
@@ -42,7 +42,15 @@
 AC_PATH_PROG(TAR, tar, /bin/tar)
 AC_PATH_PROG(XMLLINT, xmllint, /usr/bin/xmllint)
 AC_PATH_PROG(XSLTPROC, xsltproc, /usr/bin/xsltproc)
+AC_PATH_PROG(LOGGER, logger)
 
+AC_DEFINE_UNQUOTED(LOGGER, "$LOGGER",
+      [Define the location of the external 'logger' program, or
+       undefine to disable use of external 'logger'.  This is
+       used by libvirtd to write to syslog.])
+
+dnl Availability of various common functions.
+AC_CHECK_FUNCS([lrand48_r])
 
 dnl Make sure we have an ANSI compiler
 AM_C_PROTOTYPES
@@ -73,6 +81,8 @@
 
 dnl
 dnl make CFLAGS very pedantic at least during the devel phase for everybody
+dnl (Overriding $CFLAGS here is very wrong.  See discussion of AM_CFLAGS
+dnl in the automake info. - RWMJ)
 dnl
     if test "${GCC}" = "yes" ; then
 	CFLAGS="-g -O -W -Wformat -Wunused -Wimplicit -Wreturn-type -Wswitch -Wcomment -Wtrigraphs -Wformat -Wchar-subscripts -Wuninitialized -Wparentheses -Wshadow -Wpointer-arith -Wcast-align -Wwrite-strings -Waggregate-return -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline -Wredundant-decls -Wall"
@@ -233,6 +243,14 @@
 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 /dev/urandom
+AC_CHECK_FILES([/dev/urandom])
+
 dnl virsh libraries
 AC_CHECK_LIB(curses, initscr, 
 	[VIRSH_LIBS="$VIRSH_LIBS -lcurses"],
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/.cvsignore libvirt-remote/.cvsignore
--- libvirt-cvs/.cvsignore	2007-02-16 17:06:38.000000000 +0000
+++ libvirt-remote/.cvsignore	2007-02-23 16:06:44.000000000 +0000
@@ -1,3 +1,4 @@
+.git
 Makefile
 aclocal.m4
 autom4te.cache
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/.gitignore libvirt-remote/.gitignore
--- libvirt-cvs/.gitignore	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-remote/.gitignore	2007-02-26 14:43:51.000000000 +0000
@@ -0,0 +1,95 @@
+*~
+*.o
+*.lo
+*.la
+*.orig
+*.rej
+.libs
+.deps
+127001cert.pem
+127001key.pem
+ABOUT-NLS
+COPYING
+CVS
+Makefile
+INSTALL
+aclocal.m4
+autom4te.cache
+stamp-h.in
+Makefile.in
+compile
+configure
+config.cache
+config.h
+config.h.in
+config.log
+config.guess
+config.rpath
+config.status
+config.sub
+demoCA
+depcomp
+install-sh
+missing
+stamp-h
+stamp-h1
+libtool
+ltconfig
+ltmain.sh
+update.log
+libvirt.pc
+libvirt.spec
+m4
+mkinstalldirs
+servercert.pem
+serverkey.pem
+src/remote_rpc_clnt.c
+src/remote_rpc_svc.c
+src/remote_rpc_xdr.c
+src/remote_rpc.h
+src/libvirtd
+src/libvirtd.conf
+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
+qemud/libvirt_qemud
+qemud/libvirtd
+tests/Makefile
+tests/Makefile.in
+tests/xmlrpctest
+tests/sexpr2xmltest
+tests/xml2sexprtest
+tests/virshtest
+tests/conftest
+tests/reconnect
+tests/xmconfigtest
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/include/libvirt/virterror.h libvirt-remote/include/libvirt/virterror.h
--- libvirt-cvs/include/libvirt/virterror.h	2007-02-14 15:40:54.000000000 +0000
+++ libvirt-remote/include/libvirt/virterror.h	2007-02-26 14:00:14.000000000 +0000
@@ -49,6 +49,7 @@
     VIR_FROM_CONF,	/* Error in the configuration file handling */
     VIR_FROM_QEMU,      /* Error at the QEMU daemon */
     VIR_FROM_NET,       /* Error when operating on a network */
+    VIR_FROM_REMOTE	/* Error from remote driver */
 } virErrorDomain;
 
 
@@ -119,6 +120,8 @@
     VIR_ERR_XML_DETAIL, /* detail of an XML error */
     VIR_ERR_INVALID_NETWORK, /* invalid network object */
     VIR_ERR_NETWORK_EXIST, /* the network already exist */
+    VIR_ERR_RPC,		/* some sort of RPC error */
+    VIR_ERR_RPC_BAD_CONNECTION,	/* bad connection in RPC */
 } virErrorNumber;
 
 /**
@@ -133,7 +136,7 @@
 /*
  * Errors can be handled as asynchronous callbacks or after the routine
  * failed. They can also be handled globally at the library level, or
- * at the connection level (which then has priority
+ * at the connection level (which then has priority).
  */
 
 virErrorPtr		virGetLastError		(void);
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/proxy/libvirt_proxy.c libvirt-remote/proxy/libvirt_proxy.c
--- libvirt-cvs/proxy/libvirt_proxy.c	2007-01-23 14:39:45.000000000 +0000
+++ libvirt-remote/proxy/libvirt_proxy.c	2007-02-28 16:12:51.000000000 +0000
@@ -17,6 +17,7 @@
 #include <sys/poll.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <locale.h>
 #include "internal.h"
 #include "proxy_internal.h"
 #include "xen_internal.h"
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/conf.c libvirt-remote/src/conf.c
--- libvirt-cvs/src/conf.c	2007-02-14 15:40:54.000000000 +0000
+++ libvirt-remote/src/conf.c	2007-02-28 17:22:37.000000000 +0000
@@ -146,7 +146,8 @@
     free(val);
 }
 
-virConfPtr virConfNew(void)
+virConfPtr
+_virConfNew(void)
 {
     virConfPtr ret;
 
@@ -694,7 +695,7 @@
  ************************************************************************/
 
 /**
- * virConfReadFile:
+ * _virConfReadFile:
  * @filename: the path to the configuration file.
  *
  * Reads a configuration file.
@@ -703,7 +704,7 @@
  *         read or parse the file, use virConfFree() to free the data.
  */
 virConfPtr
-virConfReadFile(const char *filename)
+_virConfReadFile(const char *filename)
 {
     char content[4096];
     int fd;
@@ -728,7 +729,7 @@
 }
 
 /**
- * virConfReadMem:
+ * _virConfReadMem:
  * @memory: pointer to the content of the configuration file
  * @len: lenght in byte
  *
@@ -739,7 +740,7 @@
  *         parse the content, use virConfFree() to free the data.
  */
 virConfPtr
-virConfReadMem(const char *memory, int len)
+_virConfReadMem(const char *memory, int len)
 {
     if ((memory == NULL) || (len < 0)) {
         virConfError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__, 0);
@@ -752,7 +753,7 @@
 }
 
 /**
- * virConfFree:
+ * _virConfFree:
  * @conf: a configuration file handle
  *
  * Frees all data associated to the handle
@@ -760,7 +761,7 @@
  * Returns 0 in case of success, -1 in case of error.
  */
 int
-virConfFree(virConfPtr conf)
+_virConfFree(virConfPtr conf)
 {
     virConfEntryPtr tmp;
     if (conf == NULL) {
@@ -784,7 +785,7 @@
 }
 
 /**
- * virConfGetValue:
+ * _virConfGetValue:
  * @conf: a configuration file handle
  * @entry: the name of the entry
  *
@@ -794,7 +795,7 @@
  *         associated will be freed when virConfFree() is called
  */
 virConfValuePtr
-virConfGetValue(virConfPtr conf, const char *setting)
+_virConfGetValue(virConfPtr conf, const char *setting)
 {
     virConfEntryPtr cur;
 
@@ -808,7 +809,7 @@
 }
 
 /**
- * virConfGetValue:
+ * _virConfSetValue:
  * @conf: a configuration file handle
  * @entry: the name of the entry
  * @value: the new configuration value
@@ -820,9 +821,11 @@
  *
  * Returns 0 on success, or -1 on failure.
  */
-int             virConfSetValue         (virConfPtr conf,
-                                         const char *setting,
-                                         virConfValuePtr value) {
+int
+_virConfSetValue (virConfPtr conf,
+                  const char *setting,
+                  virConfValuePtr value)
+{
     virConfEntryPtr cur, prev = NULL;
 
     cur = conf->entries;
@@ -864,7 +867,7 @@
 
 
 /**
- * virConfWriteFile:
+ * _virConfWriteFile:
  * @filename: the path to the configuration file.
  * @conf: the conf
  *
@@ -873,7 +876,7 @@
  * Returns the number of bytes written or -1 in case of error.
  */
 int
-virConfWriteFile(const char *filename, virConfPtr conf)
+_virConfWriteFile(const char *filename, virConfPtr conf)
 {
     virBufferPtr buf;
     virConfEntryPtr cur;
@@ -913,7 +916,7 @@
 }
 
 /**
- * virConfWriteMem:
+ * _virConfWriteMem:
  * @memory: pointer to the memory to store the config file
  * @len: pointer to the lenght in byte of the store, on output the size
  * @conf: the conf
@@ -926,7 +929,7 @@
  * Returns the number of bytes written or -1 in case of error.
  */
 int
-virConfWriteMem(char *memory, int *len, virConfPtr conf)
+_virConfWriteMem(char *memory, int *len, virConfPtr conf)
 {
     virBufferPtr buf;
     virConfEntryPtr cur;
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/conf.h libvirt-remote/src/conf.h
--- libvirt-cvs/src/conf.h	2006-11-15 19:46:23.000000000 +0000
+++ libvirt-remote/src/conf.h	2007-02-28 17:29:00.000000000 +0000
@@ -50,23 +50,32 @@
 typedef struct _virConf virConf;
 typedef virConf *virConfPtr;
 
-virConfPtr      virConfNew              (void);
-virConfPtr	virConfReadFile		(const char *filename);
-virConfPtr	virConfReadMem		(const char *memory,
+virConfPtr      _virConfNew             (void);
+virConfPtr	_virConfReadFile	(const char *filename);
+virConfPtr	_virConfReadMem		(const char *memory,
 					 int len);
-int		virConfFree		(virConfPtr conf);
+int		_virConfFree		(virConfPtr conf);
 
-virConfValuePtr	virConfGetValue		(virConfPtr conf,
+virConfValuePtr	_virConfGetValue	(virConfPtr conf,
 					 const char *setting);
-int             virConfSetValue         (virConfPtr conf,
+int             _virConfSetValue        (virConfPtr conf,
 					 const char *setting,
 					 virConfValuePtr value);
-int		virConfWriteFile	(const char *filename,
+int		_virConfWriteFile	(const char *filename,
 					 virConfPtr conf);
-int		virConfWriteMem		(char *memory,
+int		_virConfWriteMem	(char *memory,
 					 int *len,
 					 virConfPtr conf);
 
+#define virConfNew() (_virConfNew())
+#define virConfReadFile(f) (_virConfReadFile((f)))
+#define virConfReadMem(m,l) (_virConfReadMem((m),(l)))
+#define virConfFree(c) (_virConfFree((c)))
+#define virConfGetValue(c,s) (_virConfGetValue((c),(s)))
+#define virConfSetValue(c,s,v) (_virConfSetValue((c),(s),(v)))
+#define virConfWriteFile(f,c) (_virConfWriteFile((f),(c)))
+#define virConfWriteMem(m,l,c) (_virConfWriteMem((m),(l),(c)))
+
 #ifdef __cplusplus
 }
 #endif
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/.cvsignore libvirt-remote/src/.cvsignore
--- libvirt-cvs/src/.cvsignore	2007-02-23 17:15:42.000000000 +0000
+++ libvirt-remote/src/.cvsignore	2007-02-26 11:05:38.000000000 +0000
@@ -5,6 +5,11 @@
 *.lo
 *.la
 virsh
+libvirtd
+remote_rpc_clnt.c
+remote_rpc_svc.c
+remote_rpc_xdr.c
+remote_rpc.h
 *.gcda
 *.gcno
 *.gcov
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/driver.h libvirt-remote/src/driver.h
--- libvirt-cvs/src/driver.h	2007-02-23 08:51:30.000000000 +0000
+++ libvirt-remote/src/driver.h	2007-02-28 15:15:34.000000000 +0000
@@ -23,7 +23,8 @@
     VIR_DRV_TEST = 4,
     VIR_DRV_XEN_PROXY = 5,
     VIR_DRV_XEN_XM = 6,
-    VIR_DRV_QEMU = 7
+    VIR_DRV_QEMU = 7,
+    VIR_DRV_REMOTE = 8
 } virDrvNo;
 
 
@@ -32,10 +33,24 @@
     VIR_DRV_OPEN_RO = 2
 } virDrvOpenFlag;
 
-typedef int
-	(*virDrvOpen)			(virConnectPtr conn,
-					 const char *name,
-					 int flags);
+/* Status codes returned from driver open call. */
+typedef enum {
+    /* Opened successfully. */
+    VIR_DRV_OPEN_SUCCESS = 0,
+
+    /* 'name' is not for us. */
+    VIR_DRV_OPEN_DECLINED = -1,
+
+    /* 'name' is for us, but there was some error.  virConnectOpen will
+     * return an error rather than continue probing the other drivers.
+     */
+    VIR_DRV_OPEN_ERROR = -2,
+} virDrvOpenStatus;
+
+typedef virDrvOpenStatus
+    (*virDrvOpen)			(virConnectPtr conn,
+                             const char *name,
+                             int flags);
 typedef int
 	(*virDrvClose)			(virConnectPtr conn);
 typedef const char *
@@ -277,3 +292,17 @@
 }
 #endif /* __cplusplus */
 #endif /* __VIR_DRIVER_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 -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/hash.c libvirt-remote/src/hash.c
--- libvirt-cvs/src/hash.c	2007-02-14 15:40:54.000000000 +0000
+++ libvirt-remote/src/hash.c	2007-02-26 19:04:57.000000000 +0000
@@ -735,8 +735,8 @@
 /**
  * virGetDomain:
  * @conn: the hypervisor connection
- * @name: pointer to the domain name or NULL
- * @uuid: pointer to the uuid or NULL
+ * @name: pointer to the domain name
+ * @uuid: pointer to the uuid
  *
  * Lookup if the domain is already registered for that connection,
  * if yes return a new pointer to it, if no allocate a new structure,
@@ -749,7 +749,7 @@
 virGetDomain(virConnectPtr conn, const char *name, const unsigned char *uuid) {
     virDomainPtr ret = NULL;
 
-    if ((!VIR_IS_CONNECT(conn)) || ((name == NULL) && (uuid == NULL)) ||
+    if ((!VIR_IS_CONNECT(conn)) || (name == NULL) || (uuid == NULL) ||
         (conn->hashes_mux == NULL)) {
         virHashError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
         return(NULL);
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/internal.h libvirt-remote/src/internal.h
--- libvirt-cvs/src/internal.h	2007-02-14 15:40:54.000000000 +0000
+++ libvirt-remote/src/internal.h	2007-02-23 16:02:21.000000000 +0000
@@ -133,6 +133,13 @@
 
     int qemud_fd;           /* connection to qemud */
 
+    /* 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 */
@@ -235,3 +242,17 @@
 }
 #endif                          /* __cplusplus */
 #endif                          /* __VIR_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 -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/libvirt.c libvirt-remote/src/libvirt.c
--- libvirt-cvs/src/libvirt.c	2007-02-23 08:51:30.000000000 +0000
+++ libvirt-remote/src/libvirt.c	2007-02-28 15:24:43.000000000 +0000
@@ -30,6 +30,7 @@
 #include "xs_internal.h"
 #include "xm_internal.h"
 #include "proxy_internal.h"
+#include "remote_internal.h"
 #include "xml.h"
 #include "test.h"
 #include "qemu_internal.h"
@@ -77,6 +78,7 @@
     /*
      * Note that the order is important the first ones have a higher priority
      */
+    remoteRegister ();
     xenHypervisorRegister();
     xenProxyRegister();
     xenDaemonRegister();
@@ -303,16 +305,22 @@
     for (i = 0;i < MAX_DRIVERS;i++) {
         if ((virDriverTab[i] != NULL) && (virDriverTab[i]->open != NULL)) {
 	    res = virDriverTab[i]->open(ret, name, VIR_DRV_OPEN_QUIET);
-	    /*
-	     * For a default connect to Xen make sure we manage to contact
-	     * all related drivers.
-	     */
-	    if ((res < 0) && (for_xen) &&
-	        (!strncasecmp(virDriverTab[i]->name, "xen", 3)) &&
-		(virDriverTab[i]->no != VIR_DRV_XEN_PROXY))
-		goto failed;
-	    if (res == 0)
+	    switch (res)
+	      {
+	      case VIR_DRV_OPEN_ERROR: goto failed;
+	      case VIR_DRV_OPEN_DECLINED:
+		/*
+		 * For a default connect to Xen make sure we manage to contact
+		 * all related drivers.
+		 */
+		if (for_xen &&
+		    strncasecmp(virDriverTab[i]->name, "xen", 3) == 0 &&
+		    virDriverTab[i]->no != VIR_DRV_XEN_PROXY)
+		  goto failed;
+		break;
+	      case VIR_DRV_OPEN_SUCCESS:
 	        ret->drivers[ret->nb_drivers++] = virDriverTab[i];
+	      }
 	}
     }
 
@@ -379,9 +387,13 @@
         if ((virDriverTab[i] != NULL) && (virDriverTab[i]->open != NULL)) {
 	    res = virDriverTab[i]->open(ret, name,
 	                                VIR_DRV_OPEN_QUIET | VIR_DRV_OPEN_RO);
-	    if (res == 0)
-	        ret->drivers[ret->nb_drivers++] = virDriverTab[i];
-
+	    switch (res)
+	      {
+	      case VIR_DRV_OPEN_ERROR: goto failed;
+	      case VIR_DRV_OPEN_DECLINED: break;
+	      case VIR_DRV_OPEN_SUCCESS:
+		ret->drivers[ret->nb_drivers++] = virDriverTab[i];
+	      }
 	}
         if ((virNetworkDriverTab[i] != NULL) && (virNetworkDriverTab[i]->open != NULL) &&
 	    (res = virNetworkDriverTab[i]->open(ret, name, VIR_DRV_OPEN_QUIET)) == 0) {
@@ -455,6 +467,7 @@
  *
  * Returns NULL in case of error, a static zero terminated string otherwise.
  */
+/* See also: https://www.redhat.com/archives/libvir-list/2007-February/msg00096.html */
 const char *
 virConnectGetType(virConnectPtr conn)
 {
@@ -2892,3 +2905,17 @@
     virLibConnError(network->conn, VIR_ERR_CALL_FAILED, __FUNCTION__);
     return (-1);
 }
+
+/*
+ * 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 -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/libvirtd.c libvirt-remote/src/libvirtd.c
--- libvirt-cvs/src/libvirtd.c	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-remote/src/libvirtd.c	2007-02-28 14:21:48.000000000 +0000
@@ -0,0 +1,1014 @@
+/*
+ * 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 <fcntl.h>
+#include <malloc.h>
+#include <time.h>
+#include <rpc/rpc.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <netdb.h>
+#include <errno.h>
+#include <getopt.h>
+#include <assert.h>
+#include <fnmatch.h>
+#include <gnutls/gnutls.h>
+#include <stdint.h>
+
+#include "config.h"
+#include "sunrpc/svc_gnutls.h"
+#include "sunrpc/svc_tcp2.h"
+#include "sunrpc/svc_unix2.h"
+#include "remote_internal.h"
+#include "remote_rpc.h"
+#include "conf.h"
+#include "libvirt/virterror.h"
+#include "libvirt/libvirt.h"
+#include "internal.h"
+
+/* Set by the command line. */
+static int debug = 0, quiet = 0;
+
+/* Defaults for configuration file elements. */
+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 = "serverkey.pem";
+const char *cert_file = "servercert.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);
+
+static void copy_error (remote_error *err, virErrorPtr verr);
+static void out_of_memory_error (remote_error *err);
+static void bad_connection_error (remote_error *err);
+
+static virConnectPtr lookup_connection (struct svc_req *req);
+static void set_connection (struct svc_req *req, virConnectPtr conn);
+
+/*----------------------------------------------------------------------*/
+/* Server side of the remote procedure calls. */
+
+remote_open_ret *
+remote_open_1_svc (char **name, struct svc_req *req)
+{
+    static struct remote_open_ret ret;
+
+    if (lookup_connection (req) != 0) {
+        ret.status = -1;
+        bad_connection_error (&ret.remote_open_ret_u.err);
+    } else {
+        /* If the name is "", convert it to NULL. */
+        char *real_name = *name;
+        if (strcmp (real_name, "") == 0) real_name = NULL;
+
+        /* Log connection name. */
+        fprintf (stderr, "libvirtd (%d): open \"%s\"\n", getpid (), real_name);
+
+        virConnectPtr conn = virConnectOpen (real_name);
+
+        if (conn) {
+            set_connection (req, conn);
+            ret.status = 0;
+        } else {
+            ret.status = -1;
+            copy_error (&ret.remote_open_ret_u.err, virGetLastError ());
+        }
+    }
+
+    return &ret;
+}
+
+remote_close_ret *
+remote_close_1_svc (void *vp __attribute__((unused)),
+                        struct svc_req *req)
+{
+    static struct remote_close_ret ret;
+    virConnectPtr conn = lookup_connection (req);
+
+    if (!conn) {
+        ret.status = -1;
+        bad_connection_error (&ret.remote_close_ret_u.err);
+    } else {
+        int r = virConnectClose (conn);
+
+        if (r == 0) {
+            ret.status = 0;
+            set_connection (req, 0);
+        } else {
+            ret.status = -1;
+            copy_error (&ret.remote_close_ret_u.err,
+                        virConnGetLastError (conn));
+        }
+    }
+
+    return &ret;
+}
+
+remote_type_ret *
+remote_type_1_svc (void *vp __attribute__((unused)),
+                   struct svc_req *req)
+{
+    static struct remote_type_ret ret;
+    virConnectPtr conn = lookup_connection (req);
+
+    if (!conn) {
+        ret.status = -1;
+        bad_connection_error (&ret.remote_type_ret_u.err);
+    } else {
+        const char *type = virConnectGetType (conn);
+
+        if (type) {
+            ret.status = 0;
+            ret.remote_type_ret_u.type = (char *) type; // string is static
+        } else {
+            ret.status = -1;
+            copy_error (&ret.remote_type_ret_u.err,
+                        virConnGetLastError (conn));
+        }
+    }
+
+    return &ret;
+}
+
+remote_version_ret *
+remote_version_1_svc (void *vp __attribute__((unused)),
+                      struct svc_req *req)
+{
+    static struct remote_version_ret ret;
+    virConnectPtr conn = lookup_connection (req);
+
+    if (!conn) {
+        ret.status = -1;
+        bad_connection_error (&ret.remote_version_ret_u.err);
+    } else {
+        unsigned long hvVer;
+        if (virConnectGetVersion (conn, &hvVer) == 0) {
+            ret.status = 0;
+            ret.remote_version_ret_u.hvVer = hvVer;
+        } else {
+            ret.status = -1;
+            copy_error (&ret.remote_version_ret_u.err,
+                        virConnGetLastError (conn));
+        }
+    }
+
+    return &ret;
+}
+
+remote_nodeGetInfo_ret *
+remote_nodegetinfo_1_svc (void *vp __attribute__((unused)),
+                          struct svc_req *req)
+{
+    static struct remote_nodeGetInfo_ret ret;
+    virConnectPtr conn = lookup_connection (req);
+
+    if (!conn) {
+        ret.status = -1;
+        bad_connection_error (&ret.remote_nodeGetInfo_ret_u.err);
+    } else {
+        static virNodeInfo info;
+        if (virNodeGetInfo (conn, &info) == 0) {
+            ret.status = 0;
+            ret.remote_nodeGetInfo_ret_u.info.model = info.model;
+            ret.remote_nodeGetInfo_ret_u.info.memory = info.memory;
+            ret.remote_nodeGetInfo_ret_u.info.cpus = info.cpus;
+            ret.remote_nodeGetInfo_ret_u.info.mhz = info.mhz;
+            ret.remote_nodeGetInfo_ret_u.info.nodes = info.nodes;
+            ret.remote_nodeGetInfo_ret_u.info.sockets = info.sockets;
+            ret.remote_nodeGetInfo_ret_u.info.cores = info.cores;
+            ret.remote_nodeGetInfo_ret_u.info.threads = info.threads;
+        } else {
+            ret.status = -1;
+            copy_error (&ret.remote_nodeGetInfo_ret_u.err,
+                        virConnGetLastError (conn));
+        }
+    }
+
+    return &ret;
+}
+
+// Just a number large enough to use for validation of the
+// externally produced maxids.
+#define MAX_DOMAINS 10000
+
+remote_listDomains_ret *
+remote_listdomains_1_svc (int *maxids,
+                          struct svc_req *req)
+{
+    static struct remote_listDomains_ret ret;
+    virConnectPtr conn = lookup_connection (req);
+
+    if (!conn) {
+        ret.status = -1;
+        bad_connection_error (&ret.remote_listDomains_ret_u.err);
+           // Sanity-check maxids before allocating the on-stack array.
+    } else if (*maxids < 0 || *maxids > MAX_DOMAINS) {
+        ret.status = -1;
+        out_of_memory_error (&ret.remote_listDomains_ret_u.err);
+    } else {
+        int ids[*maxids];
+        int len = virConnectListDomains (conn, ids, *maxids);
+
+        if (len >= 0) {
+            ret.status = 0;
+            ret.remote_listDomains_ret_u.ids.ids_len = len;
+            ret.remote_listDomains_ret_u.ids.ids_val = ids;
+        } else {
+            ret.status = -1;
+            copy_error (&ret.remote_listDomains_ret_u.err,
+                        virConnGetLastError (conn));
+        }
+    }
+
+    return &ret;
+}
+
+remote_numOfDomains_ret *
+remote_numofdomains_1_svc (void *vp __attribute__((unused)),
+                           struct svc_req *req)
+{
+    static struct remote_numOfDomains_ret ret;
+    virConnectPtr conn = lookup_connection (req);
+
+    if (!conn) {
+        ret.status = -1;
+        bad_connection_error (&ret.remote_numOfDomains_ret_u.err);
+    } else {
+        int nr = virConnectNumOfDomains (conn);
+
+        if (nr >= 0) {
+            ret.status = 0;
+            ret.remote_numOfDomains_ret_u.nr_domains = nr;
+        } else {
+            ret.status = -1;
+            copy_error (&ret.remote_numOfDomains_ret_u.err,
+                        virConnGetLastError (conn));
+        }
+    }
+
+    return &ret;
+}
+
+remote_domainCreateLinux_ret *
+remote_domaincreatelinux_1_svc (remote_domainCreateLinux_args *args,
+                                struct svc_req *req)
+{
+    static struct remote_domainCreateLinux_ret ret;
+    virConnectPtr conn = lookup_connection (req);
+
+    if (!conn) {
+        ret.status = -1;
+        bad_connection_error (&ret.remote_domainCreateLinux_ret_u.err);
+    } else {
+        virDomainPtr dom = virDomainCreateLinux (conn,
+                                                 args->xmlDesc, args->flags);
+
+        if (dom) {
+            ret.status = 0;
+            // XXX No idea if this is the right thing to do.
+            ret.remote_domainCreateLinux_ret_u.domain =
+                malloc (sizeof *ret.remote_domainCreateLinux_ret_u.domain);
+            ret.remote_domainCreateLinux_ret_u.domain->name =
+                (char *) dom->name;
+            memcpy
+                (ret.remote_domainCreateLinux_ret_u.domain->uuid,
+                 dom->uuid,
+                 VIR_UUID_BUFLEN);
+        } else {
+            ret.status = -1;
+            copy_error (&ret.remote_domainCreateLinux_ret_u.err,
+                        virConnGetLastError (conn));
+        }
+    }
+
+    return &ret;
+}
+
+
+
+
+
+
+/* You cannot use NULL for string<> in SunRPC.  Instead, pass
+ * empty strings (using nasty casting to get around lack of
+ * any sort of const-correctness in SunRPC code).  Convert
+ * empty strings on the client side back to NULLs.
+ */
+static char *null_string = (char *) "";
+
+/* Handle translation between <virterror.h> and what we send
+ * back to the client over the wire.
+ */
+static void
+copy_error (remote_error *err, virErrorPtr verr)
+{
+    err->code = verr->code;
+    err->domain = verr->domain;
+    err->message = verr->message ? : null_string;
+    err->level = verr->level;
+    if (verr->dom) {
+        // XXX I have no idea if this is the right way to do this.
+        err->dom = malloc (sizeof *err->dom);
+        err->dom->name = verr->dom->name;
+        memcpy (err->dom->uuid, verr->dom->uuid, VIR_UUID_BUFLEN);
+    }
+    else err->dom = NULL;
+    err->str1 = verr->str1 ? : null_string;
+    err->str2 = verr->str2 ? : null_string;
+    err->str3 = verr->str3 ? : null_string;
+    err->int1 = verr->int1;
+    err->int2 = verr->int2;
+    if (verr->net) {
+        // XXX I have no idea if this is the right way to do this.
+        err->net = malloc (sizeof *err->net);
+        err->net->name = verr->net->name;
+        memcpy (err->net->uuid, verr->net->uuid, VIR_UUID_BUFLEN);
+    } else err->net = NULL;
+}
+
+/* We have a few of our own errors. */
+static void
+out_of_memory_error (remote_error *err)
+{
+    char *msg = (char *) "out of memory";
+    err->code = VIR_ERR_NO_MEMORY;
+    err->domain = VIR_FROM_REMOTE;
+    err->message = msg;
+    err->level = VIR_ERR_ERROR;
+    err->dom = NULL;
+    err->str1 = msg;
+    err->str2 = null_string;
+    err->str3 = null_string;
+    err->int1 = 0;
+    err->int2 = 0;
+    err->net = NULL;
+}
+
+static void
+bad_connection_error (remote_error *err)
+{
+    char *msg = (char *) "bad connection";
+    err->code = VIR_ERR_RPC_BAD_CONNECTION;
+    err->domain = VIR_FROM_REMOTE;
+    err->message = msg;
+    err->level = VIR_ERR_ERROR;
+    err->dom = NULL;
+    err->str1 = msg;
+    err->str2 = null_string;
+    err->str3 = null_string;
+    err->int1 = 0;
+    err->int2 = 0;
+    err->net = NULL;
+}
+
+/*----------------------------------------------------------------------*/
+/* Map SunRPC connections to virConnectPtr. */
+
+/* SunRPC is "connectionless", but in fact when used over TCP it is
+ * really connection-oriented.  If your TCP connection goes down,
+ * the client needs to manually reconnect, which the remote client
+ * never does.  So we associate virConnectPtr with an actual TCP
+ * connection.
+ *
+ * The questions are: (1) How and where do we store this association?
+ * (2) How can we clean up when the connection goes away?
+ *
+ * So for (1) we note that each server-side callback gets a pointer
+ * to struct svc_req, which contains a pointer to the transport
+ * (SVCXPRT *).  It turns out (you need to read the code closely)
+ * that each transport pointer is unique to the connection, so we
+ * use that.
+ *
+ * For (2) we have modified the transports so that we can supply a
+ * cleanup callback which is called when the connection goes away.
+ *
+ * Now all we need is a mapping from SVCXPRT * to virConnectPtr,
+ * and a function to clean these up.
+ */
+
+static struct virconnmap {
+    struct virconnmap *next;
+
+    /* Key. */
+    SVCXPRT *xprt;
+
+    /* conn may be NULL in the case where a mapping exists, but the
+     * virConnectPtr has either not been created yet, or has been
+     * properly closed using the remote_close call.
+     */
+    virConnectPtr conn;
+} *virconnmap = 0;
+
+static void log_peer (int sock);
+
+/* Callback from transport: create a new mapping. */
+static int
+create_mapping (SVCXPRT *xprt)
+{
+    log_peer (xprt->xp_sock);
+
+    /* It's an internal error if it exists already. */
+    struct virconnmap *p;
+    for (p = virconnmap; p; p = p->next)
+        if (p->xprt == xprt) {
+            fprintf (stderr, "libvirtd: internal error: create_mapping called but mapping already exists\n");
+            return -1;
+        }
+
+    /* Add the mapping. */
+    p = malloc (sizeof *p);
+    if (p == 0) {
+        perror ("malloc");
+        return -1;
+    }
+    p->next = virconnmap;
+    virconnmap = p;
+    p->xprt = xprt;
+    p->conn = 0;
+    return 0;
+}
+
+/* Callback from transport: destroy an existing mapping. */
+static void
+destroy_mapping (SVCXPRT *xprt)
+{
+    /* Log connection closed. */
+    fprintf (stderr, "libvirtd (%d): connection closed\n", getpid ());
+
+    struct virconnmap *p, *lastp;
+    for (p = virconnmap, lastp = 0; p; lastp = p, p = p->next)
+        if (p->xprt == xprt)
+            goto found_it;
+
+    /* Mapping not found - that's an internal error. */
+    fprintf (stderr, "libvirtd: internal error: destroy_mapping called but mapping not found\n");
+    return;
+
+    found_it:
+    /* Do we need to clean up the connection?  If the client called
+     * remote_close then conn will be NULL.  Otherwise the connection
+     * has been dropped without a clean close, so we close it here.
+     */
+    if (p->conn)
+        (void) virConnectClose (p->conn);
+
+    /* Remove from linked list. */
+    if (lastp) lastp->next = p->next;
+    else virconnmap = p->next;
+    free (p);
+}
+
+/* Look up a connection in the mapping table. */
+static virConnectPtr
+lookup_connection (struct svc_req *req)
+{
+    SVCXPRT *xprt = req->rq_xprt;
+
+    struct virconnmap *p;
+    for (p = virconnmap; p; p = p->next)
+        if (p->xprt == xprt)
+            return p->conn;
+
+    fprintf (stderr, "libvirtd: internal error: cannot find connection in mapping table\n");
+    return NULL;
+}
+
+/* Set connection in the mapping table. */
+static void
+set_connection (struct svc_req *req, virConnectPtr conn)
+{
+    SVCXPRT *xprt = req->rq_xprt;
+
+    struct virconnmap *p;
+    for (p = virconnmap; p; p = p->next)
+        if (p->xprt == xprt) {
+            p->conn = conn;
+            return;
+        }
+
+    abort (); // Should never happen.
+}
+
+static void
+log_peer (int sock)
+{
+    /* Log the connection to stderr.  It will end up in syslog if
+     * logger is running.
+     */
+    int pid = getpid (), r;
+    fprintf (stderr, "libvirtd (%d): new connection\n", pid);
+    struct sockaddr_storage addr;
+    socklen_t addrlen = sizeof addr;
+    r = getpeername (sock, (struct sockaddr *) &addr, &addrlen);
+    if (r == -1)
+        perror ("getpeername");
+    else {
+        if (addr.ss_family == AF_UNIX)
+            /* getnameinfo doesn't do anything sensible with AF_UNIX
+             * addresses, but doesn't fail either (it sets host to
+             * "localhost" and leaves serv as random), so print those
+             * out manually instead.
+             */
+            fprintf (stderr, "libvirtd (%d): connection from Unix domain socket (may include connections over ssh and ext transports)\n",
+                     pid);
+        else {
+            char host[NI_MAXHOST] = { '\0' }, serv[NI_MAXSERV] = { '\0' };
+            r = getnameinfo ((struct sockaddr *) &addr, addrlen,
+                             host, sizeof host,
+                             serv, sizeof serv,
+                             NI_NUMERICHOST | NI_NUMERICSERV);
+            if (r != 0)
+                fprintf (stderr, "libvirtd (%d): getnameinfo: %s\n",
+                         pid, gai_strerror (r));
+            else
+                fprintf (stderr, "libvirtd (%d): connection from %s port %s\n",
+                         pid, host, serv);
+        }
+    }
+}
+
+/*----------------------------------------------------------------------*/
+/* 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);
+
+int
+main (int argc, char *argv[])
+{
+    int 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) {
+        int err;
+
+        /* Initialise GnuTLS. */
+        gnutls_global_init ();
+
+        err = gnutls_certificate_allocate_credentials (&x509_cred);
+        if (err) { gnutls_perror (err); exit (1); }
+        if (ca_file && ca_file[0] != '\0') {
+            if (!quiet)
+                fprintf (stderr, "libvirtd: loading CA cert from %s\n",
+                         ca_file);
+            err = gnutls_certificate_set_x509_trust_file (x509_cred, ca_file,
+                                                          GNUTLS_X509_FMT_PEM);
+            if (err < 0) { gnutls_perror (err); exit (1); }
+        }
+
+        if (crl_file && crl_file[0] != '\0') {
+            if (!quiet)
+                fprintf (stderr, "libvirtd: loading CRL from %s\n",
+                         crl_file);
+            err = gnutls_certificate_set_x509_crl_file (x509_cred, crl_file,
+                                                        GNUTLS_X509_FMT_PEM);
+            if (err < 0) { gnutls_perror (err); exit (1); }
+        }
+
+        if (cert_file && cert_file[0] != '\0' &&
+            key_file && key_file[0] != '\0') {
+            if (!quiet)
+                fprintf (stderr,
+                         "libvirtd: loading cert and key from %s and %s\n",
+                         cert_file, key_file);
+            err =
+                gnutls_certificate_set_x509_key_file (x509_cred,
+                                                      cert_file, key_file,
+                                                      GNUTLS_X509_FMT_PEM);
+            if (err < 0) { gnutls_perror (err); exit (1); }
+        }
+
+        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,
+                                                create_mapping,
+                                                destroy_mapping,
+                                                x509_cred,
+                                                tls_no_verify_certificate,
+                                                tls_no_verify_address,
+                                                NULL, check_allowed_client);
+            if (!transp) {
+                fprintf (stderr, "libvirtd: cannot create TLS service on port %s\n", tls_port);
+                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,
+                                              create_mapping,
+                                              destroy_mapping);
+            if (!transp) {
+                fprintf (stderr, "libvirtd: cannot create TCP service on port %s\n", tcp_port);
+                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 = svcunix2_create (sock, 0, 0,
+                                           create_mapping,
+                                           destroy_mapping,
+                                           (char *) unix_socket);
+        if (!transp) {
+            fprintf (stderr, "libvirtd: cannot create Unix domain socket service on socket %s\n", unix_socket);
+            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) {
+        if (daemon (0, 0) == -1) {
+            perror ("daemon");
+            exit (2);
+        }
+
+#ifdef LOGGER
+        /* Send stderr to syslog using logger.  It's a lot simpler
+         * to do this.  Note that SunRPC in glibc prints lots of
+         * gumf to stderr and it'd be a load of work to change that.
+         */
+        int fd[2];
+        if (pipe (fd) == -1) {
+            perror ("pipe");
+            exit (2);
+        }
+        int pid = fork ();
+        if (pid == -1) {
+            perror ("fork");
+            exit (2);
+        }
+        if (pid == 0) {         /* Child - logger. */
+            const char *args[] = {
+                "logger [libvirtd]",
+                "-t", "libvirtd",
+                "-p", "daemon.notice",
+                NULL
+            };
+            close (fd[1]);
+            dup2 (fd[0], 0);
+            close (fd[0]);
+            execv (LOGGER, (char *const *) args);
+            perror ("execv");
+            _exit (1);
+        }
+        close (fd[0]);
+        dup2 (fd[1], 2);
+        close (fd[1]);
+#endif
+    }
+
+    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)
+{
+    int err;
+
+    /* 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.
+     */
+    err = gnutls_dh_params_init (&dh_params);
+    if (err) { gnutls_perror (err); exit (1); }
+    err = gnutls_dh_params_generate2 (dh_params, DH_BITS);
+    if (err) { gnutls_perror (err); exit (1); }
+}
+
+// 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 -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/libvirt_sym.version libvirt-remote/src/libvirt_sym.version
--- libvirt-cvs/src/libvirt_sym.version	2007-02-23 08:51:30.000000000 +0000
+++ libvirt-remote/src/libvirt_sym.version	2007-02-28 17:17:05.000000000 +0000
@@ -60,6 +60,10 @@
     virDomainAttachDevice;
     virDomainDetachDevice;
 
+	_virConfReadFile;
+	_virConfGetValue;
+	_virConfFree;
+
 	virConnectNumOfNetworks;
 	virConnectListNetworks;
 	virConnectNumOfDefinedNetworks;
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/Makefile.am libvirt-remote/src/Makefile.am
--- libvirt-cvs/src/Makefile.am	2007-02-23 12:46:35.000000000 +0000
+++ libvirt-remote/src/Makefile.am	2007-02-28 16:04:34.000000000 +0000
@@ -3,6 +3,7 @@
 INCLUDES = -I$(top_builddir)/include -I top_srcdir@/include @LIBXML_CFLAGS@ -I top_srcdir@/qemud \
 	   -DBINDIR=\""$(libexecdir)"\" -DSBINDIR=\""$(sbindir)"\" -DLOCALEBASEDIR=\""$(datadir)/locale"\" \
            -DLOCAL_STATE_DIR=\""$(localstatedir)"\" \
+	   -DSYSCONFDIR=\""$(sysconfdir)"\" \
            -DGETTEXT_PACKAGE=\"$(PACKAGE)\"
 DEPS = libvirt.la
 LDADDS = @STATIC_BINARIES@ libvirt.la
@@ -29,10 +30,17 @@
 		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		\
 		qemu_internal.c qemu_internal.h
 
 bin_PROGRAMS = virsh
+sbin_PROGRAMS = libvirtd
 
 virsh_SOURCES = virsh.c console.c console.h
 virsh_LDFLAGS = $(COVERAGE_LDFLAGS)
@@ -40,6 +48,26 @@
 virsh_LDADD = $(LDADDS) $(VIRSH_LIBS)
 virsh_CFLAGS = $(COVERAGE_CFLAGS)
 
+libvirtd_SOURCES =						\
+		remote_rpc_svc.c remote_rpc_xdr.c		\
+		sunrpc/svc_gnutls.c sunrpc/svc_gnutls.h		\
+		sunrpc/svc_tcp2.c sunrpc/svc_tcp2.h		\
+		sunrpc/svc_unix2.c sunrpc/svc_unix2.h		\
+		libvirtd.c
+libvirtd_LDFLAGS =
+libvirtd_DEPENDENCIES = $(DEPS)
+libvirtd_LDADD = $(LDADDS)
+
+# 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
 #
@@ -56,4 +84,5 @@
 %.cov: .libs/%.o
 	gcov -b -f -o .libs $< > $@
 
-CLEANFILES = *.cov *.gcov .libs/*.gcda .libs/*.gcno *.gcno *.gcda
+CLEANFILES = *.cov *.gcov .libs/*.gcda .libs/*.gcno *.gcno *.gcda \
+	remote_rpc_clnt.c remote_rpc.h remote_rpc_svc.c remote_rpc_xdr.c
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/remote_internal.c libvirt-remote/src/remote_internal.c
--- libvirt-cvs/src/remote_internal.c	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-remote/src/remote_internal.c	2007-02-28 16:58:50.000000000 +0000
@@ -0,0 +1,1051 @@
+/*
+ * 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 <stdint.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 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);
+static void server_error (virConnectPtr conn, remote_error *err);
+static virDomainPtr get_domain_from_name (virConnectPtr conn, remote_domain);
+static virNetworkPtr get_network_from_name (virConnectPtr conn, remote_network);
+
+#define CAFILE "demoCA/cacert.pem" /* XXX */
+#define KEY_FILE "127001key.pem" /* XXX */
+#define CERT_FILE "127001cert.pem" /* XXX */
+
+static gnutls_certificate_credentials_t x509_cred;
+
+static int
+initialise_gnutls (void)
+{
+    static int initialised = 0;
+    int err;
+
+    if (initialised) return 0;
+
+    gnutls_global_init ();
+
+    /* X509 stuff */
+    err = gnutls_certificate_allocate_credentials (&x509_cred);
+    if (err) { gnutls_perror (err); return -1; }
+
+    /* Set the trusted CA cert. */
+#if DEBUG
+    fprintf (stderr, "loading CA file %s\n", CAFILE);
+#endif
+    err =
+        gnutls_certificate_set_x509_trust_file (x509_cred, CAFILE,
+                                                GNUTLS_X509_FMT_PEM);
+    if (err < 0) { gnutls_perror (err); return -1; }
+
+    /* Set the client certificate and private key. */
+#if DEBUG
+    fprintf (stderr, "loading client cert and key from files %s and %s\n",
+             CERT_FILE, KEY_FILE);
+#endif
+    err =
+        gnutls_certificate_set_x509_key_file (x509_cred,
+                                              CERT_FILE, KEY_FILE,
+                                              GNUTLS_X509_FMT_PEM);
+    if (err < 0) { gnutls_perror (err); exit (1); }
+
+    initialised = 1;
+    return 0;
+}
+
+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
+do_open (virConnectPtr conn, const char *uri_str,
+             int flags ATTRIBUTE_UNUSED)
+{
+    if (!uri_str) return VIR_DRV_OPEN_DECLINED;
+
+    /* 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 VIR_DRV_OPEN_DECLINED; /* Decline - not a URL. */
+
+    char *transport_str = get_transport_from_scheme (uri->scheme);
+    if (!uri->server && !transport_str)
+        return VIR_DRV_OPEN_DECLINED; /* 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 VIR_DRV_OPEN_ERROR;
+    }
+
+    /* Return code from this function, and the private data. */
+    int retcode = VIR_DRV_OPEN_ERROR;
+    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
+
+    if (initialise_gnutls () == -1) {
+        error (conn, VIR_ERR_INVALID_ARG,
+               "remote_open: GnuTLS initialisation failed");
+        goto failed;
+    }
+
+    /* 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. */
+        /* XXX This loop contains a subtle problem.  In the case
+         * where a host is accessible over IPv4 and IPv6, it will
+         * try the IPv4 and IPv6 addresses in turn.  However it
+         * should be able to present different client certificates
+         * (because the commonName field in a client cert contains
+         * the client IP address, which is different for IPv4 and
+         * IPv6).  At the moment we only have a single client
+         * certificate, and no way to specify what address family
+         * that certificate belongs to.
+         */
+        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,
+                                     x509_cred,
+                                     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);
+
+#define UNIX_PATH_MAX(addr) (sizeof (addr).sun_path)
+        struct sockaddr_un addr;
+        memset (&addr, 0, sizeof addr);
+        addr.sun_family = AF_UNIX;
+        strncpy (addr.sun_path, sockname, UNIX_PATH_MAX (addr));
+
+        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).
+    remote_open_ret *ret = remote_open_1 (&name, private.cl);
+    if (!ret) {
+        error (conn, VIR_ERR_RPC,
+               clnt_sperror (private.cl, "remote_open"));
+        clnt_destroy (private.cl);
+        goto failed;
+    }
+    if (ret->status == -1) {
+        server_error (conn, &ret->remote_open_ret_u.err);
+        goto failed;
+    }
+    clnt_freeres (private.cl, (xdrproc_t) xdr_remote_open_ret, (char *) ret);
+
+    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 = VIR_DRV_OPEN_SUCCESS;
+    /*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
+do_close (virConnectPtr conn)
+{
+    GET_PRIVATE (conn, -1);
+
+    void *nothing = 0;
+    remote_close_ret *ret = remote_close_1 (&nothing, private->cl);
+    if (!ret) {
+        error (conn, VIR_ERR_RPC,
+               clnt_sperror (private->cl, "remote_close"));
+    failed:
+        clnt_freeres (private->cl,
+                      (xdrproc_t) xdr_remote_close_ret, (char *) ret);
+        return -1;
+    }
+    if (ret->status == -1) {
+        server_error (conn, &ret->remote_close_ret_u.err);
+        goto failed;
+    }
+
+    clnt_freeres (private->cl, (xdrproc_t) xdr_remote_close_ret, (char *) ret);
+
+    // 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 0;
+}
+
+/* Remote_open and remote_close functions above are the complex ones.  The
+ * rest just shuffle arguments and pass them along to the remote libvirtd.
+ */
+
+static const char *
+do_type (virConnectPtr conn)
+{
+    GET_PRIVATE (conn, NULL);
+
+    void *nothing = 0;
+    remote_type_ret *ret = remote_type_1 (&nothing, private->cl);
+    if (!ret) {
+        error (conn, VIR_ERR_RPC,
+               clnt_sperror (private->cl, "remote_type"));
+    failed:
+        clnt_freeres (private->cl,
+                      (xdrproc_t) xdr_remote_type_ret, (char *) ret);
+        return NULL;
+    }
+    if (ret->status == -1) {
+        server_error (conn, &ret->remote_type_ret_u.err);
+        goto failed;
+    }
+
+    /* This function is supposed to return a statically allocated string.
+     * We get around this by saving strings.
+     * https://www.redhat.com/archives/libvir-list/2007-February/msg00096.html
+     */
+    static struct strings {
+        struct strings *next;
+        char *string;
+    } *strings = 0;
+    struct strings *p;
+    for (p = strings; p; p = p->next) {
+        if (strcmp (p->string, ret->remote_type_ret_u.type) == 0)
+            break;
+    }
+    if (!p) {
+        p = malloc (sizeof (struct strings));
+        char *s = strdup (ret->remote_type_ret_u.type);
+        if (!p || !s) {
+            error (conn, VIR_ERR_NO_MEMORY, "remote_type");
+            goto failed;
+        }
+        p->string = s;
+        p->next = strings;
+        strings = p;
+    }
+
+    clnt_freeres (private->cl,
+                  (xdrproc_t) xdr_remote_type_ret, (char *) ret);
+
+    return p->string;
+}
+
+static int
+do_version (virConnectPtr conn, unsigned long *hvVer)
+{
+    GET_PRIVATE (conn, -1);
+
+    void *nothing = 0;
+    remote_version_ret *ret = remote_version_1 (&nothing, private->cl);
+    if (!ret) {
+        error (conn, VIR_ERR_RPC,
+               clnt_sperror (private->cl, "remote_version"));
+    failed:
+        clnt_freeres (private->cl,
+                      (xdrproc_t) xdr_remote_version_ret, (char *) ret);
+        return -1;
+    }
+    if (ret->status == -1) {
+        server_error (conn, &ret->remote_version_ret_u.err);
+        goto failed;
+    }
+
+    *hvVer = ret->remote_version_ret_u.hvVer;
+
+    clnt_freeres (private->cl,
+                  (xdrproc_t) xdr_remote_version_ret, (char *) ret);
+    return 0;
+}
+
+static inline int
+min (int a, int b)
+{
+    if (a < b) return a; else return b;
+}
+
+static int
+do_nodeGetInfo (virConnectPtr conn, virNodeInfo *info)
+{
+    GET_PRIVATE (conn, -1);
+
+    void *nothing = 0;
+    remote_nodeGetInfo_ret *ret =
+        remote_nodegetinfo_1 (&nothing, private->cl);
+    if (!ret) {
+        error (conn, VIR_ERR_RPC,
+               clnt_sperror (private->cl, "remote_nodeGetInfo"));
+    failed:
+        clnt_freeres (private->cl,
+                      (xdrproc_t) xdr_remote_nodeGetInfo_ret, (char *) ret);
+        return -1;
+    }
+    if (ret->status == -1) {
+        server_error (conn, &ret->remote_nodeGetInfo_ret_u.err);
+        goto failed;
+    }
+
+    strncpy (info->model, ret->remote_nodeGetInfo_ret_u.info.model,
+             sizeof info->model);
+    info->memory = ret->remote_nodeGetInfo_ret_u.info.memory;
+    info->cpus = ret->remote_nodeGetInfo_ret_u.info.cpus;
+    info->mhz = ret->remote_nodeGetInfo_ret_u.info.mhz;
+    info->nodes = ret->remote_nodeGetInfo_ret_u.info.nodes;
+    info->sockets = ret->remote_nodeGetInfo_ret_u.info.sockets;
+    info->cores = ret->remote_nodeGetInfo_ret_u.info.cores;
+    info->threads = ret->remote_nodeGetInfo_ret_u.info.threads;
+
+    clnt_freeres (private->cl,
+                  (xdrproc_t) xdr_remote_nodeGetInfo_ret, (char *) ret);
+
+    return 0;
+}
+
+static int
+do_listDomains (virConnectPtr conn, int *ids, int maxids)
+{
+    GET_PRIVATE (conn, -1);
+
+    remote_listDomains_ret *ret = remote_listdomains_1 (&maxids, private->cl);
+    if (!ret) {
+        error (conn, VIR_ERR_RPC,
+               clnt_sperror (private->cl, "remote_listDomains"));
+    failed:
+        clnt_freeres (private->cl,
+                      (xdrproc_t) xdr_remote_listDomains_ret, (char *) ret);
+        return -1;
+    }
+    if (ret->status == -1) {
+        server_error (conn, &ret->remote_listDomains_ret_u.err);
+        goto failed;
+    }
+
+    int len = min (ret->remote_listDomains_ret_u.ids.ids_len, maxids);
+    int i;
+    for (i = 0; i < len; ++i)
+        ids[i] = ret->remote_listDomains_ret_u.ids.ids_val[i];
+
+    clnt_freeres (private->cl,
+                  (xdrproc_t) xdr_remote_listDomains_ret, (char *) ret);
+
+    return len;
+}
+
+static int
+do_numOfDomains (virConnectPtr conn)
+{
+    GET_PRIVATE (conn, -1);
+
+    void *nothing = 0;
+    remote_numOfDomains_ret *ret =
+        remote_numofdomains_1 (&nothing, private->cl);
+    if (!ret) {
+        error (conn, VIR_ERR_RPC,
+               clnt_sperror (private->cl, "remote_numOfDomains"));
+    failed:
+        clnt_freeres (private->cl,
+                      (xdrproc_t) xdr_remote_numOfDomains_ret, (char *) ret);
+        return -1;
+    }
+    if (ret->status == -1) {
+        server_error (conn, &ret->remote_numOfDomains_ret_u.err);
+        goto failed;
+    }
+
+    int nr_domains = ret->remote_numOfDomains_ret_u.nr_domains;
+
+    clnt_freeres (private->cl,
+                  (xdrproc_t) xdr_remote_numOfDomains_ret, (char *) ret);
+
+    return nr_domains;
+}
+
+static virDomainPtr
+do_domainCreateLinux (virConnectPtr conn,
+                      const char *xmlDesc, unsigned int flags)
+{
+    GET_PRIVATE (conn, NULL);
+
+    remote_domainCreateLinux_args args = {
+        .xmlDesc = (char *) xmlDesc,
+        .flags = flags
+    };
+    remote_domainCreateLinux_ret *ret =
+        remote_domaincreatelinux_1 (&args, private->cl);
+    if (!ret) {
+        error (conn, VIR_ERR_RPC,
+               clnt_sperror (private->cl, "remote_domainCreateLinux"));
+    failed:
+        clnt_freeres (private->cl,
+                      (xdrproc_t) xdr_remote_domainCreateLinux_ret,
+                      (char *) ret);
+        return NULL;
+    }
+    if (ret->status == -1) {
+        server_error (conn, &ret->remote_domainCreateLinux_ret_u.err);
+        goto failed;
+    }
+
+    virDomainPtr dom =
+        get_domain_from_name (conn,
+                              ret->remote_domainCreateLinux_ret_u.domain);
+
+    clnt_freeres (private->cl,
+                  (xdrproc_t) xdr_remote_domainCreateLinux_ret, (char *) ret);
+
+    return dom;
+}
+
+
+
+
+
+
+
+/* For errors internal to this library. */
+static void
+error (virConnectPtr conn, virErrorNumber code, const char *info)
+{
+    const char *errmsg;
+
+    // XXX I don't think this is correct use of virErrorMsg.
+    errmsg = __virErrorMsg (code, info);
+    __virRaiseError (conn, NULL, NULL, VIR_FROM_REMOTE,
+                     code, VIR_ERR_ERROR, errmsg, info, NULL, 0, 0,
+                     errmsg, info);
+}
+
+/* For errors generated on the server side and sent back to us. */
+static char *server_error_strdup (char *);
+
+static void
+server_error (virConnectPtr conn, remote_error *err)
+{
+    virDomainPtr dom = get_domain_from_name (conn, err->dom);
+    virNetworkPtr net = get_network_from_name (conn, err->net);
+
+    char *str1 = server_error_strdup (err->str1);
+    char *str2 = server_error_strdup (err->str2);
+    char *str3 = server_error_strdup (err->str3);
+
+    // XXX I don't think this is correct use of virErrorMsg.
+    const char *msg = __virErrorMsg (err->code, err->message);
+
+    __virRaiseError (conn, dom, net,
+                     err->domain, err->code, err->level,
+                     str1, str2, str3,
+                     err->int1, err->int2,
+                     msg);
+}
+
+static char *
+server_error_strdup (char *str)
+{
+    if (!str || strcmp (str, "") == 0) return NULL;
+
+    // Getting NULL back from strdup is OK since these strings
+    // are informational.
+    return strdup (str);
+}
+
+/* Domains and networks are represented on the wire by an
+ * optional (name, uuid) pair.  If omitted, it stands for NULL.
+ */
+static virDomainPtr
+get_domain_from_name (virConnectPtr conn, remote_domain domain)
+{
+    if (!conn || !domain) return NULL;
+    return virGetDomain (conn, domain->name, BAD_CAST domain->uuid);
+}
+
+static virNetworkPtr
+get_network_from_name (virConnectPtr conn, remote_network network)
+{
+    if (!conn || !network) return NULL;
+    return virGetNetwork (conn, network->name, BAD_CAST network->uuid);
+}
+
+static virDriver driver = {
+    .no = VIR_DRV_REMOTE,
+    .name = "remote",
+    .ver = LIBVIRTREMOTE_VERS1,
+    .open = do_open,
+    .close = do_close,
+    .type = do_type,
+    .version = do_version,
+    .nodeGetInfo = do_nodeGetInfo,
+    .listDomains = do_listDomains,
+    .numOfDomains = do_numOfDomains,
+    .domainCreateLinux = do_domainCreateLinux,
+#if 0
+    .domainLookupByID = do_domainLookupByID,
+    .domainLookupByUUID = do_domainLookupByUUID,
+    .domainLookupByName = do_domainLookupByName,
+    .domainSuspend = do_domainSuspend,
+    .domainResume = do_domainResume,
+    .domainShutdown = do_domainShutdown,
+    .domainReboot = do_domainReboot,
+    .domainDestroy = do_domainDestroy,
+    .domainGetOSType = do_domainGetOSType,
+    .domainGetMaxMemory = do_domainGetMaxMemory,
+    .domainSetMaxMemory = do_domainSetMaxMemory,
+    .domainSetMemory = do_domainSetMemory,
+    .domainGetInfo = do_domainGetInfo,
+    .domainSave = do_domainSave,
+    .domainRestore = do_domainRestore,
+    .domainCoreDump = do_domainCoreDump,
+    .domainSetVcpus = do_domainSetVcpus,
+    .domainPinVcpu = do_domainPinVcpu,
+    .domainGetVcpus = do_domainGetVcpus,
+    .domainDumpXML = do_domainDumpXML,
+    .listDefinedDomains = do_listDefinedDomains,
+    .numOfDefinedDomains = do_numOfDefinedDomains,
+    .domainCreate = do_domainCreate,
+    .domainDefineXML = do_domainDefineXML,
+    .domainUndefine = do_domainUndefine,
+    .domainAttachDevice = do_domainAttachDevice,
+    .domainDetachDevice = do_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 -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/remote_internal.h libvirt-remote/src/remote_internal.h
--- libvirt-cvs/src/remote_internal.h	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-remote/src/remote_internal.h	2007-02-23 15:59:36.000000000 +0000
@@ -0,0 +1,44 @@
+/*
+ * 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 (LOCAL_STATE_DIR "/run/libvirtd/socket")
+#define LIBVIRTD_CONFIGURATION_FILE (SYSCONFDIR "/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 -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/remote_rpc.x libvirt-remote/src/remote_rpc.x
--- libvirt-cvs/src/remote_rpc.x	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-remote/src/remote_rpc.x	2007-02-26 19:41:31.000000000 +0000
@@ -0,0 +1,180 @@
+/* -*- 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>
+ */
+
+/* XXX Get this from <libvirt.h>. */
+#define VIR_UUID_BUFLEN 16
+
+/* Domains and networks are represented on the wire by (name, UUID). */
+struct remote_name {
+    string name<>;
+    opaque uuid[VIR_UUID_BUFLEN];
+};
+typedef struct remote_name *remote_domain;
+typedef struct remote_name *remote_network;
+
+/* Error message. See <virterror.h> for explanation of fields. */
+
+/* NB. Fields "code", "domain" and "level" are really enums.  The
+ * numeric value should remain compatible between libvirt and
+ * libvirtd.  This means, no changing or reordering the enums as
+ * defined in <virterror.h>.
+ */
+struct remote_error {
+    int code;
+    int domain;
+    string message<>;
+    int level;
+    remote_domain dom;
+    string str1<>;
+    string str2<>;
+    string str3<>;
+    int int1;
+    int int2;
+    remote_network net;
+};
+
+/* virNodeInfo equivalent. */
+struct remote_node_info {
+    string model<32>;
+    hyper memory;
+    int cpus;
+    int mhz;
+    int nodes;
+    int sockets;
+    int cores;
+    int threads;
+};
+
+/* Return values include a status and either the returned object or
+ * an error message.
+ */
+union remote_open_ret switch (int status) {
+ case 0:
+     void;
+ default:
+     struct remote_error err;
+};
+
+union remote_close_ret switch (int status) {
+ case 0:
+     void;
+ default:
+     struct remote_error err;
+};
+
+union remote_type_ret switch (int status) {
+ case 0:
+     string type<>;
+ default:
+     struct remote_error err;
+};
+
+union remote_version_ret switch (int status) {
+ case 0:
+     hyper hvVer;
+ default:
+     struct remote_error err;
+};
+
+union remote_nodeGetInfo_ret switch (int status) {
+ case 0:
+     struct remote_node_info info;
+ default:
+     struct remote_error err;
+};
+
+union remote_listDomains_ret switch (int status) {
+ case 0:
+     int ids<>;
+ default:
+     struct remote_error err;
+};
+
+union remote_numOfDomains_ret switch (int status) {
+ case 0:
+     int nr_domains;
+ default:
+     struct remote_error err;
+};
+
+struct remote_domainCreateLinux_args {
+    string xmlDesc<>;
+    int flags;
+};
+
+union remote_domainCreateLinux_ret switch (int status) {
+ case 0:
+     remote_domain domain;
+ default:
+     struct remote_error err;
+};
+
+program LIBVIRTREMOTE {
+    version LIBVIRTREMOTE_VERS1 {
+        remote_open_ret remote_open (string)                        = 1;
+        remote_close_ret remote_close (void)                        = 2;
+        remote_type_ret remote_type (void)                          = 3;
+        remote_version_ret remote_version (void)                    = 4;
+        remote_nodeGetInfo_ret remote_nodeGetInfo (void)            = 5;
+        remote_listDomains_ret remote_listDomains (int)             = 6;
+        remote_numOfDomains_ret remote_numOfDomains (void)          = 7;
+        remote_domainCreateLinux_ret remote_domainCreateLinux
+            (struct remote_domainCreateLinux_args)                  = 8;
+#if 0
+        remote_domainLookupByID_ret remote_domainLookupByID ()      = 9;
+        remote_domainLookupByUUID_ret remote_domainLookupByUUID ()  = 10;
+        remote_domainLookupByName_ret remote_domainLookupByName ()  = 11;
+        remote_domainSuspend_ret remote_domainSuspend ()            = 12;
+        remote_domainResume_ret remote_domainResume ()              = 13;
+        remote_domainShutdown_ret remote_domainShutdown ()          = 14;
+        remote_domainReboot_ret remote_domainReboot ()              = 15;
+        remote_domainDestroy_ret remote_domainDestroy ()            = 16;
+        remote_domainGetOSType_ret remote_domainGetOSType ()        = 17;
+        remote_domainGetMaxMemory_ret remote_domainGetMaxMemory ()  = 18;
+        remote_domainSetMaxMemory_ret remote_domainSetMaxMemory ()  = 19;
+        remote_domainSetMemory_ret remote_domainSetMemory ()        = 20;
+        remote_domainGetInfo_ret remote_domainGetInfo ()            = 21;
+        remote_domainSave_ret remote_domainSave ()                  = 22;
+        remote_domainRestore_ret remote_domainRestore ()            = 23;
+        remote_domainCoreDump_ret remote_domainCoreDump ()          = 24;
+        remote_domainSetVcpus_ret remote_domainSetVcpus ()          = 25;
+        remote_domainPinVcpu_ret remote_domainPinVcpu ()            = 26;
+        remote_domainGetVcpus_ret remote_domainGetVcpus ()          = 27;
+        remote_domainDumpXML_ret remote_domainDumpXML ()            = 28;
+        remote_listDefinedDomains_ret remote_listDefinedDomains ()  = 29;
+        remote_numOfDefinedDomains_ret remote_numOfDefinedDomains () = 30;
+        remote_domainCreate_ret remote_domainCreate ()              = 31;
+        remote_domainDefineXML_ret remote_domainDefineXML ()        = 32;
+        remote_domainUndefine_ret remote_domainUndefine ()          = 33;
+        remote_domainAttachDevice_ret remote_domainAttachDevice ()  = 34;
+        remote_domainDetachDevice_ret remote_domainDetachDevice ()  = 35;
+#endif
+    } = 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 -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/sunrpc/clnt_ext.c libvirt-remote/src/sunrpc/clnt_ext.c
--- libvirt-cvs/src/sunrpc/clnt_ext.c	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-remote/src/sunrpc/clnt_ext.c	2007-02-23 15:59:36.000000000 +0000
@@ -0,0 +1,645 @@
+/*
+ * clnt_ext.c: SunRPC over an external program.  This is a modified version
+ * of clnt_unix.c from glibc which is written to run over a forked
+ * external program.
+ *
+ * Note that there is no corresponding svc_ext.c.  It is expected that
+ * this client will talk to a remote Unix domain socket (ie. svc_unix.c).
+ *
+ * Modifications from glibc-2.5 base by Richard Jones <rjones redhat com>.
+ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+/*
+ * clnt_unix.c, Implements a TCP/IP based, client side RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * TCP based RPC supports 'batched calls'.
+ * A sequence of calls may be batched-up in a send buffer.  The rpc call
+ * return immediately to the client even though the call was not necessarily
+ * sent.  The batching occurs if the results' xdr routine is NULL (0) AND
+ * the rpc timeout value is zero (see clnt.h, rpc).
+ *
+ * Clients should NOT casually batch calls that in fact return results; that is,
+ * the server side should be aware that a call is batched and not produce any
+ * return message.  Batched calls that produce many result messages can
+ * deadlock (netlock) the client and the server....
+ *
+ * Now go hang yourself.
+ */
+
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <rpc/rpc.h>
+#include <sys/uio.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <rpc/pmap_clnt.h>
+#ifdef USE_IN_LIBIO
+# include <wchar.h>
+#endif
+
+#include "clnt_ext.h"
+
+extern u_long _create_xid (void);
+
+#define MCALL_MSG_SIZE 24
+
+struct ct_data
+  {
+    int ct_sock;
+    bool_t ct_closeit;
+    struct timeval ct_wait;
+    bool_t ct_waitset;		/* wait set by clnt_control? */
+    struct rpc_err ct_error;
+    char ct_mcall[MCALL_MSG_SIZE];	/* marshalled callmsg */
+    u_int ct_mpos;		/* pos after marshal */
+    XDR ct_xdrs;
+    int ct_pid;			/* Child PID. */
+  };
+
+static int readunix (char *, char *, int);
+static int writeunix (char *, char *, int);
+
+static enum clnt_stat clntunix_call (CLIENT *, u_long, xdrproc_t, caddr_t,
+				    xdrproc_t, caddr_t, struct timeval);
+static void clntunix_abort (void);
+static void clntunix_geterr (CLIENT *, struct rpc_err *);
+static bool_t clntunix_freeres (CLIENT *, xdrproc_t, caddr_t);
+static bool_t clntunix_control (CLIENT *, int, char *);
+static void clntunix_destroy (CLIENT *);
+
+static const struct clnt_ops unix_ops =
+{
+  clntunix_call,
+  clntunix_abort,
+  clntunix_geterr,
+  clntunix_freeres,
+  clntunix_destroy,
+  clntunix_control
+};
+
+CLIENT *
+clntext_create (char *filename, char *argv[],
+		u_long prog, u_long vers,
+		int *sockp, u_int sendsz, u_int recvsz)
+{
+  CLIENT *h;
+  struct ct_data *ct = (struct ct_data *) mem_alloc (sizeof (*ct));
+  struct rpc_msg call_msg;
+  int len, sv[2];
+
+  h = (CLIENT *) mem_alloc (sizeof (*h));
+  if (h == NULL || ct == NULL)
+    {
+      struct rpc_createerr *ce = &get_rpc_createerr ();
+      (void) fprintf (stderr, "clntext_create: out of memory\n");
+      ce->cf_stat = RPC_SYSTEMERROR;
+      ce->cf_error.re_errno = ENOMEM;
+      goto fooy;
+    }
+
+  /* Fork off the external process.  Use socketpair to create a private
+   * (unnamed) Unix domain socket to the child process so we don't have
+   * to faff around with two file descriptors (a la 'pipe(2)').
+   */
+  if (*sockp < 0)
+    {
+      if (socketpair (PF_UNIX, SOCK_STREAM, 0, sv) == -1)
+	{
+	  struct rpc_createerr *ce = &get_rpc_createerr ();
+	  (void) fprintf (stderr, "clntext_create: socketpair\n");
+	  ce->cf_stat = RPC_SYSTEMERROR;
+	  ce->cf_error.re_errno = errno;
+	  goto fooy;
+	}
+
+      ct->ct_pid = fork ();
+      if (ct->ct_pid == -1) {
+	  struct rpc_createerr *ce = &get_rpc_createerr ();
+	  (void) fprintf (stderr, "clntext_create: fork\n");
+	  ce->cf_stat = RPC_SYSTEMERROR;
+	  ce->cf_error.re_errno = errno;
+	  goto fooy;
+      } else if (ct->ct_pid == 0) { /* Child. */
+	close (sv[0]);
+	// Connect socket (sv[1]) to stdin/stdout.
+	close (0);
+	dup (sv[1]);
+	close (1);
+	dup (sv[1]);
+	close (sv[1]);
+
+	// Run the external process.
+	
+	if (!argv) {
+	  argv = malloc (2 * sizeof (char *));
+	  argv[0] = filename;
+	  argv[1] = 0;
+	}
+	execvp (filename, argv);
+	perror (filename);
+	_exit (1);
+      }
+
+      /* Parent continues here. */
+      close (sv[1]);
+      *sockp = sv[0];
+      ct->ct_closeit = TRUE;
+    }
+  else
+    {
+      ct->ct_closeit = FALSE;
+    }
+
+  /*
+   * Set up private data struct
+   */
+  ct->ct_sock = *sockp;
+  ct->ct_wait.tv_usec = 0;
+  ct->ct_waitset = FALSE;
+
+  /*
+   * Initialize call message
+   */
+  call_msg.rm_xid = _create_xid ();
+  call_msg.rm_direction = CALL;
+  call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
+  call_msg.rm_call.cb_prog = prog;
+  call_msg.rm_call.cb_vers = vers;
+
+  /*
+   * pre-serialize the static part of the call msg and stash it away
+   */
+  xdrmem_create (&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE,
+			 XDR_ENCODE);
+  if (!xdr_callhdr (&(ct->ct_xdrs), &call_msg))
+    {
+      if (ct->ct_closeit)
+	close (*sockp);
+      goto fooy;
+    }
+  ct->ct_mpos = XDR_GETPOS (&(ct->ct_xdrs));
+  XDR_DESTROY (&(ct->ct_xdrs));
+
+  /*
+   * Create a client handle which uses xdrrec for serialization
+   * and authnone for authentication.
+   */
+  xdrrec_create (&(ct->ct_xdrs), sendsz, recvsz,
+			 (caddr_t) ct, readunix, writeunix);
+  h->cl_ops = (struct clnt_ops *) &unix_ops;
+  h->cl_private = (caddr_t) ct;
+  h->cl_auth = authnone_create ();
+  return h;
+
+fooy:
+  /*
+   * Something goofed, free stuff and barf
+   */
+  mem_free ((caddr_t) ct, sizeof (struct ct_data));
+  mem_free ((caddr_t) h, sizeof (CLIENT));
+  return (CLIENT *) NULL;
+}
+
+static enum clnt_stat
+clntunix_call (h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
+     CLIENT *h;
+     u_long proc;
+     xdrproc_t xdr_args;
+     caddr_t args_ptr;
+     xdrproc_t xdr_results;
+     caddr_t results_ptr;
+     struct timeval timeout;
+{
+  struct ct_data *ct = (struct ct_data *) h->cl_private;
+  XDR *xdrs = &(ct->ct_xdrs);
+  struct rpc_msg reply_msg;
+  u_long x_id;
+  u_int32_t *msg_x_id = (u_int32_t *) (ct->ct_mcall);	/* yuk */
+  bool_t shipnow;
+  int refreshes = 2;
+
+  if (!ct->ct_waitset)
+    {
+      ct->ct_wait = timeout;
+    }
+
+  shipnow =
+    (xdr_results == (xdrproc_t) 0 && ct->ct_wait.tv_sec == 0
+     && ct->ct_wait.tv_usec == 0) ? FALSE : TRUE;
+
+call_again:
+  xdrs->x_op = XDR_ENCODE;
+  ct->ct_error.re_status = RPC_SUCCESS;
+  x_id = ntohl (--(*msg_x_id));
+  if ((!XDR_PUTBYTES (xdrs, ct->ct_mcall, ct->ct_mpos)) ||
+      (!XDR_PUTLONG (xdrs, (long *) &proc)) ||
+      (!AUTH_MARSHALL (h->cl_auth, xdrs)) ||
+      (!(*xdr_args) (xdrs, args_ptr)))
+    {
+      if (ct->ct_error.re_status == RPC_SUCCESS)
+	ct->ct_error.re_status = RPC_CANTENCODEARGS;
+      (void) xdrrec_endofrecord (xdrs, TRUE);
+      return ct->ct_error.re_status;
+    }
+  if (!xdrrec_endofrecord (xdrs, shipnow))
+    return ct->ct_error.re_status = RPC_CANTSEND;
+  if (!shipnow)
+    return RPC_SUCCESS;
+  /*
+   * Hack to provide rpc-based message passing
+   */
+  if (ct->ct_wait.tv_sec == 0 && ct->ct_wait.tv_usec == 0)
+    return ct->ct_error.re_status = RPC_TIMEDOUT;
+
+
+  /*
+   * Keep receiving until we get a valid transaction id
+   */
+  xdrs->x_op = XDR_DECODE;
+  while (TRUE)
+    {
+      reply_msg.acpted_rply.ar_verf = _null_auth;
+      reply_msg.acpted_rply.ar_results.where = NULL;
+      reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
+      if (!xdrrec_skiprecord (xdrs))
+	return ct->ct_error.re_status;
+      /* now decode and validate the response header */
+      if (!xdr_replymsg (xdrs, &reply_msg))
+	{
+	  if (ct->ct_error.re_status == RPC_SUCCESS)
+	    continue;
+	  return ct->ct_error.re_status;
+	}
+      if (reply_msg.rm_xid == x_id)
+	break;
+    }
+
+  /*
+   * process header
+   */
+  _seterr_reply (&reply_msg, &(ct->ct_error));
+  if (ct->ct_error.re_status == RPC_SUCCESS)
+    {
+      if (!AUTH_VALIDATE (h->cl_auth, &reply_msg.acpted_rply.ar_verf))
+	{
+	  ct->ct_error.re_status = RPC_AUTHERROR;
+	  ct->ct_error.re_why = AUTH_INVALIDRESP;
+	}
+      else if (!(*xdr_results) (xdrs, results_ptr))
+	{
+	  if (ct->ct_error.re_status == RPC_SUCCESS)
+	    ct->ct_error.re_status = RPC_CANTDECODERES;
+	}
+      /* free verifier ... */
+      if (reply_msg.acpted_rply.ar_verf.oa_base != NULL)
+	{
+	  xdrs->x_op = XDR_FREE;
+	  (void) xdr_opaque_auth (xdrs,
+					  &(reply_msg.acpted_rply.ar_verf));
+	}
+    }				/* end successful completion */
+  else
+    {
+      /* maybe our credentials need to be refreshed ... */
+      if (refreshes-- && AUTH_REFRESH (h->cl_auth))
+	goto call_again;
+    }				/* end of unsuccessful completion */
+  return ct->ct_error.re_status;
+}
+
+static void
+clntunix_geterr (CLIENT *h, struct rpc_err *errp)
+{
+  struct ct_data *ct = (struct ct_data *) h->cl_private;
+
+  *errp = ct->ct_error;
+}
+
+static bool_t
+clntunix_freeres (cl, xdr_res, res_ptr)
+     CLIENT *cl;
+     xdrproc_t xdr_res;
+     caddr_t res_ptr;
+{
+  struct ct_data *ct = (struct ct_data *) cl->cl_private;
+  XDR *xdrs = &(ct->ct_xdrs);
+
+  xdrs->x_op = XDR_FREE;
+  return (*xdr_res) (xdrs, res_ptr);
+}
+
+static void
+clntunix_abort ()
+{
+}
+
+static bool_t
+clntunix_control (CLIENT *cl, int request, char *info)
+{
+  struct ct_data *ct = (struct ct_data *) cl->cl_private;
+
+
+  switch (request)
+    {
+    case CLSET_FD_CLOSE:
+      ct->ct_closeit = TRUE;
+      break;
+    case CLSET_FD_NCLOSE:
+      ct->ct_closeit = FALSE;
+      break;
+    case CLSET_TIMEOUT:
+      ct->ct_wait = *(struct timeval *) info;
+      break;
+    case CLGET_TIMEOUT:
+      *(struct timeval *) info = ct->ct_wait;
+      break;
+    case CLGET_FD:
+      *(int *)info = ct->ct_sock;
+      break;
+    case CLGET_XID:
+      /*
+       * use the knowledge that xid is the
+       * first element in the call structure *.
+       * This will get the xid of the PREVIOUS call
+       */
+      *(u_long *) info = ntohl (*(u_long *)ct->ct_mcall);
+      break;
+    case CLSET_XID:
+      /* This will set the xid of the NEXT call */
+      *(u_long *) ct->ct_mcall =  htonl (*(u_long *)info - 1);
+      /* decrement by 1 as clntunix_call() increments once */
+    case CLGET_VERS:
+      /*
+       * This RELIES on the information that, in the call body,
+       * the version number field is the fifth field from the
+       * begining of the RPC header. MUST be changed if the
+       * call_struct is changed
+       */
+      *(u_long *) info = ntohl (*(u_long *) (ct->ct_mcall
+					     + 4 * BYTES_PER_XDR_UNIT));
+      break;
+    case CLSET_VERS:
+      *(u_long *) (ct->ct_mcall + 4 * BYTES_PER_XDR_UNIT)
+	= htonl (*(u_long *) info);
+      break;
+    case CLGET_PROG:
+      /*
+       * This RELIES on the information that, in the call body,
+       * the program number field is the  field from the
+       * begining of the RPC header. MUST be changed if the
+       * call_struct is changed
+       */
+      *(u_long *) info = ntohl (*(u_long *) (ct->ct_mcall
+					     + 3 * BYTES_PER_XDR_UNIT));
+      break;
+    case CLSET_PROG:
+      *(u_long *) (ct->ct_mcall + 3 * BYTES_PER_XDR_UNIT)
+	= htonl(*(u_long *) info);
+      break;
+    /* The following are only possible with TI-RPC */
+    case CLGET_RETRY_TIMEOUT:
+    case CLSET_RETRY_TIMEOUT:
+    case CLGET_SVC_ADDR:
+    case CLSET_SVC_ADDR:
+    case CLSET_PUSH_TIMOD:
+    case CLSET_POP_TIMOD:
+    default:
+      return FALSE;
+    }
+  return TRUE;
+}
+
+
+static void
+clntunix_destroy (CLIENT *h)
+{
+  struct ct_data *ct =
+  (struct ct_data *) h->cl_private;
+
+  if (ct->ct_closeit)
+    {
+      (void) close (ct->ct_sock);
+    }
+  XDR_DESTROY (&(ct->ct_xdrs));
+  mem_free ((caddr_t) ct, sizeof (struct ct_data));
+  mem_free ((caddr_t) h, sizeof (CLIENT));
+
+  // Wait for child to finish.  Print any errors but don't stop.
+  int status;
+  if (waitpid (ct->ct_pid, &status, 0) == -1) {
+    perror ("waitpid");
+    return;
+  }
+  if (WIFEXITED (status)) {
+    int s = WEXITSTATUS (status);
+    if (s != 0)
+      fprintf (stderr, "clntext_destroy: warning: external command exited with non-zero exit status %d\n", s);
+  } else if (WIFSIGNALED (status)) {
+    int s = WTERMSIG (status);
+    fprintf (stderr, "clntext_destroy: warning: external command died on signal %d\n", s);
+  } else if (WIFSTOPPED (status)) {
+    int s = WSTOPSIG (status);
+    fprintf (stderr, "clntext_destroy: warning: external command stopped on signal %d\n", s);
+  }
+}
+
+static int
+__msgread (int sock, void *data, size_t cnt)
+{
+  struct iovec iov;
+  struct msghdr msg;
+#ifdef SCM_CREDENTIALS
+  static char cm[CMSG_SPACE(sizeof (struct ucred))];
+#endif
+  int len;
+
+  iov.iov_base = data;
+  iov.iov_len = cnt;
+
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  msg.msg_name = NULL;
+  msg.msg_namelen = 0;
+#ifdef SCM_CREDENTIALS
+  msg.msg_control = (caddr_t) &cm;
+  msg.msg_controllen = CMSG_SPACE(sizeof (struct ucred));
+#endif
+  msg.msg_flags = 0;
+
+#ifdef SO_PASSCRED
+  {
+    int on = 1;
+    if (setsockopt (sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof (on)))
+      return -1;
+  }
+#endif
+
+ restart:
+  len = recvmsg (sock, &msg, 0);
+  if (len >= 0)
+    {
+      if (msg.msg_flags & MSG_CTRUNC || len == 0)
+	return 0;
+      else
+	return len;
+    }
+  if (errno == EINTR)
+    goto restart;
+  return -1;
+}
+
+static int
+__msgwrite (int sock, void *data, size_t cnt)
+{
+#ifndef SCM_CREDENTIALS
+  /* We cannot implement this reliably.  */
+  __set_errno (ENOSYS);
+  return -1;
+#else
+  struct iovec iov;
+  struct msghdr msg;
+  struct cmsghdr *cmsg = alloca (CMSG_SPACE(sizeof (struct ucred)));
+  struct ucred cred;
+  int len;
+
+  /* XXX I'm not sure, if gete?id() is always correct, or if we should use
+     get?id(). But since keyserv needs geteuid(), we have no other chance.
+     It would be much better, if the kernel could pass both to the server. */
+  cred.pid = getpid ();
+  cred.uid = geteuid ();
+  cred.gid = getegid ();
+
+  memcpy (CMSG_DATA(cmsg), &cred, sizeof (struct ucred));
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_CREDENTIALS;
+  cmsg->cmsg_len = sizeof(*cmsg) + sizeof(struct ucred);
+
+  iov.iov_base = data;
+  iov.iov_len = cnt;
+
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  msg.msg_name = NULL;
+  msg.msg_namelen = 0;
+  msg.msg_control = cmsg;
+  msg.msg_controllen = CMSG_ALIGN(cmsg->cmsg_len);
+  msg.msg_flags = 0;
+
+ restart:
+  len = sendmsg (sock, &msg, 0);
+  if (len >= 0)
+    return len;
+  if (errno == EINTR)
+    goto restart;
+  return -1;
+
+#endif
+}
+
+
+/*
+ * Interface between xdr serializer and unix connection.
+ * Behaves like the system calls, read & write, but keeps some error state
+ * around for the rpc level.
+ */
+static int
+readunix (char *ctptr, char *buf, int len)
+{
+  struct ct_data *ct = (struct ct_data *) ctptr;
+  struct pollfd fd;
+  int milliseconds = ((ct->ct_wait.tv_sec * 1000)
+		      + (ct->ct_wait.tv_usec / 1000));
+
+  if (len == 0)
+    return 0;
+
+  fd.fd = ct->ct_sock;
+  fd.events = POLLIN;
+  while (TRUE)
+    {
+      switch (poll (&fd, 1, milliseconds))
+        {
+        case 0:
+          ct->ct_error.re_status = RPC_TIMEDOUT;
+          return -1;
+
+        case -1:
+          if (errno == EINTR)
+            continue;
+          ct->ct_error.re_status = RPC_CANTRECV;
+          ct->ct_error.re_errno = errno;
+          return -1;
+        }
+      break;
+    }
+  switch (len = __msgread (ct->ct_sock, buf, len))
+    {
+
+    case 0:
+      /* premature eof */
+      ct->ct_error.re_errno = ECONNRESET;
+      ct->ct_error.re_status = RPC_CANTRECV;
+      len = -1;			/* it's really an error */
+      break;
+
+    case -1:
+      ct->ct_error.re_errno = errno;
+      ct->ct_error.re_status = RPC_CANTRECV;
+      break;
+    }
+  return len;
+}
+
+static int
+writeunix (char *ctptr, char *buf, int len)
+{
+  int i, cnt;
+  struct ct_data *ct = (struct ct_data *) ctptr;
+
+  for (cnt = len; cnt > 0; cnt -= i, buf += i)
+    {
+      if ((i = __msgwrite (ct->ct_sock, buf, cnt)) == -1)
+	{
+	  ct->ct_error.re_errno = errno;
+	  ct->ct_error.re_status = RPC_CANTSEND;
+	  return -1;
+	}
+    }
+  return len;
+}
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/sunrpc/clnt_ext.h libvirt-remote/src/sunrpc/clnt_ext.h
--- libvirt-cvs/src/sunrpc/clnt_ext.h	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-remote/src/sunrpc/clnt_ext.h	2007-02-23 15:59:36.000000000 +0000
@@ -0,0 +1,18 @@
+/*
+ * clnt_ext.h: Interface to the SunRPC over external program.
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * See COPYING.LIB for the license of this software.
+ *
+ * Richard Jones <rjones redhat com>
+ */
+
+#ifndef __CLNT_EXT_H__
+#define __CLNT_EXT_H__
+
+extern CLIENT *clntext_create (char *filename, char *argv[],
+			       u_long prog, u_long vers,
+			       int *sockp, u_int sendsz, u_int recvsz);
+
+#endif /* __CLNT_EXT_H__ */
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/sunrpc/clnt_gnutls.c libvirt-remote/src/sunrpc/clnt_gnutls.c
--- libvirt-cvs/src/sunrpc/clnt_gnutls.c	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-remote/src/sunrpc/clnt_gnutls.c	2007-02-28 16:54:00.000000000 +0000
@@ -0,0 +1,718 @@
+/*
+ * 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
+  };
+  int err;
+
+  /* Initialize TLS session 
+   */
+  err = gnutls_init (&ct->ct_session, GNUTLS_CLIENT);
+  if (err) { gnutls_perror (err); return -1; }
+
+  /* Use default priorities */
+  err = gnutls_set_default_priority (ct->ct_session);
+  if (err) { gnutls_perror (err); return -1; }
+  err =
+    gnutls_certificate_type_set_priority (ct->ct_session, cert_type_priority);
+  if (err) { gnutls_perror (err); return -1; }
+
+  /* put the x509 credentials to the current session
+   */
+  err = gnutls_credentials_set (ct->ct_session, GNUTLS_CRD_CERTIFICATE, xcred);
+  if (err) { gnutls_perror (err); return -1; }
+
+  gnutls_transport_set_ptr (ct->ct_session,
+			    (gnutls_transport_ptr_t) (long) sock);
+
+  /* Perform the TLS handshake. */
+ again:
+  err = gnutls_handshake (ct->ct_session);
+  if (err < 0)
+    {
+      if (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED) goto again;
+      fprintf (stderr, "clnt_gnutls: TLS handshake failed\n");
+      gnutls_perror (err);
+      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;
+    }
+
+  /* At this point, the server is verifying _our_ certificate, IP address,
+   * etc.  If we make the grade, it will send us a '\1' byte.
+   */
+  char buf[1];
+  int len;
+ again_2:
+  len = gnutls_record_recv (ct->ct_session, buf, 1);
+  if (len < 0 && len != GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
+    {
+      if (len == GNUTLS_E_AGAIN || len == GNUTLS_E_INTERRUPTED) goto again_2;
+      gnutls_perror (len);
+      return -1;
+    }
+  if (len != 1 || buf[0] != '\1')
+    {
+      fprintf (stderr, "clnt_gnutls: server verification (of our certificate or IP address) failed\n");
+      return -1;
+    }
+
+#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;
+  int err;
+
+ again:
+  if ((err = gnutls_record_send (ct->ct_session, buf, len)) < 0)
+    {
+      if (err == GNUTLS_E_INTERRUPTED || err == GNUTLS_E_AGAIN) goto again;
+      /* XXX Assume here that the underlying errno is preserved. */
+      ct->ct_error.re_errno = errno;
+      ct->ct_error.re_status = RPC_CANTSEND;
+      return -1;
+    }
+
+  return len;
+}
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/sunrpc/clnt_gnutls.h libvirt-remote/src/sunrpc/clnt_gnutls.h
--- libvirt-cvs/src/sunrpc/clnt_gnutls.h	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-remote/src/sunrpc/clnt_gnutls.h	2007-02-23 15:59:36.000000000 +0000
@@ -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 -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/sunrpc/clnt_tcp2.c libvirt-remote/src/sunrpc/clnt_tcp2.c
--- libvirt-cvs/src/sunrpc/clnt_tcp2.c	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-remote/src/sunrpc/clnt_tcp2.c	2007-02-23 15:59:36.000000000 +0000
@@ -0,0 +1,544 @@
+/*
+ * clnt_tcp2.c: SunRPC TCP client.  This is a very slightly modified
+ * version of clnt_tcp.c from glibc which removes some of the IPv4
+ * assumptions, so this version can be used over IPv4 or IPv6 sockets.
+ *
+ * Modifications from glibc-2.5 base by Richard Jones <rjones redhat com>.
+ */
+
+/* @(#)clnt_tcp.c	2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * TCP based RPC supports 'batched calls'.
+ * A sequence of calls may be batched-up in a send buffer.  The rpc call
+ * return immediately to the client even though the call was not necessarily
+ * sent.  The batching occurs if the results' xdr routine is NULL (0) AND
+ * the rpc timeout value is zero (see clnt.h, rpc).
+ *
+ * Clients should NOT casually batch calls that in fact return results; that is,
+ * the server side should be aware that a call is batched and not produce any
+ * return message.  Batched calls that produce many result messages can
+ * deadlock (netlock) the client and the server....
+ *
+ * Now go hang yourself.
+ */
+
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <rpc/rpc.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <rpc/pmap_clnt.h>
+#ifdef USE_IN_LIBIO
+# include <wchar.h>
+#endif
+
+#include "clnt_tcp2.h"
+
+extern u_long _create_xid (void);
+
+#define MCALL_MSG_SIZE 24
+
+struct ct_data
+  {
+    int ct_sock;
+    bool_t ct_closeit;
+    struct timeval ct_wait;
+    bool_t ct_waitset;		/* wait set by clnt_control? */
+    struct rpc_err ct_error;
+    char ct_mcall[MCALL_MSG_SIZE];	/* marshalled callmsg */
+    u_int ct_mpos;		/* pos after marshal */
+    XDR ct_xdrs;
+  };
+
+static int readtcp (char *, char *, int);
+static int writetcp (char *, char *, int);
+
+static enum clnt_stat clnttcp_call (CLIENT *, u_long, xdrproc_t, caddr_t,
+				    xdrproc_t, caddr_t, struct timeval);
+static void clnttcp_abort (void);
+static void clnttcp_geterr (CLIENT *, struct rpc_err *);
+static bool_t clnttcp_freeres (CLIENT *, xdrproc_t, caddr_t);
+static bool_t clnttcp_control (CLIENT *, int, char *);
+static void clnttcp_destroy (CLIENT *);
+
+static const struct clnt_ops tcp_ops =
+{
+  clnttcp_call,
+  clnttcp_abort,
+  clnttcp_geterr,
+  clnttcp_freeres,
+  clnttcp_destroy,
+  clnttcp_control
+};
+
+/*
+ * Create a client handle for a tcp/ip connection.
+ * If *sockp<0, *sockp is set to a newly created TCP socket and it is
+ * connected to raddr.  If *sockp non-negative then
+ * raddr is ignored.  The rpc/tcp package does buffering
+ * similar to stdio, so the client must pick send and receive buffer sizes,];
+ * 0 => use the default.
+ * If raddr->sin_port is 0, then a binder on the remote machine is
+ * consulted for the right port number.
+ * NB: *sockp is copied into a private area.
+ * NB: It is the clients responsibility to close *sockp.
+ * NB: The rpch->cl_auth is set null authentication.  Caller may wish to set this
+ * something more useful.
+ */
+CLIENT *
+clnttcp2_create (int af, struct sockaddr *raddr, int raddr_size,
+		 u_long prog, u_long vers,
+		 int *sockp, u_int sendsz, u_int recvsz)
+{
+  CLIENT *h;
+  struct ct_data *ct;
+  struct rpc_msg call_msg;
+
+  h = (CLIENT *) mem_alloc (sizeof (*h));
+  ct = (struct ct_data *) mem_alloc (sizeof (*ct));
+  if (h == NULL || ct == NULL)
+    {
+      struct rpc_createerr *ce = &get_rpc_createerr ();
+      (void) fprintf (stderr, "clnttcp_create: out of memory\n");
+      ce->cf_stat = RPC_SYSTEMERROR;
+      ce->cf_error.re_errno = ENOMEM;
+      goto fooy;
+    }
+
+#if 0
+  // RWMJ: Note this will never be supported for portmap because the
+  // portmap protocol depends pretty fundamentally on IPv4.  Don't
+  /// use portmapper anyway -- it's silly.
+  /*
+   * If no port number given ask the pmap for one
+   */
+  if (port == 0)
+    {
+      u_short port;
+      if ((port = pmap_getport (raddr, prog, vers, IPPROTO_TCP)) == 0)
+	{
+	  mem_free ((caddr_t) ct, sizeof (struct ct_data));
+	  mem_free ((caddr_t) h, sizeof (CLIENT));
+	  return ((CLIENT *) NULL);
+	}
+      raddr->sin_port = htons (port);
+    }
+#endif
+
+  /*
+   * If no socket given, open one
+   */
+  if (*sockp < 0)
+    {
+      *sockp = socket (af, SOCK_STREAM, 0);
+      // RWMJ: Why?
+      //(void) bindresvport (*sockp, (struct sockaddr_in *) 0);
+      if ((*sockp < 0)
+	  || (connect (*sockp, raddr, raddr_size) < 0))
+	{
+	  struct rpc_createerr *ce = &get_rpc_createerr ();
+	  ce->cf_stat = RPC_SYSTEMERROR;
+	  ce->cf_error.re_errno = errno;
+	  if (*sockp >= 0)
+	    (void) close (*sockp);
+	  goto fooy;
+	}
+      ct->ct_closeit = TRUE;
+    }
+  else
+    {
+      ct->ct_closeit = FALSE;
+    }
+
+  /*
+   * Set up private data struct
+   */
+  ct->ct_sock = *sockp;
+  ct->ct_wait.tv_usec = 0;
+  ct->ct_waitset = FALSE;
+
+  /*
+   * Initialize call message
+   */
+  call_msg.rm_xid = _create_xid ();
+  call_msg.rm_direction = CALL;
+  call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
+  call_msg.rm_call.cb_prog = prog;
+  call_msg.rm_call.cb_vers = vers;
+
+  /*
+   * pre-serialize the static part of the call msg and stash it away
+   */
+  xdrmem_create (&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE,
+			 XDR_ENCODE);
+  if (!xdr_callhdr (&(ct->ct_xdrs), &call_msg))
+    {
+      if (ct->ct_closeit)
+	{
+	  (void) close (*sockp);
+	}
+      goto fooy;
+    }
+  ct->ct_mpos = XDR_GETPOS (&(ct->ct_xdrs));
+  XDR_DESTROY (&(ct->ct_xdrs));
+
+  /*
+   * Create a client handle which uses xdrrec for serialization
+   * and authnone for authentication.
+   */
+  xdrrec_create (&(ct->ct_xdrs), sendsz, recvsz,
+			 (caddr_t) ct, readtcp, writetcp);
+  h->cl_ops = (struct clnt_ops *) &tcp_ops;
+  h->cl_private = (caddr_t) ct;
+  h->cl_auth = authnone_create ();
+  return h;
+
+fooy:
+  /*
+   * Something goofed, free stuff and barf
+   */
+  mem_free ((caddr_t) ct, sizeof (struct ct_data));
+  mem_free ((caddr_t) h, sizeof (CLIENT));
+  return ((CLIENT *) NULL);
+}
+
+static enum clnt_stat
+clnttcp_call (h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
+     CLIENT *h;
+     u_long proc;
+     xdrproc_t xdr_args;
+     caddr_t args_ptr;
+     xdrproc_t xdr_results;
+     caddr_t results_ptr;
+     struct timeval timeout;
+{
+  struct ct_data *ct = (struct ct_data *) h->cl_private;
+  XDR *xdrs = &(ct->ct_xdrs);
+  struct rpc_msg reply_msg;
+  u_long x_id;
+  u_int32_t *msg_x_id = (u_int32_t *) (ct->ct_mcall);	/* yuk */
+  bool_t shipnow;
+  int refreshes = 2;
+
+  if (!ct->ct_waitset)
+    {
+      ct->ct_wait = timeout;
+    }
+
+  shipnow =
+    (xdr_results == (xdrproc_t) 0 && ct->ct_wait.tv_sec == 0
+     && ct->ct_wait.tv_usec == 0) ? FALSE : TRUE;
+
+call_again:
+  xdrs->x_op = XDR_ENCODE;
+  ct->ct_error.re_status = RPC_SUCCESS;
+  x_id = ntohl (--(*msg_x_id));
+  if ((!XDR_PUTBYTES (xdrs, ct->ct_mcall, ct->ct_mpos)) ||
+      (!XDR_PUTLONG (xdrs, (long *) &proc)) ||
+      (!AUTH_MARSHALL (h->cl_auth, xdrs)) ||
+      (!(*xdr_args) (xdrs, args_ptr)))
+    {
+      if (ct->ct_error.re_status == RPC_SUCCESS)
+	ct->ct_error.re_status = RPC_CANTENCODEARGS;
+      (void) xdrrec_endofrecord (xdrs, TRUE);
+      return (ct->ct_error.re_status);
+    }
+  if (!xdrrec_endofrecord (xdrs, shipnow))
+    return ct->ct_error.re_status = RPC_CANTSEND;
+  if (!shipnow)
+    return RPC_SUCCESS;
+  /*
+   * Hack to provide rpc-based message passing
+   */
+  if (ct->ct_wait.tv_sec == 0 && ct->ct_wait.tv_usec == 0)
+    {
+      return ct->ct_error.re_status = RPC_TIMEDOUT;
+    }
+
+
+  /*
+   * Keep receiving until we get a valid transaction id
+   */
+  xdrs->x_op = XDR_DECODE;
+  while (TRUE)
+    {
+      reply_msg.acpted_rply.ar_verf = _null_auth;
+      reply_msg.acpted_rply.ar_results.where = NULL;
+      reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
+      if (!xdrrec_skiprecord (xdrs))
+	return (ct->ct_error.re_status);
+      /* now decode and validate the response header */
+      if (!xdr_replymsg (xdrs, &reply_msg))
+	{
+	  if (ct->ct_error.re_status == RPC_SUCCESS)
+	    continue;
+	  return ct->ct_error.re_status;
+	}
+      if ((u_int32_t) reply_msg.rm_xid == (u_int32_t) x_id)
+	break;
+    }
+
+  /*
+   * process header
+   */
+  _seterr_reply (&reply_msg, &(ct->ct_error));
+  if (ct->ct_error.re_status == RPC_SUCCESS)
+    {
+      if (!AUTH_VALIDATE (h->cl_auth, &reply_msg.acpted_rply.ar_verf))
+	{
+	  ct->ct_error.re_status = RPC_AUTHERROR;
+	  ct->ct_error.re_why = AUTH_INVALIDRESP;
+	}
+      else if (!(*xdr_results) (xdrs, results_ptr))
+	{
+	  if (ct->ct_error.re_status == RPC_SUCCESS)
+	    ct->ct_error.re_status = RPC_CANTDECODERES;
+	}
+      /* free verifier ... */
+      if (reply_msg.acpted_rply.ar_verf.oa_base != NULL)
+	{
+	  xdrs->x_op = XDR_FREE;
+	  (void) xdr_opaque_auth (xdrs,
+					  &(reply_msg.acpted_rply.ar_verf));
+	}
+    }				/* end successful completion */
+  else
+    {
+      /* maybe our credentials need to be refreshed ... */
+      if (refreshes-- && AUTH_REFRESH (h->cl_auth))
+	goto call_again;
+    }				/* end of unsuccessful completion */
+  return ct->ct_error.re_status;
+}
+
+static void
+clnttcp_geterr (h, errp)
+     CLIENT *h;
+     struct rpc_err *errp;
+{
+  struct ct_data *ct =
+  (struct ct_data *) h->cl_private;
+
+  *errp = ct->ct_error;
+}
+
+static bool_t
+clnttcp_freeres (cl, xdr_res, res_ptr)
+     CLIENT *cl;
+     xdrproc_t xdr_res;
+     caddr_t res_ptr;
+{
+  struct ct_data *ct = (struct ct_data *) cl->cl_private;
+  XDR *xdrs = &(ct->ct_xdrs);
+
+  xdrs->x_op = XDR_FREE;
+  return (*xdr_res) (xdrs, res_ptr);
+}
+
+static void
+clnttcp_abort ()
+{
+}
+
+static bool_t
+clnttcp_control (CLIENT *cl, int request, char *info)
+{
+  struct ct_data *ct = (struct ct_data *) cl->cl_private;
+
+
+  switch (request)
+    {
+    case CLSET_FD_CLOSE:
+      ct->ct_closeit = TRUE;
+      break;
+    case CLSET_FD_NCLOSE:
+      ct->ct_closeit = FALSE;
+      break;
+    case CLSET_TIMEOUT:
+      ct->ct_wait = *(struct timeval *) info;
+      ct->ct_waitset = TRUE;
+      break;
+    case CLGET_TIMEOUT:
+      *(struct timeval *) info = ct->ct_wait;
+      break;
+    case CLGET_FD:
+      *(int *)info = ct->ct_sock;
+      break;
+    case CLGET_XID:
+      /*
+       * use the knowledge that xid is the
+       * first element in the call structure *.
+       * This will get the xid of the PREVIOUS call
+       */
+      *(u_long *)info = ntohl (*(u_long *)ct->ct_mcall);
+      break;
+    case CLSET_XID:
+      /* This will set the xid of the NEXT call */
+      *(u_long *)ct->ct_mcall =  htonl (*(u_long *)info - 1);
+      /* decrement by 1 as clnttcp_call() increments once */
+    case CLGET_VERS:
+      /*
+       * This RELIES on the information that, in the call body,
+       * the version number field is the fifth field from the
+       * begining of the RPC header. MUST be changed if the
+       * call_struct is changed
+       */
+      *(u_long *)info = ntohl (*(u_long *)(ct->ct_mcall +
+					   4 * BYTES_PER_XDR_UNIT));
+      break;
+    case CLSET_VERS:
+      *(u_long *)(ct->ct_mcall + 4 * BYTES_PER_XDR_UNIT)
+	= htonl (*(u_long *)info);
+      break;
+    case CLGET_PROG:
+      /*
+       * This RELIES on the information that, in the call body,
+       * the program number field is the  field from the
+       * begining of the RPC header. MUST be changed if the
+       * call_struct is changed
+       */
+      *(u_long *)info = ntohl(*(u_long *)(ct->ct_mcall +
+					  3 * BYTES_PER_XDR_UNIT));
+      break;
+    case CLSET_PROG:
+      *(u_long *)(ct->ct_mcall + 3 * BYTES_PER_XDR_UNIT)
+	= htonl(*(u_long *)info);
+      break;
+    /* The following are only possible with TI-RPC */
+    case CLGET_RETRY_TIMEOUT:
+    case CLSET_RETRY_TIMEOUT:
+    case CLGET_SVC_ADDR:
+    case CLSET_SVC_ADDR:
+    case CLSET_PUSH_TIMOD:
+    case CLSET_POP_TIMOD:
+    default:
+      return FALSE;
+    }
+  return TRUE;
+}
+
+
+static void
+clnttcp_destroy (CLIENT *h)
+{
+  struct ct_data *ct =
+  (struct ct_data *) h->cl_private;
+
+  if (ct->ct_closeit)
+    {
+      (void) close (ct->ct_sock);
+    }
+  XDR_DESTROY (&(ct->ct_xdrs));
+  mem_free ((caddr_t) ct, sizeof (struct ct_data));
+  mem_free ((caddr_t) h, sizeof (CLIENT));
+}
+
+/*
+ * Interface between xdr serializer and tcp connection.
+ * Behaves like the system calls, read & write, but keeps some error state
+ * around for the rpc level.
+ */
+static int
+readtcp (char *ctptr, char *buf, int len)
+{
+  struct ct_data *ct = (struct ct_data *)ctptr;
+  struct pollfd fd;
+  int milliseconds = (ct->ct_wait.tv_sec * 1000) +
+    (ct->ct_wait.tv_usec / 1000);
+
+  if (len == 0)
+    return 0;
+
+  fd.fd = ct->ct_sock;
+  fd.events = POLLIN;
+  while (TRUE)
+    {
+      switch (poll(&fd, 1, milliseconds))
+	{
+	case 0:
+	  ct->ct_error.re_status = RPC_TIMEDOUT;
+	  return -1;
+
+	case -1:
+	  if (errno == EINTR)
+	    continue;
+	  ct->ct_error.re_status = RPC_CANTRECV;
+	  ct->ct_error.re_errno = errno;
+	  return -1;
+	}
+      break;
+    }
+  switch (len = read (ct->ct_sock, buf, len))
+    {
+
+    case 0:
+      /* premature eof */
+      ct->ct_error.re_errno = ECONNRESET;
+      ct->ct_error.re_status = RPC_CANTRECV;
+      len = -1;			/* it's really an error */
+      break;
+
+    case -1:
+      ct->ct_error.re_errno = errno;
+      ct->ct_error.re_status = RPC_CANTRECV;
+      break;
+    }
+  return len;
+}
+
+static int
+writetcp (char *ctptr, char *buf, int len)
+{
+  int i, cnt;
+  struct ct_data *ct = (struct ct_data*)ctptr;
+
+  for (cnt = len; cnt > 0; cnt -= i, buf += i)
+    {
+      if ((i = write (ct->ct_sock, buf, cnt)) == -1)
+	{
+	  ct->ct_error.re_errno = errno;
+	  ct->ct_error.re_status = RPC_CANTSEND;
+	  return -1;
+	}
+    }
+  return len;
+}
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/sunrpc/clnt_tcp2.h libvirt-remote/src/sunrpc/clnt_tcp2.h
--- libvirt-cvs/src/sunrpc/clnt_tcp2.h	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-remote/src/sunrpc/clnt_tcp2.h	2007-02-23 15:59:36.000000000 +0000
@@ -0,0 +1,18 @@
+/*
+ * clnt_tcp2.h: Interface to the SunRPC over TCP.
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * See COPYING.LIB for the license of this software.
+ *
+ * Richard Jones <rjones redhat com>
+ */
+
+#ifndef __CLNT_TCP2_H__
+#define __CLNT_TCP2_H__
+
+extern CLIENT *clnttcp2_create (int af, struct sockaddr *raddr, int raddr_size,
+				u_long prog, u_long vers,
+				int *sockp, u_int sendsz, u_int recvsz);
+
+#endif /* __CLNT_TCP2_H__ */
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/sunrpc/create_xid.c libvirt-remote/src/sunrpc/create_xid.c
--- libvirt-cvs/src/sunrpc/create_xid.c	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-remote/src/sunrpc/create_xid.c	2007-02-23 15:59:36.000000000 +0000
@@ -0,0 +1,64 @@
+/*
+ * 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 <rpc/rpc.h>
+
+#include "config.h"
+
+#if HAVE_LRAND48_R
+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;
+}
+#else
+unsigned long
+_create_xid (void)
+{
+  return rand ();
+}
+#endif
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/sunrpc/README libvirt-remote/src/sunrpc/README
--- libvirt-cvs/src/sunrpc/README	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-remote/src/sunrpc/README	2007-02-26 14:13:55.000000000 +0000
@@ -0,0 +1,29 @@
+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, GnuTLS and connection support.
+	(Original: svc_tcp.c)
+
+svc_tcp2.c
+	- Modified for IPv6 and connection support.
+	(Original: svc_tcp.c)
+
+svc_unix2.c
+	- Modified for connection support.
+	(Original: svc_unix.c)
+
+$Id$
\ No newline at end of file
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/sunrpc/svc_connections.h libvirt-remote/src/sunrpc/svc_connections.h
--- libvirt-cvs/src/sunrpc/svc_connections.h	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-remote/src/sunrpc/svc_connections.h	2007-02-26 14:20:54.000000000 +0000
@@ -0,0 +1,18 @@
+/*
+ * svc_connections.h: Callbacks to make SunRPC server transports
+ * connection-oriented.
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * See COPYING.LIB for the license of this software.
+ *
+ * Richard Jones <rjones redhat com>
+ */
+
+#ifndef __SVC_CONNECTIONS_H__
+#define __SVC_CONNECTIONS_H__
+
+typedef int (*svc_conn_create) (SVCXPRT *);
+typedef void (*svc_conn_destroy) (SVCXPRT *);
+
+#endif /* __SVC_CONNECTIONS_H__ */
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/sunrpc/svc_gnutls.c libvirt-remote/src/sunrpc/svc_gnutls.c
--- libvirt-cvs/src/sunrpc/svc_gnutls.c	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-remote/src/sunrpc/svc_gnutls.c	2007-02-28 17:02:18.000000000 +0000
@@ -0,0 +1,674 @@
+/* 
+ * 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, svc_conn_destroy, gnutls_session_t);
+
+struct tcp_rendezvous
+  {				/* kept in xprt->xp_p1 */
+    u_int sendsize;
+    u_int recvsize;
+    svc_conn_create create;
+    svc_conn_destroy destroy;
+    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];
+    svc_conn_destroy destroy;
+  };
+
+/*
+ * 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,
+		  svc_conn_create create, svc_conn_destroy destroy,
+		  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->create = create;
+  r->destroy = destroy;
+  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, svc_conn_destroy destroy,
+	     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;
+  cd->destroy = destroy;
+  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;
+  int err;
+
+  err = gnutls_init (&session, GNUTLS_SERVER);
+  if (err) { gnutls_perror (err); return 0; }
+
+  /* avoid calling all the priority functions, since the defaults
+   * are adequate.
+   */
+  err = gnutls_set_default_priority (session);
+  if (err) { gnutls_perror (err); return 0; }
+
+  err = gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+  if (err) { gnutls_perror (err); return 0; }
+
+  /* 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, err;
+  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);
+  if (session == 0) return FALSE;
+
+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);
+
+  /* XXX Possible DoS attack here if the client sends the handshake
+   * very slowly.  It may be possible to avoid this using a non-blocking
+   * socket, see:
+   * https://www.redhat.com/archives/libvir-list/2007-February/msg00192.html
+   */
+  int ret = gnutls_handshake (session);
+  if (ret < 0)
+    {
+      fprintf (stderr, "svc_gnutls: TLS handshake failed (%s)\n\n",
+              gnutls_strerror (ret));
+    tls_failed:
+      close (sock);
+      gnutls_deinit (session);
+      return FALSE;
+    }
+
+  /* Convert IP address to printable string (eg. "127.0.0.1") */
+  char addr[NI_MAXHOST];
+  err = getnameinfo ((struct sockaddr *) &raddr, raddrlen,
+		     addr, sizeof addr, NULL, 0,
+		     NI_NUMERICHOST);
+  if (err != 0)
+    {
+      fprintf (stderr, "svc_gnutls: getnameinfo: %s\n", gai_strerror (err));
+      goto tls_failed;
+    }
+
+  /* Verify client certificate. */
+  if (verify_certificate (session, addr) == -1)
+    {
+      fprintf (stderr, "svc_gnutls: failed to verify client'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;
+    }
+
+  /* Checks have succeeded.  Write a '\1' byte back to the client to
+   * indicate this (otherwise the socket is abruptly closed in
+   * tls_failed above).  This is necessary to avoid the client
+   * segfaulting if the checks fail.
+   */
+  char buf[1] = { '\1' };
+ again_2:
+  if ((err = gnutls_record_send (session, buf, 1)) < 0)
+    {
+      if (err == GNUTLS_E_INTERRUPTED || err == GNUTLS_E_AGAIN) goto again_2;
+      fprintf (stderr, "svc_gnutls: client closed connection early\n");
+      goto tls_failed;
+    }
+
+  /*
+   * make a new transporter (re-uses xprt)
+   */
+  xprt = makefd_xprt (sock, r->sendsize, r->recvsize, r->destroy, session);
+#if 0				/* This assumed IPv4 - commented out. */
+  memcpy (&xprt->xp_raddr, &addr, sizeof (addr));
+  xprt->xp_addrlen = len;
+#endif
+  if (r->create) (void) r->create (xprt);
+  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 client certificate is not trusted.\n");
+
+    if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+      fprintf (stderr, "svc_gnutls: the client certificate hasn't got a known issuer.\n");
+
+    if (status & GNUTLS_CERT_REVOKED)
+      fprintf (stderr, "svc_gnutls: the client certificate has been revoked.\n");
+
+    if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
+      fprintf (stderr, "svc_gnutls: the client 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 client 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 client 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 client certificate's subjectAltName.iPAddress or commonName does not match the client's actual 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.  If
+ * and only if there is no subjectAltName iPAddress extension, then
+ * we check against the CN.  It currently only allows exact matches
+ * (ie. not wildcards).  See RFC 2818 sections 3.1 & 3.2.  Also
+ * updated in RFC 3280.  Also see lib/x509/rfc2818_hostname.c in
+ * the GnuTLS source.
+ */
+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)
+    {
+      namesize = sizeof name;
+      if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0,
+                                         0, name, &namesize) < 0)
+	return 0;
+
+      if (strcmp (name, addr) == 0) return 1;
+    }
+
+  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);
+
+  if (cd->destroy) cd->destroy (xprt);
+
+  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;
+  int err;
+
+ again:
+  if ((err = gnutls_record_send (session, buf, len)) < 0) {
+    if (err == GNUTLS_E_INTERRUPTED || err == GNUTLS_E_AGAIN) goto again;
+    ((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 -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/sunrpc/svc_gnutls.h libvirt-remote/src/sunrpc/svc_gnutls.h
--- libvirt-cvs/src/sunrpc/svc_gnutls.h	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-remote/src/sunrpc/svc_gnutls.h	2007-02-26 14:19:25.000000000 +0000
@@ -0,0 +1,25 @@
+/*
+ * 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__
+
+#include "svc_connections.h"
+
+extern SVCXPRT *svcgnutls_create (int sock, u_int sendsize, u_int recvsize,
+				  svc_conn_create create,
+				  svc_conn_destroy destroy,
+                                  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 -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/sunrpc/svc_tcp2.c libvirt-remote/src/sunrpc/svc_tcp2.c
--- libvirt-cvs/src/sunrpc/svc_tcp2.c	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-remote/src/sunrpc/svc_tcp2.c	2007-02-26 14:41:18.000000000 +0000
@@ -0,0 +1,440 @@
+/*
+ * 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, svc_conn_destroy);
+
+struct tcp_rendezvous
+  {				/* kept in xprt->xp_p1 */
+    u_int sendsize;
+    u_int recvsize;
+    svc_conn_create create;
+    svc_conn_destroy destroy;
+  };
+
+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];
+    svc_conn_destroy destroy;
+  };
+
+/*
+ * 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,
+		svc_conn_create create, svc_conn_destroy destroy)
+{
+  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;
+  r->create = create;
+  r->destroy = destroy;
+  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, svc_conn_destroy destroy)
+{
+  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;
+  cd->destroy = destroy;
+  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, r->destroy);
+#if 0
+  memcpy (&xprt->xp_raddr, &addr, sizeof (addr));
+  xprt->xp_addrlen = len;
+#endif
+  if (r->create) (void) r->create (xprt);
+  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;
+
+  if (cd->destroy) cd->destroy (xprt);
+
+  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 -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/sunrpc/svc_tcp2.h libvirt-remote/src/sunrpc/svc_tcp2.h
--- libvirt-cvs/src/sunrpc/svc_tcp2.h	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-remote/src/sunrpc/svc_tcp2.h	2007-02-26 14:19:05.000000000 +0000
@@ -0,0 +1,20 @@
+/*
+ * 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__
+
+#include "svc_connections.h"
+
+extern SVCXPRT *svctcp2_create (int sock, u_int sendsize, u_int recvsize,
+				svc_conn_create create,
+				svc_conn_destroy destroy);
+
+#endif /* __SVC_TCP2_H__ */
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/sunrpc/svc_unix2.c libvirt-remote/src/sunrpc/svc_unix2.c
--- libvirt-cvs/src/sunrpc/svc_unix2.c	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-remote/src/sunrpc/svc_unix2.c	2007-02-26 14:43:23.000000000 +0000
@@ -0,0 +1,542 @@
+/*
+ * svc_unix2.c: SunRPC Unix domain socket server.  This is a very
+ * slightly modified version of svc_unix.c from glibc which adds
+ * support for connections.
+ *
+ * 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
+ */
+
+/*
+ * svc_unix.c, Server side for TCP/IP based RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * Actually implements two flavors of transporter -
+ * a unix rendezvouser (a listener and connection establisher)
+ * and a record/unix stream.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <rpc/rpc.h>
+#include <rpc/svc.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/poll.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <libintl.h>
+
+#ifdef USE_IN_LIBIO
+# include <wchar.h>
+#endif
+
+#include "svc_unix2.h"
+
+/*
+ * Ops vector for AF_UNIX based rpc service handle
+ */
+static bool_t svcunix_recv (SVCXPRT *, struct rpc_msg *);
+static enum xprt_stat svcunix_stat (SVCXPRT *);
+static bool_t svcunix_getargs (SVCXPRT *, xdrproc_t, caddr_t);
+static bool_t svcunix_reply (SVCXPRT *, struct rpc_msg *);
+static bool_t svcunix_freeargs (SVCXPRT *, xdrproc_t, caddr_t);
+static void svcunix_destroy (SVCXPRT *);
+
+static const struct xp_ops svcunix_op =
+{
+  svcunix_recv,
+  svcunix_stat,
+  svcunix_getargs,
+  svcunix_reply,
+  svcunix_freeargs,
+  svcunix_destroy
+};
+
+/*
+ * Ops vector for AF_UNIX rendezvous handler
+ */
+static bool_t rendezvous_request (SVCXPRT *, struct rpc_msg *);
+static enum xprt_stat rendezvous_stat (SVCXPRT *);
+static void svcunix_rendezvous_abort (void) __attribute__ ((__noreturn__));
+
+/* This function makes sure abort() relocation goes through PLT
+   and thus can be lazy bound.  */
+static void
+svcunix_rendezvous_abort (void)
+{
+  abort ();
+};
+
+static const struct xp_ops svcunix_rendezvous_op =
+{
+  rendezvous_request,
+  rendezvous_stat,
+  (bool_t (*) (SVCXPRT *, xdrproc_t, caddr_t)) svcunix_rendezvous_abort,
+  (bool_t (*) (SVCXPRT *, struct rpc_msg *)) svcunix_rendezvous_abort,
+  (bool_t (*) (SVCXPRT *, xdrproc_t, caddr_t)) svcunix_rendezvous_abort,
+  svcunix_destroy
+};
+
+static int readunix (char*, char *, int);
+static int writeunix (char *, char *, int);
+static SVCXPRT *makefd_xprt (int, u_int, u_int, svc_conn_destroy);
+
+struct unix_rendezvous {        /* kept in xprt->xp_p1 */
+  u_int sendsize;
+  u_int recvsize;
+  svc_conn_create create;
+  svc_conn_destroy destroy;
+};
+
+struct unix_conn {		/* kept in xprt->xp_p1 */
+  enum xprt_stat strm_stat;
+  u_long x_id;
+  XDR xdrs;
+  char verf_body[MAX_AUTH_BYTES];
+  svc_conn_destroy destroy;
+};
+
+/*
+ * Usage:
+ *      xprt = svcunix_create(sock, send_buf_size, recv_buf_size);
+ *
+ * Creates, registers, and returns a (rpc) unix based transporter.
+ * Once *xprt is initialized, it is registered as a transporter
+ * see (svc.h, xprt_register).  This routine returns
+ * a NULL if a problem occurred.
+ *
+ * If sock<0 then a socket is created, else sock is used.
+ * If the socket, sock is not bound to a port then svcunix_create
+ * binds it to an arbitrary port.  The routine then starts a unix
+ * listener on the socket's associated port.  In any (successful) case,
+ * xprt->xp_sock is the registered socket number and xprt->xp_port is the
+ * associated port number.
+ *
+ * Since unix streams do buffered io similar to stdio, the caller can specify
+ * how big the send and receive buffers are via the second and third parms;
+ * 0 => use the system default.
+ */
+SVCXPRT *
+svcunix2_create (int sock, u_int sendsize, u_int recvsize,
+		 svc_conn_create create, svc_conn_destroy destroy,
+		 char *path)
+{
+  bool_t madesock = FALSE;
+  SVCXPRT *xprt;
+  struct unix_rendezvous *r;
+  struct sockaddr_un addr;
+  socklen_t len = sizeof (struct sockaddr_in);
+
+  if (sock == RPC_ANYSOCK)
+    {
+      if ((sock = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
+	{
+	  perror ("svc_unix.c - AF_UNIX socket creation problem");
+	  return (SVCXPRT *) NULL;
+	}
+      madesock = TRUE;
+    }
+  memset (&addr, '\0', sizeof (addr));
+  addr.sun_family = AF_UNIX;
+  len = strlen (path) + 1;
+  memcpy (addr.sun_path, path, len);
+  len += sizeof (addr.sun_family);
+
+  bind (sock, (struct sockaddr *) &addr, len);
+
+  if (getsockname (sock, (struct sockaddr *) &addr, &len) != 0
+      || listen (sock, SOMAXCONN) != 0)
+    {
+      perror ("svc_unix.c - cannot getsockname or listen");
+      if (madesock)
+	close (sock);
+      return (SVCXPRT *) NULL;
+    }
+
+  r = (struct unix_rendezvous *) mem_alloc (sizeof (*r));
+  xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT));
+  if (r == NULL || xprt == NULL)
+    {
+      fprintf (stderr, "svcunix_create: out of memory\n");
+      mem_free (r, sizeof (*r));
+      mem_free (xprt, sizeof (SVCXPRT));
+      return NULL;
+    }
+  r->sendsize = sendsize;
+  r->recvsize = recvsize;
+  r->create = create;
+  r->destroy = destroy;
+  xprt->xp_p2 = NULL;
+  xprt->xp_p1 = (caddr_t) r;
+  xprt->xp_verf = _null_auth;
+  xprt->xp_ops = &svcunix_rendezvous_op;
+  xprt->xp_port = -1;
+  xprt->xp_sock = sock;
+  xprt_register (xprt);
+  return xprt;
+}
+
+#if 0
+/*
+ * Like svunix_create(), except the routine takes any *open* UNIX file
+ * descriptor as its first input.
+ */
+SVCXPRT *
+svcunixfd_create (int fd, u_int sendsize, u_int recvsize)
+{
+  return makefd_xprt (fd, sendsize, recvsize);
+}
+#endif
+
+static SVCXPRT *
+makefd_xprt (int fd, u_int sendsize, u_int recvsize, svc_conn_destroy destroy)
+{
+  SVCXPRT *xprt;
+  struct unix_conn *cd;
+
+  xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT));
+  cd = (struct unix_conn *) mem_alloc (sizeof (struct unix_conn));
+  if (xprt == (SVCXPRT *) NULL || cd == (struct unix_conn *) NULL)
+    {
+      fprintf (stderr, "svc_unix: makefd_xprt: out of memory\n");
+      mem_free (xprt, sizeof (SVCXPRT));
+      mem_free (cd, sizeof (struct unix_conn));
+      return NULL;
+    }
+  cd->strm_stat = XPRT_IDLE;
+  cd->destroy = destroy;
+  xdrrec_create (&(cd->xdrs), sendsize, recvsize,
+		 (caddr_t) xprt, readunix, writeunix);
+  xprt->xp_p2 = NULL;
+  xprt->xp_p1 = (caddr_t) cd;
+  xprt->xp_verf.oa_base = cd->verf_body;
+  xprt->xp_addrlen = 0;
+  xprt->xp_ops = &svcunix_op;	/* truly deals with calls */
+  xprt->xp_port = 0;		/* this is a connection, not a rendezvouser */
+  xprt->xp_sock = fd;
+  xprt_register (xprt);
+  return xprt;
+}
+
+static bool_t
+rendezvous_request (SVCXPRT *xprt __attribute__((unused)),
+		    struct rpc_msg *errmsg __attribute__((unused)))
+{
+  int sock;
+  struct unix_rendezvous *r;
+  struct sockaddr_un addr;
+  struct sockaddr_in in_addr;
+  socklen_t len;
+
+  r = (struct unix_rendezvous *) xprt->xp_p1;
+again:
+  len = sizeof (struct sockaddr_un);
+  if ((sock = accept (xprt->xp_sock, (struct sockaddr *) &addr, &len)) < 0)
+    {
+      if (errno == EINTR)
+	goto again;
+      return FALSE;
+    }
+  /*
+   * make a new transporter (re-uses xprt)
+   */
+  memset (&in_addr, '\0', sizeof (in_addr));
+  in_addr.sin_family = AF_UNIX;
+  xprt = makefd_xprt (sock, r->sendsize, r->recvsize, r->destroy);
+  memcpy (&xprt->xp_raddr, &in_addr, sizeof (in_addr));
+  xprt->xp_addrlen = len;
+  if (r->create) (void) r->create (xprt);
+  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
+svcunix_destroy (SVCXPRT *xprt)
+{
+  struct unix_conn *cd = (struct unix_conn *) xprt->xp_p1;
+
+  if (cd->destroy) cd->destroy (xprt);
+
+  xprt_unregister (xprt);
+  close (xprt->xp_sock);
+  if (xprt->xp_port != 0)
+    {
+      /* a rendezvouser socket */
+      xprt->xp_port = 0;
+    }
+  else
+    {
+      /* an actual connection socket */
+      XDR_DESTROY (&(cd->xdrs));
+    }
+  mem_free ((caddr_t) cd, sizeof (struct unix_conn));
+  mem_free ((caddr_t) xprt, sizeof (SVCXPRT));
+}
+
+#ifdef SCM_CREDENTIALS
+struct cmessage {
+  struct cmsghdr cmsg;
+  struct ucred cmcred;
+  /* hack to make sure we have enough memory */
+  char dummy[(CMSG_ALIGN (sizeof (struct ucred)) - sizeof (struct ucred) + sizeof (long))];
+};
+
+/* XXX This is not thread safe, but since the main functions in svc.c
+   and the rpcgen generated *_svc functions for the daemon are also not
+   thread safe and uses static global variables, it doesn't matter. */
+static struct cmessage cm;
+#endif
+
+static int
+__msgread (int sock, void *data, size_t cnt)
+{
+  struct iovec iov;
+  struct msghdr msg;
+  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 = sizeof (struct cmessage);
+#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 = &cm.cmsg;
+  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
+}
+
+/*
+ * reads data from the unix connection.
+ * any error is fatal and the connection is closed.
+ * (And a read of zero bytes is a half closed stream => error.)
+ */
+static int
+readunix (char *xprtptr, char *buf, int len)
+{
+  SVCXPRT *xprt = (SVCXPRT *) xprtptr;
+  int sock = xprt->xp_sock;
+  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 = __msgread (sock, buf, len)) > 0)
+    return len;
+
+ fatal_err:
+  ((struct unix_conn *) (xprt->xp_p1))->strm_stat = XPRT_DIED;
+  return -1;
+}
+
+/*
+ * writes data to the unix connection.
+ * Any error is fatal and the connection is closed.
+ */
+static int
+writeunix (char *xprtptr, char * buf, int len)
+{
+  SVCXPRT *xprt = (SVCXPRT *) xprtptr;
+  int i, cnt;
+
+  for (cnt = len; cnt > 0; cnt -= i, buf += i)
+    {
+      if ((i = __msgwrite (xprt->xp_sock, buf, cnt)) < 0)
+	{
+	  ((struct unix_conn *) (xprt->xp_p1))->strm_stat = XPRT_DIED;
+	  return -1;
+	}
+    }
+  return len;
+}
+
+static enum xprt_stat
+svcunix_stat (SVCXPRT *xprt)
+{
+  struct unix_conn *cd =
+  (struct unix_conn *) (xprt->xp_p1);
+
+  if (cd->strm_stat == XPRT_DIED)
+    return XPRT_DIED;
+  if (!xdrrec_eof (&(cd->xdrs)))
+    return XPRT_MOREREQS;
+  return XPRT_IDLE;
+}
+
+static bool_t
+svcunix_recv (SVCXPRT *xprt, struct rpc_msg *msg)
+{
+  struct unix_conn *cd = (struct unix_conn *) (xprt->xp_p1);
+  XDR *xdrs = &(cd->xdrs);
+
+  xdrs->x_op = XDR_DECODE;
+  xdrrec_skiprecord (xdrs);
+  if (xdr_callmsg (xdrs, msg))
+    {
+      cd->x_id = msg->rm_xid;
+      /* set up verifiers */
+#ifdef SCM_CREDENTIALS
+      msg->rm_call.cb_verf.oa_flavor = AUTH_UNIX;
+      msg->rm_call.cb_verf.oa_base = (caddr_t) &cm;
+      msg->rm_call.cb_verf.oa_length = sizeof (cm);
+#endif
+      return TRUE;
+    }
+  cd->strm_stat = XPRT_DIED;	/* XXXX */
+  return FALSE;
+}
+
+static bool_t
+svcunix_getargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
+{
+  return (*xdr_args) (&(((struct unix_conn *) (xprt->xp_p1))->xdrs),
+		      args_ptr);
+}
+
+static bool_t
+svcunix_freeargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
+{
+  XDR *xdrs = &(((struct unix_conn *) (xprt->xp_p1))->xdrs);
+
+  xdrs->x_op = XDR_FREE;
+  return (*xdr_args) (xdrs, args_ptr);
+}
+
+static bool_t
+svcunix_reply (SVCXPRT *xprt, struct rpc_msg *msg)
+{
+  struct unix_conn *cd = (struct unix_conn *) (xprt->xp_p1);
+  XDR *xdrs = &(cd->xdrs);
+  bool_t stat;
+
+  xdrs->x_op = XDR_ENCODE;
+  msg->rm_xid = cd->x_id;
+  stat = xdr_replymsg (xdrs, msg);
+  (void) xdrrec_endofrecord (xdrs, TRUE);
+  return stat;
+}
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/sunrpc/svc_unix2.h libvirt-remote/src/sunrpc/svc_unix2.h
--- libvirt-cvs/src/sunrpc/svc_unix2.h	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-remote/src/sunrpc/svc_unix2.h	2007-02-26 14:27:23.000000000 +0000
@@ -0,0 +1,21 @@
+/*
+ * svc_unix2.h: Interface to the SunRPC over Unix domain sockets server.
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * See COPYING.LIB for the license of this software.
+ *
+ * Richard Jones <rjones redhat com>
+ */
+
+#ifndef __SVC_UNIX2_H__
+#define __SVC_UNIX2_H__
+
+#include "svc_connections.h"
+
+extern SVCXPRT *svcunix2_create (int sock, u_int sendsize, u_int recvsize,
+				 svc_conn_create create,
+				 svc_conn_destroy destroy,
+				 char *path);
+
+#endif /* __SVC_UNIX2_H__ */
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA libvirt-cvs/src/virterror.c libvirt-remote/src/virterror.c
--- libvirt-cvs/src/virterror.c	2007-02-14 15:40:54.000000000 +0000
+++ libvirt-remote/src/virterror.c	2007-02-26 13:59:59.000000000 +0000
@@ -274,6 +274,9 @@
         case VIR_FROM_NET:
             dom = "Network ";
             break;
+        case VIR_FROM_REMOTE:
+            dom = "Remote ";
+            break;
     }
     if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) {
         domain = err->dom->name;
@@ -604,6 +607,18 @@
 	    else
 	        errmsg = _("network %s exists already");
             break;
+        case VIR_ERR_RPC:
+	    if (info == NULL)
+	        errmsg = _("RPC error");
+	    else
+	        errmsg = "%s";
+	    break;
+        case VIR_ERR_RPC_BAD_CONNECTION:
+	    if (info == NULL)
+	        errmsg = _("RPC bad connection");
+	    else
+	        errmsg = "%s";
+	    break;
     }
     return (errmsg);
 }

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature


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