So this patch is just posted here for public record. It applies against the latest CVS. Rich. [1] https://www.redhat.com/archives/libvir-list/2007-March/thread.html#00333 -- Emerging Technologies, Red Hat http://et.redhat.com/~rjones/ 64 Baker Street, London, W1U 7DF Mobile: +44 7866 314 421 Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SL4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Directors: Michael Cunningham (USA), Charlie Peters (USA) and David Owens (Ireland)
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/configure.in libvirt-oldremote/configure.in
--- libvirt-cvs/configure.in 2007-04-04 15:39:43.000000000 +0100
+++ libvirt-oldremote/configure.in 2007-04-04 13:40:43.000000000 +0100
@@ -42,9 +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([regexec])
+AC_CHECK_FUNCS([lrand48_r regexec])
dnl Make sure we have an ANSI compiler
AM_C_PROTOTYPES
@@ -264,6 +270,17 @@
AC_SUBST(LIBXML_CONFIG)
AC_SUBST(LIBXML_MIN_VERSION)
+dnl GnuTLS library
+AC_CHECK_HEADER([gnutls/gnutls.h],
+ [],
+ AC_MSG_ERROR([You must install the GnuTLS development package in order to compile libvirt]))
+AC_CHECK_LIB(gnutls, gnutls_handshake,
+ [],
+ [AC_MSG_ERROR([You must install the GnuTLS library in order to compile and run libvirt])])
+
+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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/include/libvirt/virterror.h libvirt-oldremote/include/libvirt/virterror.h
--- libvirt-cvs/include/libvirt/virterror.h 2007-03-16 11:04:46.000000000 +0000
+++ libvirt-oldremote/include/libvirt/virterror.h 2007-04-04 12:54:22.000000000 +0100
@@ -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;
@@ -120,6 +121,8 @@
VIR_ERR_INVALID_NETWORK, /* invalid network object */
VIR_ERR_NETWORK_EXIST, /* the network already exist */
VIR_ERR_SYSTEM_ERROR, /* general system call failure */
+ VIR_ERR_RPC, /* some sort of RPC error */
+ VIR_ERR_RPC_BAD_CONNECTION, /* bad connection in RPC */
} virErrorNumber;
/**
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/.cvsignore libvirt-oldremote/src/.cvsignore
--- libvirt-cvs/src/.cvsignore 2007-02-23 17:15:42.000000000 +0000
+++ libvirt-oldremote/src/.cvsignore 2007-03-07 12:27:20.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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/driver.h libvirt-oldremote/src/driver.h
--- libvirt-cvs/src/driver.h 2007-04-04 15:39:43.000000000 +0100
+++ libvirt-oldremote/src/driver.h 2007-04-04 15:26:42.000000000 +0100
@@ -20,6 +20,7 @@
VIR_DRV_XEN_UNIFIED = 1,
VIR_DRV_TEST = 2,
VIR_DRV_QEMU = 3,
+ VIR_DRV_REMOTE = 4,
} virDrvNo;
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/libvirt.c libvirt-oldremote/src/libvirt.c
--- libvirt-cvs/src/libvirt.c 2007-04-04 15:39:43.000000000 +0100
+++ libvirt-oldremote/src/libvirt.c 2007-04-04 15:27:37.000000000 +0100
@@ -28,6 +28,7 @@
#include "test.h"
#include "xen_unified.h"
#include "qemu_internal.h"
+#include "remote_internal.h"
/*
* TODO:
@@ -64,6 +65,7 @@
* Note that the order is important: the first ones have a higher
* priority when calling virConnectOpen.
*/
+ if (remoteRegister () == -1) return -1;
#ifdef WITH_XEN
if (xenUnifiedRegister () == -1) return -1;
#endif
@@ -375,6 +377,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)
{
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/libvirtd.c libvirt-oldremote/src/libvirtd.c
--- libvirt-cvs/src/libvirtd.c 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/src/libvirtd.c 2007-04-04 14:39:17.000000000 +0100
@@ -0,0 +1,1053 @@
+/*
+ * 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"
+
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+
+/* 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 = "";
+
+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 {
+ pid_t pid = getpid ();
+
+ /* 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 (%ld): open \"%s\"\n",
+ (long) pid, 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,
+ virConnGetLastError (conn));
+ }
+ }
+
+ 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)
+{
+ /* No error, even though status code of the call indicates one.
+ * Return a "internal error" indication.
+ */
+ if (!verr) {
+ err->code = VIR_ERR_INTERNAL_ERROR;
+ err->domain = 0;
+ err->message = null_string;
+ err->level = VIR_ERR_ERROR;
+ err->dom = NULL;
+ err->str1 = null_string;
+ err->str2 = null_string;
+ err->str3 = null_string;
+ err->int1 = 0;
+ err->int2 = 0;
+ err->net = NULL;
+ return;
+ }
+
+ 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.
+}
+
+/* Log the peer (client) IP address. */
+static void
+log_peer (int sock)
+{
+ /* Log the connection to stderr. It will end up in syslog if
+ * logger is running.
+ */
+ pid_t pid = getpid ();
+ int r;
+ fprintf (stderr, "libvirtd (%ld): new connection\n", (long) 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 (%ld): connection from Unix domain socket (may include connections over ssh and ext transports)\n",
+ (long) 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 (%ld): getnameinfo: %s\n",
+ (long) pid, gai_strerror (r));
+ else
+ fprintf (stderr, "libvirtd (%ld): connection from %s port %s\n",
+ (long) 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);
+ }
+ pid_t 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*/"/tmp/nothere", (char *const *) args);
+ perror ("execv");
+ _exit (1);
+ }
+ close (fd[0]);
+ dup2 (fd[1], 2);
+ close (fd[1]);
+#endif
+ }
+
+ /* Start up message. Note that this also ensures that we have
+ * a clear path to syslog (if running logger) or to stderr,
+ * because we carefully check the return value. So this isn't
+ * just for vanity.
+ */
+ char msg[] =
+ "libvirtd: "
+ "version " TOSTRING(LIBVIR_VERSION_NUMBER) " "
+ "protocol " TOSTRING(LIBVIRTREMOTE_VERS1) "\n";
+ if (fprintf (stderr, "%s", msg) != sizeof msg - 1) {
+ /* Erm, well we can't write to stderr here ... XXX */
+ fprintf (stderr, "libvirtd: cannot write to stderr\n");
+ exit (1);
+ }
+
+ 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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/libvirt_sym.version libvirt-oldremote/src/libvirt_sym.version
--- libvirt-cvs/src/libvirt_sym.version 2007-03-16 11:04:46.000000000 +0000
+++ libvirt-oldremote/src/libvirt_sym.version 2007-03-16 11:13:17.000000000 +0000
@@ -63,6 +63,10 @@
virDomainAttachDevice;
virDomainDetachDevice;
+ _virConfReadFile;
+ _virConfGetValue;
+ _virConfFree;
+
virConnectNumOfNetworks;
virConnectListNetworks;
virConnectNumOfDefinedNetworks;
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/Makefile.am libvirt-oldremote/src/Makefile.am
--- libvirt-cvs/src/Makefile.am 2007-04-04 15:39:43.000000000 +0100
+++ libvirt-oldremote/src/Makefile.am 2007-04-04 15:43:51.000000000 +0100
@@ -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)\" $(WARN_CFLAGS) $(LIBVIRT_FEATURES)
DEPS = libvirt.la
LDADDS = @STATIC_BINARIES@ libvirt.la
@@ -30,10 +31,18 @@
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.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)
@@ -41,6 +50,33 @@
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
+ mv remote_rpc_xdr.c remote_rpc_xdr.c.bak
+ perl -w remote_rpc_xdr_fix.pl < remote_rpc_xdr.c.bak > remote_rpc_xdr.c
+ mv remote_rpc.h remote_rpc.h.bak
+ perl -w remote_rpc_fix.pl < remote_rpc.h.bak > remote_rpc.h
+
+remote_rpc_xdr.c: remote_rpc_xdr_fix.pl
+remote_rpc.h: remote_rpc_fix.pl
+
#
# target to ease building test programs
#
@@ -57,4 +93,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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/remote_internal.c libvirt-oldremote/src/remote_internal.c
--- libvirt-cvs/src/remote_internal.c 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/src/remote_internal.c 2007-04-04 14:39:14.000000000 +0100
@@ -0,0 +1,1054 @@
+/*
+ * 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 *priv = (struct private_data *) (conn)->privateData; \
+ assert (priv); \
+ if (priv->magic == DEAD) { \
+ error (conn, VIR_ERR_INVALID_ARG, \
+ "tried to use a closed or uninitialised handle"); \
+ return (retcode); \
+ } \
+ assert (priv->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 priv = { .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) {
+ priv.sock = RPC_ANYSOCK;
+ priv.cl =
+ transport == trans_tls
+ ? clntgnutls_create (r->ai_family, r->ai_addr, r->ai_addrlen,
+ x509_cred,
+ no_verify, server,
+ LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1,
+ &priv.sock, 0, 0)
+ : clnttcp2_create (r->ai_family, r->ai_addr, r->ai_addrlen,
+ LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1,
+ &priv.sock, 0, 0);
+ if (priv.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));
+
+ priv.sock = RPC_ANYSOCK;
+ priv.cl =
+ clntunix_create (&addr,
+ LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1,
+ &priv.sock, 0, 0);
+ if (!priv.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:
+ priv.sock = RPC_ANYSOCK;
+ priv.cl =
+ clntext_create (command, cmd_argv,
+ LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1,
+ &priv.sock, 0, 0);
+ if (!priv.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, priv.cl);
+ if (!ret) {
+ error (conn, VIR_ERR_RPC,
+ clnt_sperror (priv.cl, "remote_open"));
+ clnt_destroy (priv.cl);
+ goto failed;
+ }
+ if (ret->status == -1) {
+ server_error (conn, &ret->remote_open_ret_u.err);
+ goto failed;
+ }
+ clnt_freeres (priv.cl, (xdrproc_t) xdr_remote_open_ret, (char *) ret);
+
+ conn->privateData = malloc (sizeof priv);
+ if (!conn->privateData) {
+ error (conn, VIR_ERR_NO_MEMORY, "malloc");
+ clnt_destroy (priv.cl);
+ goto failed;
+ }
+ priv.magic = MAGIC;
+ memcpy (conn->privateData, &priv, sizeof priv);
+
+ /* 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 (¬hing, priv->cl);
+ if (!ret) {
+ error (conn, VIR_ERR_RPC,
+ clnt_sperror (priv->cl, "remote_close"));
+ failed:
+ clnt_freeres (priv->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 (priv->cl, (xdrproc_t) xdr_remote_close_ret, (char *) ret);
+
+ // NB. clnt_destroy should close the socket (priv->sock) too.
+ clnt_destroy (priv->cl);
+ // Force errors if anyone tries to reuse the closed connection.
+ priv->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 (¬hing, priv->cl);
+ if (!ret) {
+ error (conn, VIR_ERR_RPC,
+ clnt_sperror (priv->cl, "remote_type"));
+ failed:
+ clnt_freeres (priv->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 (priv->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 (¬hing, priv->cl);
+ if (!ret) {
+ error (conn, VIR_ERR_RPC,
+ clnt_sperror (priv->cl, "remote_version"));
+ failed:
+ clnt_freeres (priv->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 (priv->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 (¬hing, priv->cl);
+ if (!ret) {
+ error (conn, VIR_ERR_RPC,
+ clnt_sperror (priv->cl, "remote_nodeGetInfo"));
+ failed:
+ clnt_freeres (priv->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 (priv->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, priv->cl);
+ if (!ret) {
+ error (conn, VIR_ERR_RPC,
+ clnt_sperror (priv->cl, "remote_listDomains"));
+ failed:
+ clnt_freeres (priv->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 (priv->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 (¬hing, priv->cl);
+ if (!ret) {
+ error (conn, VIR_ERR_RPC,
+ clnt_sperror (priv->cl, "remote_numOfDomains"));
+ failed:
+ clnt_freeres (priv->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 (priv->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, priv->cl);
+ if (!ret) {
+ error (conn, VIR_ERR_RPC,
+ clnt_sperror (priv->cl, "remote_domainCreateLinux"));
+ failed:
+ clnt_freeres (priv->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 (priv->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,
+#if 0
+ .getCapabilities = do_getCapabilities,
+#endif
+ .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. */
+int
+remoteRegister (void)
+{
+ return 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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/remote_internal.h libvirt-oldremote/src/remote_internal.h
--- libvirt-cvs/src/remote_internal.h 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/src/remote_internal.h 2007-04-04 13:46:57.000000000 +0100
@@ -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 int remoteRegister (void);
+
+#define LIBVIRTD_TLS_PORT "16514"
+#define LIBVIRTD_TCP_PORT "16509"
+#define LIBVIRTD_UNIX_SOCKET (LOCAL_STATE_DIR "/run/libvirt/libvirtd-sock")
+#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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/remote_rpc_fix.pl libvirt-oldremote/src/remote_rpc_fix.pl
--- libvirt-cvs/src/remote_rpc_fix.pl 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/src/remote_rpc_fix.pl 2007-03-02 14:12:06.000000000 +0000
@@ -0,0 +1,19 @@
+# Fix remote_rpc.h (generated by rpcgen) so that it compiles
+# with warnings turned on.
+#
+# This code is evil. Arguably better would be just to compile
+# remote_rpc.h without -Werror.
+#
+# Copyright (C) 2007 Red Hat, Inc.
+#
+# See COPYING for the license of this software.
+#
+# Richard Jones <rjones redhat com>
+
+while (<>)
+{
+ if (m/^struct remote_name \{/) {
+ print "extern void libvirtremote_1 (struct svc_req *rqstp, register SVCXPRT *transp);\n";
+ }
+ print;
+}
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/remote_rpc.x libvirt-oldremote/src/remote_rpc.x
--- libvirt-cvs/src/remote_rpc.x 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/remote_rpc_xdr_fix.pl libvirt-oldremote/src/remote_rpc_xdr_fix.pl
--- libvirt-cvs/src/remote_rpc_xdr_fix.pl 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/src/remote_rpc_xdr_fix.pl 2007-03-02 12:25:53.000000000 +0000
@@ -0,0 +1,64 @@
+# Fix remote_rpc_xdr.c (generated by rpcgen) so that it compiles
+# with warnings turned on.
+#
+# This code is evil. Arguably better would be just to compile
+# remote_rpc_xdr.c without -Werror.
+#
+# Copyright (C) 2007 Red Hat, Inc.
+#
+# See COPYING for the license of this software.
+#
+# Richard Jones <rjones redhat com>
+
+use strict;
+
+my $in_function = 0;
+my @function = ();
+
+while (<>) {
+ if (m/^{/) {
+ $in_function = 1;
+ print;
+ next;
+ }
+
+ if (m/^}/) {
+ $in_function = 0;
+
+ # Note: The body of the function is in @function.
+
+ # Remove decl of buf, if buf isn't used in the function.
+ my @uses = grep /\bbuf\b/, @function;
+ @function = grep !/\bbuf\b/, @function if @uses == 1;
+
+ # Remove decl of i, if i isn't used in the function.
+ @uses = grep /\bi\b/, @function;
+ @function = grep !/\bi\b/, @function if @uses == 1;
+
+ # (char **)&objp->... gives:
+ # warning: dereferencing type-punned pointer will break
+ # strict-aliasing rules
+ # so rewrite it.
+ @uses = grep /\(char \*\*\)\&objp->remote_listDomains_ret_u\.ids\.ids_val/, @function;
+ if (@uses >= 1) {
+ unshift @function,
+ ("\tchar *objp_cp = (char *) objp->remote_listDomains_ret_u.ids.ids_val;\n",
+ "\tchar **objp_cpp = &objp_cp;\n");
+ @function =
+ map { s{\(char \*\*\)\&objp->remote_listDomains_ret_u\.ids\.ids_val}{objp_cpp}g; $_ } @function;
+ }
+
+ # The code uses 'IXDR_PUT_LONG' but in a way where the statements
+ # cannot have any effect (it's not really clear why). Remove them.
+ @function = grep !/\bIXDR_PUT_LONG\b/, @function;
+
+ print (join ("", @function));
+ @function = ();
+ }
+
+ unless ($in_function) {
+ print;
+ } else {
+ push @function, $_;
+ }
+}
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/clnt_ext.c libvirt-oldremote/src/sunrpc/clnt_ext.c
--- libvirt-cvs/src/sunrpc/clnt_ext.c 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/src/sunrpc/clnt_ext.c 2007-04-04 13:49:38.000000000 +0100
@@ -0,0 +1,644 @@
+/*
+ * 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"
+#include "create_xid.h"
+
+#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 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);
+ if (dup (sv[1]) == -1) perror ("dup");
+ close (1);
+ if (dup (sv[1]) == -1) perror ("dup");
+ 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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/clnt_ext.h libvirt-oldremote/src/sunrpc/clnt_ext.h
--- libvirt-cvs/src/sunrpc/clnt_ext.h 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/clnt_gnutls.c libvirt-oldremote/src/sunrpc/clnt_gnutls.c
--- libvirt-cvs/src/sunrpc/clnt_gnutls.c 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/src/sunrpc/clnt_gnutls.c 2007-03-02 11:38:06.000000000 +0000
@@ -0,0 +1,717 @@
+/*
+ * 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"
+#include "create_xid.h"
+
+#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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/clnt_gnutls.h libvirt-oldremote/src/sunrpc/clnt_gnutls.h
--- libvirt-cvs/src/sunrpc/clnt_gnutls.h 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/clnt_tcp2.c libvirt-oldremote/src/sunrpc/clnt_tcp2.c
--- libvirt-cvs/src/sunrpc/clnt_tcp2.c 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/src/sunrpc/clnt_tcp2.c 2007-03-02 11:38:13.000000000 +0000
@@ -0,0 +1,543 @@
+/*
+ * 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"
+#include "create_xid.h"
+
+#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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/clnt_tcp2.h libvirt-oldremote/src/sunrpc/clnt_tcp2.h
--- libvirt-cvs/src/sunrpc/clnt_tcp2.h 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/create_xid.c libvirt-oldremote/src/sunrpc/create_xid.c
--- libvirt-cvs/src/sunrpc/create_xid.c 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/src/sunrpc/create_xid.c 2007-03-02 11:37:51.000000000 +0000
@@ -0,0 +1,65 @@
+/*
+ * 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"
+#include "create_xid.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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/create_xid.h libvirt-oldremote/src/sunrpc/create_xid.h
--- libvirt-cvs/src/sunrpc/create_xid.h 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/src/sunrpc/create_xid.h 2007-03-02 11:37:34.000000000 +0000
@@ -0,0 +1,16 @@
+/*
+ * create_xid.h: Unique, unguessable identifiers.
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * See COPYING.LIB for the license of this software.
+ *
+ * Richard Jones <rjones redhat com>
+ */
+
+#ifndef __CREATE_XID_H__
+#define __CREATE_XID_H__
+
+unsigned long _create_xid (void);
+
+#endif /* __CREATE_XID_H__ */
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/README libvirt-oldremote/src/sunrpc/README
--- libvirt-cvs/src/sunrpc/README 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/svc_connections.h libvirt-oldremote/src/sunrpc/svc_connections.h
--- libvirt-cvs/src/sunrpc/svc_connections.h 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/svc_gnutls.c libvirt-oldremote/src/sunrpc/svc_gnutls.c
--- libvirt-cvs/src/sunrpc/svc_gnutls.c 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/svc_gnutls.h libvirt-oldremote/src/sunrpc/svc_gnutls.h
--- libvirt-cvs/src/sunrpc/svc_gnutls.h 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/svc_tcp2.c libvirt-oldremote/src/sunrpc/svc_tcp2.c
--- libvirt-cvs/src/sunrpc/svc_tcp2.c 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/svc_tcp2.h libvirt-oldremote/src/sunrpc/svc_tcp2.h
--- libvirt-cvs/src/sunrpc/svc_tcp2.h 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/svc_unix2.c libvirt-oldremote/src/sunrpc/svc_unix2.c
--- libvirt-cvs/src/sunrpc/svc_unix2.c 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/sunrpc/svc_unix2.h libvirt-oldremote/src/sunrpc/svc_unix2.h
--- libvirt-cvs/src/sunrpc/svc_unix2.h 1970-01-01 01:00:00.000000000 +0100
+++ libvirt-oldremote/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 --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/virterror.c libvirt-oldremote/src/virterror.c
--- libvirt-cvs/src/virterror.c 2007-03-16 11:04:46.000000000 +0000
+++ libvirt-oldremote/src/virterror.c 2007-04-04 12:54:06.000000000 +0100
@@ -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;
@@ -610,6 +613,18 @@
else
errmsg = "%s";
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