[Libvir] Preliminary patch to support remote driver / libvirtd
by Richard W.M. Jones
This patch is just for discussion. It's not in a state to be applied,
even if it were accepted (which is a long-shot at present anyway).
When looking at the patch, a good starting point is to search for
"Architecture and notes" and read from there.
Supports:
* remote driver (just does the "open", "close", "type" and
"version" calls at present as a proof of concept)
* TLS transport (built using GnuTLS)
* SSH transport (forks external ssh process)
* TCP transport (unencrypted - just for testing)
* Unix domain socket transport
* arbitrary external program / shell script transport
* IPv6-ready on client & server
I've tested all the transports, and in limited tests they all
seem to work. ie. You really can do:
virsh -c remote:tls:server version
Shortcomings in this version:
* in "open" call, name must be non-NULL (this is just a bug)
* doesn't actually invoke libvirt on the server side; just
prints out messages and returns dummy values
* "ssh" not recognised as a service name by getaddrinfo, so
you must always give a port number, ie. remote:ssh:server:22
* /tmp/socket should be cleaned up when the server exits
Potential problems:
* SunRPC is stateless so we need to hand out a cookie to
represent the virConnectPtr handle on the server side.
However if the client dies without explicitly calling
close, we have no way to know, and so the cookie/handle
on the server side lives forever.
* There's some confusion about the level of abstraction. At
the moment I'm abstracting at the driver level, but that may
be wrong and possibly I should be abstracting at the level
of vir* calls. On the other hand, there's not a huge amount
of difference.
* Security:
Is it safe for libvirt to be connecting to arbitrary TCP
sockets?
Is it safe for libvirt to be able to run arbitrary programs?
Rich.
--
Emerging Technologies, Red Hat http://et.redhat.com/~rjones/
64 Baker Street, London, W1U 7DF Mobile: +44 7866 314 421
"[Negative numbers] darken the very whole doctrines of the equations
and make dark of the things which are in their nature excessively
obvious and simple" (Francis Maseres FRS, mathematician, 1759)
? src/libvirtd.c
? src/remote_internal.c
? src/remote_internal.h
? src/remote_rpc.x
? src/sunrpc/README
? src/sunrpc/clnt_ext.c
? src/sunrpc/clnt_ext.h
? src/sunrpc/clnt_gnutls.c
? src/sunrpc/clnt_gnutls.h
? src/sunrpc/clnt_tcp2.c
? src/sunrpc/clnt_tcp2.h
? src/sunrpc/create_xid.c
? src/sunrpc/svc_gnutls.c
? src/sunrpc/svc_gnutls.h
? src/sunrpc/svc_tcp2.c
? src/sunrpc/svc_tcp2.h
Index: config.h.in
===================================================================
RCS file: /data/cvs/libvirt/config.h.in,v
retrieving revision 1.7
diff -u -r1.7 config.h.in
--- config.h.in 31 Oct 2006 10:25:13 -0000 1.7
+++ config.h.in 30 Jan 2007 17:22:07 -0000
@@ -28,6 +28,9 @@
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
+/* Define to 1 if you have the `gnutls' library (-lgnutls). */
+#undef HAVE_LIBGNUTLS
+
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
Index: configure.in
===================================================================
RCS file: /data/cvs/libvirt/configure.in,v
retrieving revision 1.52
diff -u -r1.52 configure.in
--- configure.in 22 Jan 2007 15:31:00 -0000 1.52
+++ configure.in 30 Jan 2007 17:22:07 -0000
@@ -146,6 +146,11 @@
AC_SUBST(LIBXML_CONFIG)
AC_SUBST(LIBXML_MIN_VERSION)
+dnl GnuTLS library
+AC_CHECK_LIB(gnutls, gnutls_handshake,
+ [],
+ [AC_MSG_ERROR([gnutls library not found])])
+
dnl virsh libraries
AC_CHECK_LIB(curses, initscr,
[VIRSH_LIBS="$VIRSH_LIBS -lcurses"],
Index: include/libvirt/virterror.h
===================================================================
RCS file: /data/cvs/libvirt/include/libvirt/virterror.h,v
retrieving revision 1.17
diff -u -r1.17 virterror.h
--- include/libvirt/virterror.h 8 Nov 2006 16:55:20 -0000 1.17
+++ include/libvirt/virterror.h 30 Jan 2007 17:22:07 -0000
@@ -46,7 +46,8 @@
VIR_FROM_DOM, /* Error when operating on a domain */
VIR_FROM_RPC, /* Error in the XML-RPC code */
VIR_FROM_PROXY, /* Error in the proxy code */
- VIR_FROM_CONF /* Error in the configuration file handling */
+ VIR_FROM_CONF, /* Error in the configuration file handling */
+ VIR_FROM_REMOTE /* Error from remote driver */
} virErrorDomain;
@@ -113,7 +114,8 @@
VIR_ERR_PARSE_FAILED, /* failed to parse a conf file */
VIR_ERR_CONF_SYNTAX, /* failed to parse the syntax of a conf file */
VIR_ERR_WRITE_FAILED, /* failed to write a conf file */
- VIR_ERR_XML_DETAIL /* detail of an XML error */
+ VIR_ERR_XML_DETAIL, /* detail of an XML error */
+ VIR_ERR_RPC /* some sort of RPC error */
} virErrorNumber;
/**
Index: src/.cvsignore
===================================================================
RCS file: /data/cvs/libvirt/src/.cvsignore,v
retrieving revision 1.2
diff -u -r1.2 .cvsignore
--- src/.cvsignore 5 Jul 2006 21:52:52 -0000 1.2
+++ src/.cvsignore 30 Jan 2007 17:22:07 -0000
@@ -5,3 +5,8 @@
*.lo
*.la
virsh
+libvirtd
+remote_rpc_clnt.c
+remote_rpc_svc.c
+remote_rpc_xdr.c
+remote_rpc.h
\ No newline at end of file
Index: src/Makefile.am
===================================================================
RCS file: /data/cvs/libvirt/src/Makefile.am,v
retrieving revision 1.31
diff -u -r1.31 Makefile.am
--- src/Makefile.am 26 Jan 2007 11:54:29 -0000 1.31
+++ src/Makefile.am 30 Jan 2007 17:22:07 -0000
@@ -28,15 +28,41 @@
driver.h \
proxy_internal.c proxy_internal.h \
conf.c conf.h \
- xm_internal.c xm_internal.h
+ xm_internal.c xm_internal.h \
+ sunrpc/create_xid.c remote_rpc_xdr.c \
+ remote_rpc_clnt.c remote_rpc.h \
+ sunrpc/clnt_ext.h sunrpc/clnt_gnutls.h \
+ sunrpc/clnt_tcp2.h sunrpc/clnt_ext.c \
+ sunrpc/clnt_gnutls.c sunrpc/clnt_tcp2.c \
+ remote_internal.c remote_internal.h
bin_PROGRAMS = virsh
+sbin_PROGRAMS = libvirtd
virsh_SOURCES = virsh.c console.c console.h
virsh_LDFLAGS =
virsh_DEPENDENCIES = $(DEPS)
virsh_LDADD = $(LDADDS) $(VIRSH_LIBS)
+libvirtd_SOURCES = \
+ remote_rpc_svc.c \
+ sunrpc/svc_gnutls.c sunrpc/svc_gnutls.h \
+ sunrpc/svc_tcp2.c sunrpc/svc_tcp2.h \
+ libvirtd.c
+libvirtd_LDFLAGS =
+libvirtd_DEPENDENCIES = $(DEPS)
+libvirtd_LDADD = $(LDADDS) remote_rpc_xdr.o
+
+# Build client and server stubs.
+# 'rpcgen' program comes with glibc.
+# This is convoluted because we need to build the server stubs
+# without main() (-m option), but you can't just do that in a
+# simple way.
+remote_rpc_clnt.c remote_rpc.h remote_rpc_svc.c remote_rpc_xdr.c: remote_rpc.x
+ rpcgen remote_rpc.x
+ rm -f remote_rpc_svc.c
+ rpcgen -m remote_rpc.x > remote_rpc_svc.c
+
#
# target to ease building test programs
#
Index: src/driver.h
===================================================================
RCS file: /data/cvs/libvirt/src/driver.h,v
retrieving revision 1.16
diff -u -r1.16 driver.h
--- src/driver.h 22 Jan 2007 16:21:27 -0000 1.16
+++ src/driver.h 30 Jan 2007 17:22:07 -0000
@@ -22,7 +22,8 @@
VIR_DRV_XEN_DAEMON = 3,
VIR_DRV_TEST = 4,
VIR_DRV_XEN_PROXY = 5,
- VIR_DRV_XEN_XM = 6
+ VIR_DRV_XEN_XM = 6,
+ VIR_DRV_REMOTE = 7
} virDrvNo;
Index: src/internal.h
===================================================================
RCS file: /data/cvs/libvirt/src/internal.h,v
retrieving revision 1.28
diff -u -r1.28 internal.h
--- src/internal.h 23 Jan 2007 14:39:45 -0000 1.28
+++ src/internal.h 30 Jan 2007 17:22:07 -0000
@@ -117,6 +117,13 @@
struct sockaddr_un addr_un; /* the unix address */
struct sockaddr_in addr_in; /* the inet address */
+ /* driver private data
+ * (Ought to replace the above ad-hoc Xen data, IMHO anyway.
+ * Currently only the 'remote' driver uses this.
+ * - RWMJ).
+ */
+ void *private;
+
/* error stuff */
virError err; /* the last error */
virErrorFunc handler; /* associated handlet */
Index: src/libvirt.c
===================================================================
RCS file: /data/cvs/libvirt/src/libvirt.c,v
retrieving revision 1.53
diff -u -r1.53 libvirt.c
--- src/libvirt.c 23 Jan 2007 14:39:45 -0000 1.53
+++ src/libvirt.c 30 Jan 2007 17:22:09 -0000
@@ -30,6 +30,7 @@
#include "xs_internal.h"
#include "xm_internal.h"
#include "proxy_internal.h"
+#include "remote_internal.h"
#include "xml.h"
#include "test.h"
@@ -79,6 +80,7 @@
xenStoreRegister();
xenXMRegister();
testRegister();
+ remoteRegister ();
return(0);
}
Index: src/virsh.c
===================================================================
RCS file: /data/cvs/libvirt/src/virsh.c,v
retrieving revision 1.47
diff -u -r1.47 virsh.c
--- src/virsh.c 28 Jan 2007 19:47:36 -0000 1.47
+++ src/virsh.c 30 Jan 2007 17:22:10 -0000
@@ -2760,7 +2760,7 @@
end = end ? : argc;
/* standard (non-command) options */
- while ((arg = getopt_long(end, argv, "d:hqtcv", opt, &idx)) != -1) {
+ while ((arg = getopt_long(end, argv, "d:hqtc:v", opt, &idx)) != -1) {
switch (arg) {
case 'd':
ctl->debug = atoi(optarg);
Index: src/virterror.c
===================================================================
RCS file: /data/cvs/libvirt/src/virterror.c,v
retrieving revision 1.19
diff -u -r1.19 virterror.c
--- src/virterror.c 8 Nov 2006 16:55:20 -0000 1.19
+++ src/virterror.c 30 Jan 2007 17:22:10 -0000
@@ -268,6 +268,9 @@
case VIR_FROM_RPC:
dom = "XML-RPC ";
break;
+ case VIR_FROM_REMOTE:
+ dom = "Remote ";
+ break;
}
if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) {
domain = err->dom->name;
@@ -582,6 +585,12 @@
else
errmsg = "%s";
break;
+ case VIR_ERR_RPC:
+ if (info == NULL)
+ errmsg = _("RPC error");
+ else
+ errmsg = "%s";
+ break;
}
return (errmsg);
}
--- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000
+++ src/libvirtd.c 2007-01-30 16:58:42.000000000 +0000
@@ -0,0 +1,257 @@
+/*
+ * libvirtd: This is a small server to be used in conjunction with
+ * the "remote" driver (remote_internal.c).
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * See COPYING.LIB for the license of this software.
+ *
+ * Richard Jones <rjones(a)redhat.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <time.h>
+#include <rpc/rpc.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <errno.h>
+#include <gnutls/gnutls.h>
+
+#include "sunrpc/svc_gnutls.h"
+#include "sunrpc/svc_tcp2.h"
+#include "remote_internal.h"
+#include "remote_rpc.h"
+
+// XXX Replace with variables from a configuration file
+// XXX TCP services should be disabled by default
+#define listen_tls 1
+#define listen_tcp 1
+#define listen_unix 1
+
+// XXX Still to decide where these certificates should be located.
+#define KEYFILE "newkey.pem"
+#define CERTFILE "newcert.pem"
+#define CAFILE "demoCA/cacert.pem"
+//#define CRLFILE "crl.pem"
+
+// This is autogenerated, in remote_rpc_svc.c
+extern void libvirtremote_1 (struct svc_req *rqstp, register SVCXPRT *transp);
+
+/*----------------------------------------------------------------------*/
+/* Server side of the remote procedure calls. */
+
+int *
+remote_rpc_open_1_svc (char **name,
+ struct svc_req *req __attribute__((unused)))
+{
+ static int retcode = 0;
+
+ // Before this is going to work, we will have to assign a cookie
+ // to each caller to represent their server-side virConnectPtr.
+ // XXX XXX XXX
+ //virConnectOpen (name);
+
+ printf ("libvirtd: open: name = %s\n", *name);
+ return &retcode;
+}
+
+int *
+remote_rpc_close_1_svc (void *p __attribute__((unused)),
+ struct svc_req *req __attribute__((unused)))
+{
+ static int retcode = 0;
+
+ printf ("libvirtd: close\n");
+ return &retcode;
+}
+
+char **
+remote_rpc_type_1_svc (void *p __attribute__((unused)),
+ struct svc_req *req __attribute__((unused)))
+{
+ static char *dummy = "remote";
+
+ printf ("libvirtd: type\n");
+ return &dummy;
+}
+
+struct version_ret *
+remote_rpc_version_1_svc (void *p __attribute__((unused)),
+ struct svc_req *req __attribute__((unused)))
+{
+ static struct version_ret ret = { .retcode = 0, .hvVer = 1000000 };
+
+ printf ("libvirtd: version\n");
+ return &ret;
+}
+
+/*----------------------------------------------------------------------*/
+/* Main function. */
+
+static gnutls_certificate_credentials_t x509_cred;
+static gnutls_dh_params_t dh_params;
+
+static void generate_dh_params (void);
+static int make_sockets (int *fds, int max_fds, int *nfds_r,
+ const char *service);
+
+int
+main (int argc, char *argv[])
+{
+ if (!listen_tls || !listen_tcp || !listen_unix) {
+ fprintf (stderr, "libvirtd: you need to enable at least one service in the configuration file\n");
+ exit (1);
+ }
+
+ if (listen_tls) {
+ /* Initialise GnuTLS. */
+ gnutls_global_init ();
+
+ gnutls_certificate_allocate_credentials (&x509_cred);
+ gnutls_certificate_set_x509_trust_file (x509_cred, CAFILE,
+ GNUTLS_X509_FMT_PEM);
+
+ // gnutls_certificate_set_x509_crl_file (x509_cred, CRLFILE,
+ // GNUTLS_X509_FMT_PEM);
+
+ gnutls_certificate_set_x509_key_file (x509_cred, CERTFILE, KEYFILE,
+ GNUTLS_X509_FMT_PEM);
+
+ generate_dh_params ();
+ gnutls_certificate_set_dh_params (x509_cred, dh_params);
+
+ int fds[2];
+ int nfds = 0;
+ if (make_sockets (fds, 2, &nfds, LIBVIRTD_GNUTLS_PORT) == -1)
+ exit (1);
+
+ int i;
+ for (i = 0; i < nfds; ++i) {
+ SVCXPRT *transp = svcgnutls_create (fds[i], 0, 0, x509_cred);
+ if (!transp) {
+ fprintf (stderr, "libvirtd: cannot create TLS service\n");
+ exit (1);
+ }
+
+ /* Because final arg is 0, this will not register with portmap. */
+ if (!svc_register (transp, LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1,
+ libvirtremote_1, 0)) {
+ fprintf (stderr, "libvirtd: unable to register (LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, 0)\n");
+ exit (1);
+ }
+ }
+ }
+
+ if (listen_tcp) {
+ int fds[2];
+ int nfds = 0;
+ if (make_sockets (fds, 2, &nfds, LIBVIRTD_TCP_PORT) == -1)
+ exit (1);
+
+ int i;
+ for (i = 0; i < nfds; ++i) {
+ SVCXPRT *transp = svctcp2_create (fds[i], 0, 0);
+ if (!transp) {
+ fprintf (stderr, "libvirtd: cannot create TCP service\n");
+ exit (1);
+ }
+
+ /* Because final arg is 0, this will not register with portmap. */
+ if (!svc_register (transp, LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1,
+ libvirtremote_1, 0)) {
+ fprintf (stderr, "libvirtd: unable to register (LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, 0)\n");
+ exit (1);
+ }
+ }
+ }
+
+ if (listen_unix) {
+ char *sockname = LIBVIRTD_UNIX_SOCKET;
+ int sock = RPC_ANYSOCK;
+ SVCXPRT *transp = svcunix_create (sock, 0, 0, sockname);
+ if (!transp) {
+ fprintf (stderr, "libvirtd: cannot create Unix domain socket service\n");
+ exit (1);
+ }
+
+ /* Because final arg is 0, this will not register with portmap. */
+ if (!svc_register (transp, LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1,
+ libvirtremote_1, 0)) {
+ fprintf (stderr, "libvirtd: unable to register (LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1, 0)\n");
+ exit (1);
+ }
+ }
+
+ svc_run ();
+ fprintf (stderr, "libvirtd: svc_run should not return\n");
+ exit (1);
+}
+
+// XXX DH_BITS has to match the value define in svc_gnutls.c
+#define DH_BITS 1024
+
+static void
+generate_dh_params (void)
+{
+ /* Generate Diffie Hellman parameters - for use with DHE
+ * kx algorithms. These should be discarded and regenerated
+ * once a day, once a week or once a month. Depending on the
+ * security requirements.
+ */
+ gnutls_dh_params_init (&dh_params);
+ gnutls_dh_params_generate2 (dh_params, DH_BITS);
+}
+
+// See: http://people.redhat.com/drepper/userapi-ipv6.html
+static int
+make_sockets (int *fds, int max_fds, int *nfds_r, const char *service)
+{
+ struct addrinfo *ai;
+ struct addrinfo hints;
+ memset (&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+ hints.ai_socktype = SOCK_STREAM;
+
+ int e = getaddrinfo (NULL, service, &hints, &ai);
+ if (e != 0) {
+ fprintf (stderr, "getaddrinfo: %s\n", gai_strerror (e));
+ return -1;
+ }
+
+ struct addrinfo *runp = ai;
+ while (runp && *nfds_r < max_fds) {
+ fds[*nfds_r] = socket (runp->ai_family, runp->ai_socktype,
+ runp->ai_protocol);
+ if (fds[*nfds_r] == -1) {
+ perror ("socket");
+ return -1;
+ }
+
+ int opt = 1;
+ setsockopt (fds[*nfds_r], SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt);
+
+ if (bind (fds[*nfds_r], runp->ai_addr, runp->ai_addrlen) == -1) {
+ if (errno != EADDRINUSE) {
+ perror ("bind");
+ return -1;
+ }
+ close (fds[*nfds_r]);
+ }
+ else {
+ if (listen (fds[*nfds_r], SOMAXCONN) == -1) {
+ perror ("listen");
+ return -1;
+ }
+ ++*nfds_r;
+ }
+ runp = runp->ai_next;
+ }
+
+ freeaddrinfo (ai);
+
+ return 0;
+}
--- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000
+++ src/remote_internal.c 2007-01-30 16:31:59.000000000 +0000
@@ -0,0 +1,663 @@
+/*
+ * remote_internal.c: driver to provide access to libvirtd running
+ * on a remote machine.
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * See COPYING.LIB for the license of this software.
+ *
+ * Richard Jones <rjones(a)redhat.com>
+ */
+
+/* Architecture and notes:
+ *
+ * virConnectOpen ("remote:....") invokes this driver. Depending
+ * on the exact contents of the ellipsis "...." in the name string
+ * we will try some method to connect to a libvirtd daemon, running
+ * on a remote machine (or sometimes running on the local machine).
+ *
+ * All other vir* calls made on this connection are forwarded
+ * to the libvirtd daemon which carries out the requested action.
+ * So for example if you call virDomainCreateLinux, then the
+ * domain gets created on the remote machine, and virConnectListDomains
+ * lists domains running on the remote machine.
+ *
+ * Connections can be authenticated and encrypted -- it depends
+ * on the transport selected by the name string.
+ *
+ * The current implementation uses SunRPC layered over one of:
+ * - GnuTLS (an SSL/TLS library providing enterprise-level
+ * authentication and encryption)
+ * - a local Unix domain socket
+ * - ssh or another external program such as rsh
+ * - a plain TCP socket (unencrypted, not recommended for production)
+ *
+ * See http://et.redhat.com/~rjones/secure_rpc for an insight into
+ * the thinking that went into the selection of SunRPC. In
+ * the future we may use a different RPC system - for example
+ * XML-RPC would be a logical choice - so for now you should regard
+ * the protocol used as private and subject to change in future
+ * versions of libvirt without notice.
+ *
+ * The name string selects the transport to use and the type of
+ * virtualisation at the remote end. The general format is:
+ *
+ * "remote:<protocol>:<path> var=value var=value ..."
+ *
+ * Some examples:
+ *
+ * "remote:unix:/var/run/libvirtd/socket"
+ * "remote:tls:myxenserver"
+ * "remote:ssh:myserver name=qemud"
+ * "remote:ssh:myserver command=/opt/openssh/bin/ssh"
+ *
+ * The <protocol> is one of: tls, unix, ssh, ext or tcp.
+ * The <path> is protocol specific:
+ *
+ * Protocol Path-format
+ * -----------------------------------------
+ * tls hostname[:port]
+ * unix Path to local socket
+ * ssh hostname[:port]
+ * ext Name or path of external program
+ * tcp hostname[:port]
+ *
+ * For tls, the default port is 16514. For tcp, the default port is
+ * 16509 (but note that tcp is almost never enabled because it is
+ * insecure - it's only there for testing).
+ *
+ * For ssh: The default port for ssh is 22. You should configure ssh
+ * so that it doesn't ask for a password (eg. using ssh-agent). The
+ * remote server should have a recent version of the the netcat program
+ * installed as 'nc', and the remote libvirtd must be configured to
+ * listen on a Unix domain socket. The following full command is run:
+ * ssh -p $port $hostname nc -U /var/run/libvirtd/socket
+ *
+ * For ext: Only the command you specify is run. It is up to you to
+ * write this command so that it somehow makes a connection to a
+ * remote libvirtd, and passes input and output over its stdin/stdout.
+ *
+ * The var=value pairs provide optional extra information:
+ *
+ * Variable Protocols Meaning
+ * -----------------------------------------
+ * name (all) Name used in remote virConnectOpen
+ * (default is NULL).
+ * command ssh Name or path of external program (instead
+ * of "ssh").
+ *
+ * The value is %-escaped (just like URL encoding), so if you want it
+ * to contain a literal space use "%20" or "+", if you want it to have
+ * a literal + character use "%2b", and for a literal % character use "%25".
+ *
+ * To provide some forwards compatibility, variables which are not
+ * understood are ignored (but a warning is printed on stderr).
+ *
+ * Several shorthand syntaxes are available:
+ *
+ * "remote:/var/run/libvirtd/socket" connect to Unix domain socket
+ * "remote://server" connect to TLS socket on server
+ * "remote://server:9000" connect to TLS server port 9000
+ *
+ * For the details of the implementation of SunRPC over GnuTLS, etc.
+ * please see http://et.redhat.com/~rjones/secure_rpc which contains
+ * simple code samples which will allow you to understand what's
+ * going on here.
+ *
+ * - Richard Jones <rjones(a)redhat.com>
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <rpc/rpc.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netdb.h>
+#include <assert.h>
+#include <gnutls/gnutls.h>
+
+#include "sunrpc/clnt_ext.h"
+#include "sunrpc/clnt_gnutls.h"
+#include "sunrpc/clnt_tcp2.h"
+
+#include "internal.h"
+#include "driver.h"
+#include "remote_internal.h"
+#include "remote_rpc.h"
+
+#define VERSION 1 /* Doesn't really mean anything. */
+#define DEBUG 1 /* Enable verbose messages on stderr. */
+
+#define MAGIC 999 /* private_data->magic if OK */
+#define DEAD 998 /* private_data->magic if dead/closed */
+
+struct private_data {
+ int magic; /* Should be MAGIC or DEAD. */
+ int sock; /* Socket. */
+ CLIENT *cl; /* SunRPC client. */
+};
+
+#define GET_PRIVATE(conn,retcode) \
+ struct private_data *private = (struct private_data *) (conn)->private; \
+ assert (private); \
+ if (private->magic == DEAD) { \
+ error (conn, VIR_ERR_INVALID_ARG, \
+ "tried to use a closed or uninitialised handle"); \
+ return (retcode); \
+ } \
+ assert (private->magic == MAGIC)
+
+#define CAFILE "demoCA/cacert.pem" /* XXX */
+
+static gnutls_certificate_credentials_t xcred;
+
+static void
+initialise_gnutls (void)
+{
+ static int initialised = 0;
+
+ if (initialised) return;
+
+ gnutls_global_init ();
+
+ /* X509 stuff */
+ gnutls_certificate_allocate_credentials (&xcred);
+
+ /* sets the trusted cas file
+ */
+ gnutls_certificate_set_x509_trust_file (xcred, CAFILE, GNUTLS_X509_FMT_PEM);
+
+ initialised = 1;
+}
+
+enum protocol {
+ proto_unknown,
+ proto_tls,
+ proto_unix,
+ proto_ssh,
+ proto_ext,
+ proto_tcp
+};
+
+#define whitespace " \t\n"
+
+static char *unescape (const char *);
+static int parse_hostname_port (const char *, char **hostname_r,
+ char **port_r, const char *default_port);
+static void error (virConnectPtr conn, virErrorNumber code, const char *info);
+
+static int
+remote_open (virConnectPtr conn, const char *name,
+ int flags __attribute__((unused)))
+{
+ int retcode = -1; /* Return code from this function. */
+ enum protocol proto = proto_unknown;
+ char *path = 0; /* or hostname */
+ char *port = 0;
+ char *remote_name = 0; /* Name to use at remote end of connection. */
+ char *command = 0; /* External command. */
+ char **cmd_argv = 0; /* External command argv[] array. */
+ struct private_data private = { .magic = DEAD };
+ /* Private data - copied to conn->private at the
+ * end of this function.
+ */
+
+ initialise_gnutls ();
+
+ if (strncasecmp (name, "remote:", 7) != 0)
+ return -1; /* Not for me. */
+
+ /* Split the name at whitespace and parse it. */
+ const char *p;
+ for (p = name; *p;) {
+ size_t len = strcspn (p, whitespace);
+ char *token = strndup (p, len);
+#if DEBUG
+ fprintf (stderr, "token = %s\n", token);
+#endif
+ if (p == name) {
+ /* First parameter is remote:protocol:path or a shortcut. */
+ if (strncasecmp (token, "remote://", 9) == 0) {
+ proto = proto_tls;
+ if (parse_hostname_port (token+9, &path, &port, LIBVIRTD_GNUTLS_PORT)
+ == -1) {
+ error (conn, VIR_ERR_INVALID_ARG,
+ "remote_open: cannot parse port number");
+ goto failed;
+ }
+ } else if (strncasecmp (token, "remote:/", 8) == 0) {
+ proto = proto_unix;
+ path = strdup (token+7); // include the initial slash
+ } else if (strncasecmp (token, "remote:tls:", 11) == 0) {
+ proto = proto_tls;
+ if (parse_hostname_port (token+11, &path, &port, LIBVIRTD_GNUTLS_PORT)
+ == -1) {
+ error (conn, VIR_ERR_INVALID_ARG,
+ "remote_open: cannot parse port number");
+ goto failed;
+ }
+ } else if (strncasecmp (token, "remote:unix:", 12) == 0) {
+ proto = proto_unix;
+ path = strdup (token+12);
+ } else if (strncasecmp (token, "remote:ssh:", 11) == 0) {
+ proto = proto_ssh;
+ if (parse_hostname_port (token+11, &path, &port, "ssh") == -1) {
+ error (conn, VIR_ERR_INVALID_ARG,
+ "remote_open: cannot parse port number");
+ goto failed;
+ }
+ command = strdup ("ssh");
+ } else if (strncasecmp (token, "remote:ext:", 11) == 0) {
+ proto = proto_ext;
+ command = strdup (token+11);
+ } else if (strncasecmp (token, "remote:tcp:", 11) == 0) {
+ proto = proto_tcp;
+ if (parse_hostname_port (token+11, &path, &port, LIBVIRTD_TCP_PORT)
+ == -1) {
+ error (conn, VIR_ERR_INVALID_ARG,
+ "remote_open: cannot parse port number");
+ goto failed;
+ }
+ } else {
+ error (conn, VIR_ERR_INVALID_ARG,
+ "remote_open: expecting 'remote:protocol:path' - please read the manual for the remote driver / libvirtd");
+ goto failed;
+ }
+ } else {
+ /* Variable=value. Value is URL-escaped. */
+ char *value = strchr (token, '=');
+ if (!value) {
+ error (conn, VIR_ERR_INVALID_ARG,
+ "remote_open: expecting 'variable=value'");
+ goto failed;
+ }
+ *value++ = '\0'; /* So now token = variable, value = value. */
+ /* Unescape value - this always makes a copy. */
+ value = unescape (value);
+
+ if (strcasecmp (token, "name") == 0) {
+ if (remote_name) {
+ free (remote_name);
+ fprintf (stderr, "libvir: warning: multiple name parameters. All but final will be ignored.\n");
+ }
+ remote_name = value;
+ } else if (strcasecmp (token, "command") == 0) {
+ if (command) free (command);
+ command = value;
+ } else
+ /* For forwards compatibility, just warn about variables we
+ * don't understand.
+ */
+ fprintf (stderr, "libvir: warning: variable '%s' ignored\n", token);
+ }
+
+ free (token);
+
+ /* Skip to next token. */
+ p += len;
+ p += strspn (p, whitespace);
+ }
+
+ /* Connect to the remote service. */
+ switch (proto) {
+ case proto_unknown:
+ abort (); /* Internal error in this function. */
+
+ case proto_tls:
+ case proto_tcp: {
+ // http://people.redhat.com/drepper/userapi-ipv6.html
+ struct addrinfo *res, *r;
+ struct addrinfo hints;
+ memset (&hints, 0, sizeof hints);
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_ADDRCONFIG;
+ int e = getaddrinfo (path, port, &hints, &res);
+ if (e != 0) {
+ error (conn, VIR_ERR_INVALID_ARG, gai_strerror (e));
+ goto failed;
+ }
+
+ /* Try to connect to each returned address in turn. */
+ for (r = res; r; r = r->ai_next) {
+ private.sock = RPC_ANYSOCK;
+ private.cl =
+ proto == proto_tls
+ ? clntgnutls_create (r->ai_family, r->ai_addr, r->ai_addrlen, xcred,
+ LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1,
+ &private.sock, 0, 0)
+ : clnttcp2_create (r->ai_family, r->ai_addr, r->ai_addrlen,
+ LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1,
+ &private.sock, 0, 0);
+ if (private.cl)
+ goto tcp_connected;
+ }
+
+ freeaddrinfo (res);
+ error (conn, VIR_ERR_RPC,
+ clnt_spcreateerror ("could not create SunRPC client"));
+ goto failed;
+
+ tcp_connected:
+ freeaddrinfo (res);
+
+ // NB. All versioning is done by SunRPC so we don't need to worry
+ // that we are connected to an incompatible daemon.
+ break;
+ }
+
+ case proto_unix: {
+ // 108 is hard-coded into the header files as well.
+#define UNIX_PATH_MAX 108
+ struct sockaddr_un addr;
+ memset (&addr, 0, sizeof addr);
+ addr.sun_family = AF_UNIX;
+ strncpy (addr.sun_path, path, UNIX_PATH_MAX);
+
+ private.sock = RPC_ANYSOCK;
+ private.cl =
+ clntunix_create (&addr,
+ LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1,
+ &private.sock, 0, 0);
+ if (!private.cl) {
+ error (conn, VIR_ERR_RPC,
+ clnt_spcreateerror ("could not create SunRPC client"));
+ goto failed;
+ }
+
+ break;
+ }
+
+ case proto_ssh:
+ // Generate the final command argv[] array.
+ // ssh -p $port $hostname nc -U $socket [NULL]
+ cmd_argv = malloc (8 * sizeof (char *));
+ cmd_argv[0] = strdup (command);
+ cmd_argv[1] = strdup ("-p");
+ cmd_argv[2] = strdup (port);
+ cmd_argv[3] = strdup (path);
+ cmd_argv[4] = strdup ("nc");
+ cmd_argv[5] = strdup ("-U");
+ cmd_argv[6] = strdup (LIBVIRTD_UNIX_SOCKET);
+ cmd_argv[7] = 0;
+ /*FALLTHROUGH*/
+ case proto_ext:
+ private.sock = RPC_ANYSOCK;
+ private.cl =
+ clntext_create (command, cmd_argv,
+ LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1,
+ &private.sock, 0, 0);
+ if (!private.cl) {
+ error (conn, VIR_ERR_RPC,
+ clnt_spcreateerror ("could not create SunRPC client"));
+ goto failed;
+ }
+ }
+
+ // Send name (make the actual driver open RPC).
+ int *retcode_ptr = remote_rpc_open_1 (&remote_name, private.cl);
+ if (!retcode_ptr || *retcode_ptr == -1) {
+ error (conn, VIR_ERR_RPC,
+ clnt_sperror (private.cl, "remote_rpc_open"));
+ clnt_destroy (private.cl);
+ goto failed;
+ }
+
+ conn->private = malloc (sizeof private);
+ if (!conn->private) {
+ error (conn, VIR_ERR_NO_MEMORY, "malloc");
+ clnt_destroy (private.cl);
+ goto failed;
+ }
+ private.magic = MAGIC;
+ memcpy (conn->private, &private, sizeof private);
+
+ retcode = 0; /* Success. */
+ /*FALLTHROUGH*/
+
+ failed:
+ if (path) free (path);
+ if (port) free (port);
+ if (remote_name) free (remote_name);
+ if (command) free (command);
+ if (cmd_argv) {
+ char **a = cmd_argv;
+ while (*a) { free (*a); a++; }
+ free (cmd_argv);
+ }
+
+ return retcode;
+}
+
+/* Un-%-escape the argument string. Note that this always makes
+ * a copy, and that is intentional.
+ */
+static inline int
+xdigit (char c)
+{
+ switch (c) {
+ case '0'...'9': return c - '0';
+ case 'a'...'f': return c - 'a' + 10;
+ case 'A'...'F': return c - 'A' + 10;
+ default: return -1;
+ }
+}
+
+static char *
+unescape (const char *str)
+{
+ // Returned string will always be same length or shorter than input.
+ int n = strlen (str);
+
+ char *ret = malloc (n+1);
+ if (ret == 0) return 0; // although _I_ think we should abort().
+
+ int i;
+ char *p = ret;
+ for (i = 0; i < n; ++i) {
+ if (str[i] == '+') {
+ *p++ = ' ';
+ } else if (str[i] == '%') {
+ if (i+2 < n) {
+ int c1 = xdigit (str[i+1]), c2 = xdigit (str[i+2]);
+ if (c1 == -1 || c2 == -1) {
+ fprintf (stderr, "remote_open: incorrect %%-hex sequence in name\n");
+ return 0;
+ }
+ *p++ = xdigit (c1) << 4 | xdigit (c2);
+ } else {
+ fprintf (stderr, "remote_open: short %%-hex sequence in name\n");
+ return 0;
+ }
+ } else
+ *p++ = str[i];
+ }
+ *p = '\0';
+ return ret;
+}
+
+/* Parse a string which may be either "hostname" or "hostname:port".
+ * The hostname may contain colons (eg. if it's an IPv6 name).
+ * Note that this always makes a copy of the hostname and port number,
+ * and that is intentional.
+ */
+static int
+parse_hostname_port (const char *str, char **hostname_r,
+ char **port_r, const char *default_port)
+{
+ char *p = strrchr (str, ':');
+ if (p) {
+ *hostname_r = strndup (str, p-str);
+ *port_r = strdup (p+1);
+ } else {
+ // No :port, so just copy the hostname.
+ *hostname_r = strdup (str);
+ *port_r = strdup (default_port);
+ }
+ return 0;
+}
+
+static int
+remote_close (virConnectPtr conn)
+{
+ GET_PRIVATE (conn, -1);
+
+ int *retcode = remote_rpc_close_1 (NULL, private->cl);
+ if (retcode == 0) {
+ error (conn, VIR_ERR_RPC,
+ clnt_sperror (private->cl, "remote_rpc_open"));
+ return -1;
+ }
+ if (*retcode == -1) return -1;
+
+ // XXX freeres
+
+ // NB. clnt_destroy should close the socket (private->sock) too.
+ clnt_destroy (private->cl);
+ // Force errors if anyone tries to reuse the closed connection.
+ private->magic = DEAD;
+
+ return *retcode;
+}
+
+/* Remote_open and remote_close functions above are the complex ones. The
+ * rest just shuffle arguments and pass them along to the remote libvirtd.
+ */
+
+// Should we return our local type (ie. "remote"), or the type
+// of the remote HV, or the type of the remote HV + some flag?
+// I took the view that we should just transparently shuffle.
+static const char *
+remote_type (virConnectPtr conn)
+{
+ GET_PRIVATE (conn, NULL);
+
+ char **type = remote_rpc_type_1 (NULL, private->cl);
+ if (type == 0) {
+ error (conn, VIR_ERR_RPC,
+ clnt_sperror (private->cl, "remote_rpc_open"));
+ return NULL;
+ }
+
+ // XXX freeres
+
+ return *type;
+}
+
+static int
+remote_version (virConnectPtr conn, unsigned long *hvVer)
+{
+ GET_PRIVATE (conn, -1);
+
+ struct version_ret *ret = remote_rpc_version_1 (NULL, private->cl);
+ if (ret == 0) {
+ error (conn, VIR_ERR_RPC,
+ clnt_sperror (private->cl, "remote_rpc_open"));
+ return -1;
+ }
+
+ // XXX freeres
+
+ if (ret->retcode == -1) return -1;
+ *hvVer = ret->hvVer;
+ return ret->retcode;
+}
+
+/*
+ .nodeGetInfo = remote_nodeGetInfo,
+ .listDomains = remote_listDomains,
+ .numOfDomains = remote_numOfDomains,
+ .domainCreateLinux = remote_domainCreateLinux,
+ .domainLookupByID = remote_domainLookupByID,
+ .domainLookupByUUID = remote_domainLookupByUUID,
+ .domainLookupByName = remote_domainLookupByName,
+ .domainSuspend = remote_domainSuspend,
+ .domainResume = remote_domainResume,
+ .domainShutdown = remote_domainShutdown,
+ .domainReboot = remote_domainReboot,
+ .domainDestroy = remote_domainDestroy,
+ .domainGetOSType = remote_domainGetOSType,
+ .domainGetMaxMemory = remote_domainGetMaxMemory,
+ .domainSetMaxMemory = remote_domainSetMaxMemory,
+ .domainSetMemory = remote_domainSetMemory,
+ .domainGetInfo = remote_domainGetInfo,
+ .domainSave = remote_domainSave,
+ .domainRestore = remote_domainRestore,
+ .domainCoreDump = remote_domainCoreDump,
+ .domainSetVcpus = remote_domainSetVcpus,
+ .domainPinVcpu = remote_domainPinVcpu,
+ .domainGetVcpus = remote_domainGetVcpus,
+ .domainDumpXML = remote_domainDumpXML,
+ .listDefinedDomains = remote_listDefinedDomains,
+ .numOfDefinedDomains = remote_numOfDefinedDomains,
+ .domainCreate = remote_domainCreate,
+ .domainDefineXML = remote_domainDefineXML,
+ .domainUndefine = remote_domainUndefine,
+ .domainAttachDevice = remote_domainAttachDevice,
+ .domainDetachDevice = remote_domainDetachDevice,
+*/
+
+/* Error handling. This error handling is on crack. */
+static void
+error (virConnectPtr conn, virErrorNumber code, const char *info)
+{
+ const char *errmsg;
+
+ errmsg = __virErrorMsg (code, info);
+ __virRaiseError (conn, NULL, VIR_FROM_REMOTE,
+ code, VIR_ERR_ERROR, errmsg, info, NULL, 0, 0,
+ errmsg, info);
+}
+
+static virDriver driver = {
+ .no = VIR_DRV_REMOTE,
+ .name = "remote",
+ .ver = VERSION,
+ //.init = remote_init,
+ .open = remote_open,
+ .close = remote_close,
+ .type = remote_type,
+ .version = remote_version,
+#if 0
+ .nodeGetInfo = remote_nodeGetInfo,
+ .listDomains = remote_listDomains,
+ .numOfDomains = remote_numOfDomains,
+ .domainCreateLinux = remote_domainCreateLinux,
+ .domainLookupByID = remote_domainLookupByID,
+ .domainLookupByUUID = remote_domainLookupByUUID,
+ .domainLookupByName = remote_domainLookupByName,
+ .domainSuspend = remote_domainSuspend,
+ .domainResume = remote_domainResume,
+ .domainShutdown = remote_domainShutdown,
+ .domainReboot = remote_domainReboot,
+ .domainDestroy = remote_domainDestroy,
+ .domainGetOSType = remote_domainGetOSType,
+ .domainGetMaxMemory = remote_domainGetMaxMemory,
+ .domainSetMaxMemory = remote_domainSetMaxMemory,
+ .domainSetMemory = remote_domainSetMemory,
+ .domainGetInfo = remote_domainGetInfo,
+ .domainSave = remote_domainSave,
+ .domainRestore = remote_domainRestore,
+ .domainCoreDump = remote_domainCoreDump,
+ .domainSetVcpus = remote_domainSetVcpus,
+ .domainPinVcpu = remote_domainPinVcpu,
+ .domainGetVcpus = remote_domainGetVcpus,
+ .domainDumpXML = remote_domainDumpXML,
+ .listDefinedDomains = remote_listDefinedDomains,
+ .numOfDefinedDomains = remote_numOfDefinedDomains,
+ .domainCreate = remote_domainCreate,
+ .domainDefineXML = remote_domainDefineXML,
+ .domainUndefine = remote_domainUndefine,
+ .domainAttachDevice = remote_domainAttachDevice,
+ .domainDetachDevice = remote_domainDetachDevice,
+#endif
+};
+
+/* Register driver. */
+void
+remoteRegister (void)
+{
+ virRegisterDriver (&driver);
+}
--- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000
+++ src/remote_internal.h 2007-01-30 16:21:19.000000000 +0000
@@ -0,0 +1,34 @@
+/*
+ * remote_internal.h: driver to provide access to libvirtd running
+ * on a remote machine.
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * See COPYING.LIB for the license of this software.
+ *
+ * Richard Jones <rjones(a)redhat.com>
+ */
+
+#ifndef __VIR_REMOTE_INTERNAL_H__
+#define __VIR_REMOTE_INTERNAL_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void remoteRegister (void);
+
+/* The port numbers are strings because you can also use
+ * service names here.
+ */
+#define LIBVIRTD_GNUTLS_PORT "16514"
+#define LIBVIRTD_TCP_PORT "16509"
+ //#define LIBVIRTD_UNIX_SOCKET "/var/run/libvirtd/socket"
+#define LIBVIRTD_UNIX_SOCKET "/tmp/socket" // Just for testing
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* __VIR_REMOTE_INTERNAL_H__ */
--- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000
+++ src/remote_rpc.x 2007-01-30 13:32:56.000000000 +0000
@@ -0,0 +1,38 @@
+/* -*- C -*-
+ * remote_rpc.x: Remote procedure call interface for the remote driver.
+ * Process this file with rpcgen to generate client and server stubs.
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * See COPYING.LIB for the license of this software.
+ *
+ * Richard Jones <rjones(a)redhat.com>
+ */
+
+/* Structure returned from remote_rpc_version. */
+struct version_ret {
+ int retcode;
+ long hvVer;
+};
+
+program LIBVIRTREMOTE {
+ version LIBVIRTREMOTE_VERS1 {
+ /* XXX The open interface should return a cookie to represent
+ * the virConnectPtr on the server side. Note also that SunRPC
+ * is stateless so it's unclear when cookies can be garbage
+ * collected.
+ */
+ int remote_rpc_open (string) = 1;
+ int remote_rpc_close (void) = 2;
+ string remote_rpc_type (void) = 3;
+ version_ret remote_rpc_version (void) = 4;
+
+ /* etc */
+
+ } = 1;
+ /* It doesn't really matter what program number we choose here because
+ * there will only ever be one "program" listening on the assigned
+ * TCP port number. Nevertheless, choose one from the Sun private
+ * space.
+ */
+} = 0x20008080;
--- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000
+++ src/sunrpc/README 2007-01-30 16:15:43.000000000 +0000
@@ -0,0 +1,25 @@
+This directory contains modified SunRPC transports. They are based on
+the standard transports from glibc 2.5 (in the sunrpc/ directory
+there).
+
+clnt_ext.c
+ - Fork an external program, eg. ssh.
+ (Original: clnt_unix.c)
+
+clnt_gnutls.c
+ - Modified for IPv6 and GnuTLS support.
+ (Original: clnt_tcp.c)
+
+clnt_tcp2.c
+ - Modified for IPv6 support.
+ (Original: clnt_tcp.c)
+
+svc_gnutls.c
+ - Modified for IPv6 and GnuTLS support.
+ (Original: svc_tcp.c)
+
+svc_tcp2.c
+ - Modified for IPv6 support.
+ (Original: svc_tcp.c)
+
+$Id$
\ No newline at end of file
--- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000
+++ src/sunrpc/clnt_ext.c 2007-01-30 11:48:09.000000000 +0000
@@ -0,0 +1,645 @@
+/*
+ * clnt_ext.c: SunRPC over an external program. This is a modified version
+ * of clnt_unix.c from glibc which is written to run over a forked
+ * external program.
+ *
+ * Note that there is no corresponding svc_ext.c. It is expected that
+ * this client will talk to a remote Unix domain socket (ie. svc_unix.c).
+ *
+ * Modifications from glibc-2.5 base by Richard Jones <rjones(a)redhat.com>.
+ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * clnt_unix.c, Implements a TCP/IP based, client side RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * TCP based RPC supports 'batched calls'.
+ * A sequence of calls may be batched-up in a send buffer. The rpc call
+ * return immediately to the client even though the call was not necessarily
+ * sent. The batching occurs if the results' xdr routine is NULL (0) AND
+ * the rpc timeout value is zero (see clnt.h, rpc).
+ *
+ * Clients should NOT casually batch calls that in fact return results; that is,
+ * the server side should be aware that a call is batched and not produce any
+ * return message. Batched calls that produce many result messages can
+ * deadlock (netlock) the client and the server....
+ *
+ * Now go hang yourself.
+ */
+
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <rpc/rpc.h>
+#include <sys/uio.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <rpc/pmap_clnt.h>
+#ifdef USE_IN_LIBIO
+# include <wchar.h>
+#endif
+
+#include "clnt_ext.h"
+
+extern u_long _create_xid (void);
+
+#define MCALL_MSG_SIZE 24
+
+struct ct_data
+ {
+ int ct_sock;
+ bool_t ct_closeit;
+ struct timeval ct_wait;
+ bool_t ct_waitset; /* wait set by clnt_control? */
+ struct rpc_err ct_error;
+ char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */
+ u_int ct_mpos; /* pos after marshal */
+ XDR ct_xdrs;
+ int ct_pid; /* Child PID. */
+ };
+
+static int readunix (char *, char *, int);
+static int writeunix (char *, char *, int);
+
+static enum clnt_stat clntunix_call (CLIENT *, u_long, xdrproc_t, caddr_t,
+ xdrproc_t, caddr_t, struct timeval);
+static void clntunix_abort (void);
+static void clntunix_geterr (CLIENT *, struct rpc_err *);
+static bool_t clntunix_freeres (CLIENT *, xdrproc_t, caddr_t);
+static bool_t clntunix_control (CLIENT *, int, char *);
+static void clntunix_destroy (CLIENT *);
+
+static const struct clnt_ops unix_ops =
+{
+ clntunix_call,
+ clntunix_abort,
+ clntunix_geterr,
+ clntunix_freeres,
+ clntunix_destroy,
+ clntunix_control
+};
+
+CLIENT *
+clntext_create (char *filename, char *argv[],
+ u_long prog, u_long vers,
+ int *sockp, u_int sendsz, u_int recvsz)
+{
+ CLIENT *h;
+ struct ct_data *ct = (struct ct_data *) mem_alloc (sizeof (*ct));
+ struct rpc_msg call_msg;
+ int len, sv[2];
+
+ h = (CLIENT *) mem_alloc (sizeof (*h));
+ if (h == NULL || ct == NULL)
+ {
+ struct rpc_createerr *ce = &get_rpc_createerr ();
+ (void) fprintf (stderr, "clntext_create: out of memory\n");
+ ce->cf_stat = RPC_SYSTEMERROR;
+ ce->cf_error.re_errno = ENOMEM;
+ goto fooy;
+ }
+
+ /* Fork off the external process. Use socketpair to create a private
+ * (unnamed) Unix domain socket to the child process so we don't have
+ * to faff around with two file descriptors (a la 'pipe(2)').
+ */
+ if (*sockp < 0)
+ {
+ if (socketpair (PF_UNIX, SOCK_STREAM, 0, sv) == -1)
+ {
+ struct rpc_createerr *ce = &get_rpc_createerr ();
+ (void) fprintf (stderr, "clntext_create: socketpair\n");
+ ce->cf_stat = RPC_SYSTEMERROR;
+ ce->cf_error.re_errno = errno;
+ goto fooy;
+ }
+
+ ct->ct_pid = fork ();
+ if (ct->ct_pid == -1) {
+ struct rpc_createerr *ce = &get_rpc_createerr ();
+ (void) fprintf (stderr, "clntext_create: fork\n");
+ ce->cf_stat = RPC_SYSTEMERROR;
+ ce->cf_error.re_errno = errno;
+ goto fooy;
+ } else if (ct->ct_pid == 0) { /* Child. */
+ close (sv[0]);
+ // Connect socket (sv[1]) to stdin/stdout.
+ close (0);
+ dup (sv[1]);
+ close (1);
+ dup (sv[1]);
+ close (sv[1]);
+
+ // Run the external process.
+
+ if (!argv) {
+ argv = malloc (2 * sizeof (char *));
+ argv[0] = filename;
+ argv[1] = 0;
+ }
+ execvp (filename, argv);
+ perror (filename);
+ _exit (1);
+ }
+
+ /* Parent continues here. */
+ close (sv[1]);
+ *sockp = sv[0];
+ ct->ct_closeit = TRUE;
+ }
+ else
+ {
+ ct->ct_closeit = FALSE;
+ }
+
+ /*
+ * Set up private data struct
+ */
+ ct->ct_sock = *sockp;
+ ct->ct_wait.tv_usec = 0;
+ ct->ct_waitset = FALSE;
+
+ /*
+ * Initialize call message
+ */
+ call_msg.rm_xid = _create_xid ();
+ call_msg.rm_direction = CALL;
+ call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
+ call_msg.rm_call.cb_prog = prog;
+ call_msg.rm_call.cb_vers = vers;
+
+ /*
+ * pre-serialize the static part of the call msg and stash it away
+ */
+ xdrmem_create (&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE,
+ XDR_ENCODE);
+ if (!xdr_callhdr (&(ct->ct_xdrs), &call_msg))
+ {
+ if (ct->ct_closeit)
+ close (*sockp);
+ goto fooy;
+ }
+ ct->ct_mpos = XDR_GETPOS (&(ct->ct_xdrs));
+ XDR_DESTROY (&(ct->ct_xdrs));
+
+ /*
+ * Create a client handle which uses xdrrec for serialization
+ * and authnone for authentication.
+ */
+ xdrrec_create (&(ct->ct_xdrs), sendsz, recvsz,
+ (caddr_t) ct, readunix, writeunix);
+ h->cl_ops = (struct clnt_ops *) &unix_ops;
+ h->cl_private = (caddr_t) ct;
+ h->cl_auth = authnone_create ();
+ return h;
+
+fooy:
+ /*
+ * Something goofed, free stuff and barf
+ */
+ mem_free ((caddr_t) ct, sizeof (struct ct_data));
+ mem_free ((caddr_t) h, sizeof (CLIENT));
+ return (CLIENT *) NULL;
+}
+
+static enum clnt_stat
+clntunix_call (h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
+ CLIENT *h;
+ u_long proc;
+ xdrproc_t xdr_args;
+ caddr_t args_ptr;
+ xdrproc_t xdr_results;
+ caddr_t results_ptr;
+ struct timeval timeout;
+{
+ struct ct_data *ct = (struct ct_data *) h->cl_private;
+ XDR *xdrs = &(ct->ct_xdrs);
+ struct rpc_msg reply_msg;
+ u_long x_id;
+ u_int32_t *msg_x_id = (u_int32_t *) (ct->ct_mcall); /* yuk */
+ bool_t shipnow;
+ int refreshes = 2;
+
+ if (!ct->ct_waitset)
+ {
+ ct->ct_wait = timeout;
+ }
+
+ shipnow =
+ (xdr_results == (xdrproc_t) 0 && ct->ct_wait.tv_sec == 0
+ && ct->ct_wait.tv_usec == 0) ? FALSE : TRUE;
+
+call_again:
+ xdrs->x_op = XDR_ENCODE;
+ ct->ct_error.re_status = RPC_SUCCESS;
+ x_id = ntohl (--(*msg_x_id));
+ if ((!XDR_PUTBYTES (xdrs, ct->ct_mcall, ct->ct_mpos)) ||
+ (!XDR_PUTLONG (xdrs, (long *) &proc)) ||
+ (!AUTH_MARSHALL (h->cl_auth, xdrs)) ||
+ (!(*xdr_args) (xdrs, args_ptr)))
+ {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ ct->ct_error.re_status = RPC_CANTENCODEARGS;
+ (void) xdrrec_endofrecord (xdrs, TRUE);
+ return ct->ct_error.re_status;
+ }
+ if (!xdrrec_endofrecord (xdrs, shipnow))
+ return ct->ct_error.re_status = RPC_CANTSEND;
+ if (!shipnow)
+ return RPC_SUCCESS;
+ /*
+ * Hack to provide rpc-based message passing
+ */
+ if (ct->ct_wait.tv_sec == 0 && ct->ct_wait.tv_usec == 0)
+ return ct->ct_error.re_status = RPC_TIMEDOUT;
+
+
+ /*
+ * Keep receiving until we get a valid transaction id
+ */
+ xdrs->x_op = XDR_DECODE;
+ while (TRUE)
+ {
+ reply_msg.acpted_rply.ar_verf = _null_auth;
+ reply_msg.acpted_rply.ar_results.where = NULL;
+ reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
+ if (!xdrrec_skiprecord (xdrs))
+ return ct->ct_error.re_status;
+ /* now decode and validate the response header */
+ if (!xdr_replymsg (xdrs, &reply_msg))
+ {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ continue;
+ return ct->ct_error.re_status;
+ }
+ if (reply_msg.rm_xid == x_id)
+ break;
+ }
+
+ /*
+ * process header
+ */
+ _seterr_reply (&reply_msg, &(ct->ct_error));
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ {
+ if (!AUTH_VALIDATE (h->cl_auth, &reply_msg.acpted_rply.ar_verf))
+ {
+ ct->ct_error.re_status = RPC_AUTHERROR;
+ ct->ct_error.re_why = AUTH_INVALIDRESP;
+ }
+ else if (!(*xdr_results) (xdrs, results_ptr))
+ {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ ct->ct_error.re_status = RPC_CANTDECODERES;
+ }
+ /* free verifier ... */
+ if (reply_msg.acpted_rply.ar_verf.oa_base != NULL)
+ {
+ xdrs->x_op = XDR_FREE;
+ (void) xdr_opaque_auth (xdrs,
+ &(reply_msg.acpted_rply.ar_verf));
+ }
+ } /* end successful completion */
+ else
+ {
+ /* maybe our credentials need to be refreshed ... */
+ if (refreshes-- && AUTH_REFRESH (h->cl_auth))
+ goto call_again;
+ } /* end of unsuccessful completion */
+ return ct->ct_error.re_status;
+}
+
+static void
+clntunix_geterr (CLIENT *h, struct rpc_err *errp)
+{
+ struct ct_data *ct = (struct ct_data *) h->cl_private;
+
+ *errp = ct->ct_error;
+}
+
+static bool_t
+clntunix_freeres (cl, xdr_res, res_ptr)
+ CLIENT *cl;
+ xdrproc_t xdr_res;
+ caddr_t res_ptr;
+{
+ struct ct_data *ct = (struct ct_data *) cl->cl_private;
+ XDR *xdrs = &(ct->ct_xdrs);
+
+ xdrs->x_op = XDR_FREE;
+ return (*xdr_res) (xdrs, res_ptr);
+}
+
+static void
+clntunix_abort ()
+{
+}
+
+static bool_t
+clntunix_control (CLIENT *cl, int request, char *info)
+{
+ struct ct_data *ct = (struct ct_data *) cl->cl_private;
+
+
+ switch (request)
+ {
+ case CLSET_FD_CLOSE:
+ ct->ct_closeit = TRUE;
+ break;
+ case CLSET_FD_NCLOSE:
+ ct->ct_closeit = FALSE;
+ break;
+ case CLSET_TIMEOUT:
+ ct->ct_wait = *(struct timeval *) info;
+ break;
+ case CLGET_TIMEOUT:
+ *(struct timeval *) info = ct->ct_wait;
+ break;
+ case CLGET_FD:
+ *(int *)info = ct->ct_sock;
+ break;
+ case CLGET_XID:
+ /*
+ * use the knowledge that xid is the
+ * first element in the call structure *.
+ * This will get the xid of the PREVIOUS call
+ */
+ *(u_long *) info = ntohl (*(u_long *)ct->ct_mcall);
+ break;
+ case CLSET_XID:
+ /* This will set the xid of the NEXT call */
+ *(u_long *) ct->ct_mcall = htonl (*(u_long *)info - 1);
+ /* decrement by 1 as clntunix_call() increments once */
+ case CLGET_VERS:
+ /*
+ * This RELIES on the information that, in the call body,
+ * the version number field is the fifth field from the
+ * begining of the RPC header. MUST be changed if the
+ * call_struct is changed
+ */
+ *(u_long *) info = ntohl (*(u_long *) (ct->ct_mcall
+ + 4 * BYTES_PER_XDR_UNIT));
+ break;
+ case CLSET_VERS:
+ *(u_long *) (ct->ct_mcall + 4 * BYTES_PER_XDR_UNIT)
+ = htonl (*(u_long *) info);
+ break;
+ case CLGET_PROG:
+ /*
+ * This RELIES on the information that, in the call body,
+ * the program number field is the field from the
+ * begining of the RPC header. MUST be changed if the
+ * call_struct is changed
+ */
+ *(u_long *) info = ntohl (*(u_long *) (ct->ct_mcall
+ + 3 * BYTES_PER_XDR_UNIT));
+ break;
+ case CLSET_PROG:
+ *(u_long *) (ct->ct_mcall + 3 * BYTES_PER_XDR_UNIT)
+ = htonl(*(u_long *) info);
+ break;
+ /* The following are only possible with TI-RPC */
+ case CLGET_RETRY_TIMEOUT:
+ case CLSET_RETRY_TIMEOUT:
+ case CLGET_SVC_ADDR:
+ case CLSET_SVC_ADDR:
+ case CLSET_PUSH_TIMOD:
+ case CLSET_POP_TIMOD:
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+static void
+clntunix_destroy (CLIENT *h)
+{
+ struct ct_data *ct =
+ (struct ct_data *) h->cl_private;
+
+ if (ct->ct_closeit)
+ {
+ (void) close (ct->ct_sock);
+ }
+ XDR_DESTROY (&(ct->ct_xdrs));
+ mem_free ((caddr_t) ct, sizeof (struct ct_data));
+ mem_free ((caddr_t) h, sizeof (CLIENT));
+
+ // Wait for child to finish. Print any errors but don't stop.
+ int status;
+ if (waitpid (ct->ct_pid, &status, 0) == -1) {
+ perror ("waitpid");
+ return;
+ }
+ if (WIFEXITED (status)) {
+ int s = WEXITSTATUS (status);
+ if (s != 0)
+ fprintf (stderr, "clntext_destroy: warning: external command exited with non-zero exit status %d\n", s);
+ } else if (WIFSIGNALED (status)) {
+ int s = WTERMSIG (status);
+ fprintf (stderr, "clntext_destroy: warning: external command died on signal %d\n", s);
+ } else if (WIFSTOPPED (status)) {
+ int s = WSTOPSIG (status);
+ fprintf (stderr, "clntext_destroy: warning: external command stopped on signal %d\n", s);
+ }
+}
+
+static int
+__msgread (int sock, void *data, size_t cnt)
+{
+ struct iovec iov;
+ struct msghdr msg;
+#ifdef SCM_CREDENTIALS
+ static char cm[CMSG_SPACE(sizeof (struct ucred))];
+#endif
+ int len;
+
+ iov.iov_base = data;
+ iov.iov_len = cnt;
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+#ifdef SCM_CREDENTIALS
+ msg.msg_control = (caddr_t) &cm;
+ msg.msg_controllen = CMSG_SPACE(sizeof (struct ucred));
+#endif
+ msg.msg_flags = 0;
+
+#ifdef SO_PASSCRED
+ {
+ int on = 1;
+ if (setsockopt (sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof (on)))
+ return -1;
+ }
+#endif
+
+ restart:
+ len = recvmsg (sock, &msg, 0);
+ if (len >= 0)
+ {
+ if (msg.msg_flags & MSG_CTRUNC || len == 0)
+ return 0;
+ else
+ return len;
+ }
+ if (errno == EINTR)
+ goto restart;
+ return -1;
+}
+
+static int
+__msgwrite (int sock, void *data, size_t cnt)
+{
+#ifndef SCM_CREDENTIALS
+ /* We cannot implement this reliably. */
+ __set_errno (ENOSYS);
+ return -1;
+#else
+ struct iovec iov;
+ struct msghdr msg;
+ struct cmsghdr *cmsg = alloca (CMSG_SPACE(sizeof (struct ucred)));
+ struct ucred cred;
+ int len;
+
+ /* XXX I'm not sure, if gete?id() is always correct, or if we should use
+ get?id(). But since keyserv needs geteuid(), we have no other chance.
+ It would be much better, if the kernel could pass both to the server. */
+ cred.pid = getpid ();
+ cred.uid = geteuid ();
+ cred.gid = getegid ();
+
+ memcpy (CMSG_DATA(cmsg), &cred, sizeof (struct ucred));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_CREDENTIALS;
+ cmsg->cmsg_len = sizeof(*cmsg) + sizeof(struct ucred);
+
+ iov.iov_base = data;
+ iov.iov_len = cnt;
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_control = cmsg;
+ msg.msg_controllen = CMSG_ALIGN(cmsg->cmsg_len);
+ msg.msg_flags = 0;
+
+ restart:
+ len = sendmsg (sock, &msg, 0);
+ if (len >= 0)
+ return len;
+ if (errno == EINTR)
+ goto restart;
+ return -1;
+
+#endif
+}
+
+
+/*
+ * Interface between xdr serializer and unix connection.
+ * Behaves like the system calls, read & write, but keeps some error state
+ * around for the rpc level.
+ */
+static int
+readunix (char *ctptr, char *buf, int len)
+{
+ struct ct_data *ct = (struct ct_data *) ctptr;
+ struct pollfd fd;
+ int milliseconds = ((ct->ct_wait.tv_sec * 1000)
+ + (ct->ct_wait.tv_usec / 1000));
+
+ if (len == 0)
+ return 0;
+
+ fd.fd = ct->ct_sock;
+ fd.events = POLLIN;
+ while (TRUE)
+ {
+ switch (poll (&fd, 1, milliseconds))
+ {
+ case 0:
+ ct->ct_error.re_status = RPC_TIMEDOUT;
+ return -1;
+
+ case -1:
+ if (errno == EINTR)
+ continue;
+ ct->ct_error.re_status = RPC_CANTRECV;
+ ct->ct_error.re_errno = errno;
+ return -1;
+ }
+ break;
+ }
+ switch (len = __msgread (ct->ct_sock, buf, len))
+ {
+
+ case 0:
+ /* premature eof */
+ ct->ct_error.re_errno = ECONNRESET;
+ ct->ct_error.re_status = RPC_CANTRECV;
+ len = -1; /* it's really an error */
+ break;
+
+ case -1:
+ ct->ct_error.re_errno = errno;
+ ct->ct_error.re_status = RPC_CANTRECV;
+ break;
+ }
+ return len;
+}
+
+static int
+writeunix (char *ctptr, char *buf, int len)
+{
+ int i, cnt;
+ struct ct_data *ct = (struct ct_data *) ctptr;
+
+ for (cnt = len; cnt > 0; cnt -= i, buf += i)
+ {
+ if ((i = __msgwrite (ct->ct_sock, buf, cnt)) == -1)
+ {
+ ct->ct_error.re_errno = errno;
+ ct->ct_error.re_status = RPC_CANTSEND;
+ return -1;
+ }
+ }
+ return len;
+}
--- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000
+++ src/sunrpc/clnt_ext.h 2007-01-30 11:47:24.000000000 +0000
@@ -0,0 +1,18 @@
+/*
+ * clnt_ext.h: Interface to the SunRPC over external program.
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * See COPYING.LIB for the license of this software.
+ *
+ * Richard Jones <rjones(a)redhat.com>
+ */
+
+#ifndef __CLNT_EXT_H__
+#define __CLNT_EXT_H__
+
+extern CLIENT *clntext_create (char *filename, char *argv[],
+ u_long prog, u_long vers,
+ int *sockp, u_int sendsz, u_int recvsz);
+
+#endif /* __CLNT_EXT_H__ */
--- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000
+++ src/sunrpc/clnt_gnutls.c 2007-01-30 17:03:31.000000000 +0000
@@ -0,0 +1,603 @@
+/*
+ * clnt_gnutls.c: SunRPC over GnuTLS client. This is a modified version
+ * of clnt_tcp.c from glibc which is written to run over GnuTLS.
+ *
+ * Modifications from glibc-2.5 base by Richard Jones <rjones(a)redhat.com>.
+ */
+
+/* @(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * TCP based RPC supports 'batched calls'.
+ * A sequence of calls may be batched-up in a send buffer. The rpc call
+ * return immediately to the client even though the call was not necessarily
+ * sent. The batching occurs if the results' xdr routine is NULL (0) AND
+ * the rpc timeout value is zero (see clnt.h, rpc).
+ *
+ * Clients should NOT casually batch calls that in fact return results; that is,
+ * the server side should be aware that a call is batched and not produce any
+ * return message. Batched calls that produce many result messages can
+ * deadlock (netlock) the client and the server....
+ *
+ * Now go hang yourself.
+ */
+
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <rpc/rpc.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <rpc/pmap_clnt.h>
+#ifdef USE_IN_LIBIO
+# include <wchar.h>
+#endif
+#include <gnutls/gnutls.h>
+
+#include "clnt_gnutls.h"
+
+extern u_long _create_xid (void);
+
+#define MCALL_MSG_SIZE 24
+
+struct ct_data
+ {
+ int ct_sock;
+ bool_t ct_closeit;
+ struct timeval ct_wait;
+ bool_t ct_waitset; /* wait set by clnt_control? */
+ struct rpc_err ct_error;
+ char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */
+ u_int ct_mpos; /* pos after marshal */
+ XDR ct_xdrs;
+ gnutls_session_t ct_session; /* GnuTLS session. */
+ };
+
+static int readtcp (char *, char *, int);
+static int writetcp (char *, char *, int);
+
+static enum clnt_stat clnttcp_call (CLIENT *, u_long, xdrproc_t, caddr_t,
+ xdrproc_t, caddr_t, struct timeval);
+static void clnttcp_abort (void);
+static void clnttcp_geterr (CLIENT *, struct rpc_err *);
+static bool_t clnttcp_freeres (CLIENT *, xdrproc_t, caddr_t);
+static bool_t clnttcp_control (CLIENT *, int, char *);
+static void clnttcp_destroy (CLIENT *);
+
+static const struct clnt_ops tcp_ops =
+{
+ clnttcp_call,
+ clnttcp_abort,
+ clnttcp_geterr,
+ clnttcp_freeres,
+ clnttcp_destroy,
+ clnttcp_control
+};
+
+static int start_gnutls_session (gnutls_certificate_credentials_t xcred,
+ struct ct_data *ct, int sock);
+/*
+ * Create a client handle for a tcp/ip connection.
+ * If *sockp<0, *sockp is set to a newly created TCP socket and it is
+ * connected to raddr. If *sockp non-negative then
+ * raddr is ignored. The rpc/tcp package does buffering
+ * similar to stdio, so the client must pick send and receive buffer sizes,];
+ * 0 => use the default.
+ * If raddr->sin_port is 0, then a binder on the remote machine is
+ * consulted for the right port number.
+ * NB: *sockp is copied into a private area.
+ * NB: It is the clients responsibility to close *sockp.
+ * NB: The rpch->cl_auth is set null authentication. Caller may wish to set this
+ * something more useful.
+ */
+CLIENT *
+clntgnutls_create (int pf, struct sockaddr *raddr, int raddr_size,
+ gnutls_certificate_credentials_t xcred,
+ u_long prog, u_long vers,
+ int *sockp, u_int sendsz, u_int recvsz)
+{
+ CLIENT *h;
+ struct ct_data *ct;
+ struct rpc_msg call_msg;
+
+ h = (CLIENT *) mem_alloc (sizeof (*h));
+ ct = (struct ct_data *) mem_alloc (sizeof (*ct));
+ if (h == NULL || ct == NULL)
+ {
+ struct rpc_createerr *ce = &get_rpc_createerr ();
+ (void) fprintf (stderr, "clnttcp_create: out of memory\n");
+ ce->cf_stat = RPC_SYSTEMERROR;
+ ce->cf_error.re_errno = ENOMEM;
+ goto fooy;
+ }
+
+#if 0
+ // RWMJ: Note this will never be supported for portmap because the
+ // portmap protocol depends pretty fundamentally on IPv4. Don't
+ /// use portmapper anyway -- it's silly.
+ /*
+ * If no port number given ask the pmap for one
+ */
+ if (port == 0)
+ {
+ u_short port;
+ if ((port = pmap_getport (raddr, prog, vers, IPPROTO_TCP)) == 0)
+ {
+ mem_free ((caddr_t) ct, sizeof (struct ct_data));
+ mem_free ((caddr_t) h, sizeof (CLIENT));
+ return ((CLIENT *) NULL);
+ }
+ raddr->sin_port = htons (port);
+ }
+#endif
+
+ /*
+ * If no socket given, open one
+ */
+ if (*sockp < 0)
+ {
+ *sockp = socket (pf, SOCK_STREAM, 0);
+ // RWMJ: Why?
+ //(void) bindresvport (*sockp, (struct sockaddr_in *) 0);
+ if ((*sockp < 0)
+ || (connect (*sockp, raddr, raddr_size) < 0))
+ {
+ struct rpc_createerr *ce = &get_rpc_createerr ();
+ ce->cf_stat = RPC_SYSTEMERROR;
+ ce->cf_error.re_errno = errno;
+ if (*sockp >= 0)
+ (void) close (*sockp);
+ goto fooy;
+ }
+ ct->ct_closeit = TRUE;
+ }
+ else
+ {
+ ct->ct_closeit = FALSE;
+ }
+
+ /*
+ * Set up private data struct
+ */
+ ct->ct_sock = *sockp;
+ ct->ct_wait.tv_usec = 0;
+ ct->ct_waitset = FALSE;
+
+ /*
+ * Initialize call message
+ */
+ call_msg.rm_xid = _create_xid ();
+ call_msg.rm_direction = CALL;
+ call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
+ call_msg.rm_call.cb_prog = prog;
+ call_msg.rm_call.cb_vers = vers;
+
+ /*
+ * pre-serialize the static part of the call msg and stash it away
+ */
+ xdrmem_create (&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE,
+ XDR_ENCODE);
+ if (!xdr_callhdr (&(ct->ct_xdrs), &call_msg))
+ {
+ if (ct->ct_closeit)
+ {
+ (void) close (*sockp);
+ }
+ goto fooy;
+ }
+ ct->ct_mpos = XDR_GETPOS (&(ct->ct_xdrs));
+ XDR_DESTROY (&(ct->ct_xdrs));
+
+ /*
+ * Create a client handle which uses xdrrec for serialization
+ * and authnone for authentication.
+ */
+ xdrrec_create (&(ct->ct_xdrs), sendsz, recvsz,
+ (caddr_t) ct, readtcp, writetcp);
+ h->cl_ops = (struct clnt_ops *) &tcp_ops;
+ h->cl_private = (caddr_t) ct;
+ h->cl_auth = authnone_create ();
+
+ /* Start GnuTLS on this socket. */
+ if (start_gnutls_session (xcred, ct, *sockp) == -1) {
+ if (ct->ct_closeit)
+ close (*sockp);
+ goto fooy;
+ }
+
+ return h;
+
+fooy:
+ /*
+ * Something goofed, free stuff and barf
+ */
+ mem_free ((caddr_t) ct, sizeof (struct ct_data));
+ mem_free ((caddr_t) h, sizeof (CLIENT));
+ return ((CLIENT *) NULL);
+}
+
+static int
+start_gnutls_session (gnutls_certificate_credentials_t xcred,
+ struct ct_data *ct, int sock)
+{
+ const int cert_type_priority[3] = {
+ GNUTLS_CRT_X509,
+ GNUTLS_CRT_OPENPGP,
+ 0
+ };
+
+ /* Initialize TLS session
+ */
+ gnutls_init (&ct->ct_session, GNUTLS_CLIENT);
+
+ /* Use default priorities */
+ gnutls_set_default_priority (ct->ct_session);
+ gnutls_certificate_type_set_priority (ct->ct_session, cert_type_priority);
+
+ /* put the x509 credentials to the current session
+ */
+ gnutls_credentials_set (ct->ct_session, GNUTLS_CRD_CERTIFICATE, xcred);
+
+ gnutls_transport_set_ptr (ct->ct_session,
+ (gnutls_transport_ptr_t) (long) sock);
+
+ /* Perform the TLS handshake
+ */
+ int ret = gnutls_handshake (ct->ct_session);
+
+ if (ret < 0)
+ {
+ fprintf (stderr, "clnt_gnutls: TLS handshake failed\n");
+ gnutls_perror (ret);
+ exit (1);
+ }
+
+ /* XXX You need to verify the peer's certificate matches its name. */
+ printf ("XXX need to verify peer's certificate matches its name.\n");
+
+#if 0
+ /* Print session info. */
+ print_info (session);
+#endif
+
+ return 0;
+}
+
+static enum clnt_stat
+clnttcp_call (h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
+ CLIENT *h;
+ u_long proc;
+ xdrproc_t xdr_args;
+ caddr_t args_ptr;
+ xdrproc_t xdr_results;
+ caddr_t results_ptr;
+ struct timeval timeout;
+{
+ struct ct_data *ct = (struct ct_data *) h->cl_private;
+ XDR *xdrs = &(ct->ct_xdrs);
+ struct rpc_msg reply_msg;
+ u_long x_id;
+ u_int32_t *msg_x_id = (u_int32_t *) (ct->ct_mcall); /* yuk */
+ bool_t shipnow;
+ int refreshes = 2;
+
+ if (!ct->ct_waitset)
+ {
+ ct->ct_wait = timeout;
+ }
+
+ shipnow =
+ (xdr_results == (xdrproc_t) 0 && ct->ct_wait.tv_sec == 0
+ && ct->ct_wait.tv_usec == 0) ? FALSE : TRUE;
+
+call_again:
+ xdrs->x_op = XDR_ENCODE;
+ ct->ct_error.re_status = RPC_SUCCESS;
+ x_id = ntohl (--(*msg_x_id));
+ if ((!XDR_PUTBYTES (xdrs, ct->ct_mcall, ct->ct_mpos)) ||
+ (!XDR_PUTLONG (xdrs, (long *) &proc)) ||
+ (!AUTH_MARSHALL (h->cl_auth, xdrs)) ||
+ (!(*xdr_args) (xdrs, args_ptr)))
+ {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ ct->ct_error.re_status = RPC_CANTENCODEARGS;
+ (void) xdrrec_endofrecord (xdrs, TRUE);
+ return (ct->ct_error.re_status);
+ }
+ if (!xdrrec_endofrecord (xdrs, shipnow))
+ return ct->ct_error.re_status = RPC_CANTSEND;
+ if (!shipnow)
+ return RPC_SUCCESS;
+ /*
+ * Hack to provide rpc-based message passing
+ */
+ if (ct->ct_wait.tv_sec == 0 && ct->ct_wait.tv_usec == 0)
+ {
+ return ct->ct_error.re_status = RPC_TIMEDOUT;
+ }
+
+
+ /*
+ * Keep receiving until we get a valid transaction id
+ */
+ xdrs->x_op = XDR_DECODE;
+ while (TRUE)
+ {
+ reply_msg.acpted_rply.ar_verf = _null_auth;
+ reply_msg.acpted_rply.ar_results.where = NULL;
+ reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
+ if (!xdrrec_skiprecord (xdrs))
+ return (ct->ct_error.re_status);
+ /* now decode and validate the response header */
+ if (!xdr_replymsg (xdrs, &reply_msg))
+ {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ continue;
+ return ct->ct_error.re_status;
+ }
+ if ((u_int32_t) reply_msg.rm_xid == (u_int32_t) x_id)
+ break;
+ }
+
+ /*
+ * process header
+ */
+ _seterr_reply (&reply_msg, &(ct->ct_error));
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ {
+ if (!AUTH_VALIDATE (h->cl_auth, &reply_msg.acpted_rply.ar_verf))
+ {
+ ct->ct_error.re_status = RPC_AUTHERROR;
+ ct->ct_error.re_why = AUTH_INVALIDRESP;
+ }
+ else if (!(*xdr_results) (xdrs, results_ptr))
+ {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ ct->ct_error.re_status = RPC_CANTDECODERES;
+ }
+ /* free verifier ... */
+ if (reply_msg.acpted_rply.ar_verf.oa_base != NULL)
+ {
+ xdrs->x_op = XDR_FREE;
+ (void) xdr_opaque_auth (xdrs,
+ &(reply_msg.acpted_rply.ar_verf));
+ }
+ } /* end successful completion */
+ else
+ {
+ /* maybe our credentials need to be refreshed ... */
+ if (refreshes-- && AUTH_REFRESH (h->cl_auth))
+ goto call_again;
+ } /* end of unsuccessful completion */
+ return ct->ct_error.re_status;
+}
+
+static void
+clnttcp_geterr (h, errp)
+ CLIENT *h;
+ struct rpc_err *errp;
+{
+ struct ct_data *ct =
+ (struct ct_data *) h->cl_private;
+
+ *errp = ct->ct_error;
+}
+
+static bool_t
+clnttcp_freeres (cl, xdr_res, res_ptr)
+ CLIENT *cl;
+ xdrproc_t xdr_res;
+ caddr_t res_ptr;
+{
+ struct ct_data *ct = (struct ct_data *) cl->cl_private;
+ XDR *xdrs = &(ct->ct_xdrs);
+
+ xdrs->x_op = XDR_FREE;
+ return (*xdr_res) (xdrs, res_ptr);
+}
+
+static void
+clnttcp_abort ()
+{
+}
+
+static bool_t
+clnttcp_control (CLIENT *cl, int request, char *info)
+{
+ struct ct_data *ct = (struct ct_data *) cl->cl_private;
+
+
+ switch (request)
+ {
+ case CLSET_FD_CLOSE:
+ ct->ct_closeit = TRUE;
+ break;
+ case CLSET_FD_NCLOSE:
+ ct->ct_closeit = FALSE;
+ break;
+ case CLSET_TIMEOUT:
+ ct->ct_wait = *(struct timeval *) info;
+ ct->ct_waitset = TRUE;
+ break;
+ case CLGET_TIMEOUT:
+ *(struct timeval *) info = ct->ct_wait;
+ break;
+ case CLGET_FD:
+ *(int *)info = ct->ct_sock;
+ break;
+ case CLGET_XID:
+ /*
+ * use the knowledge that xid is the
+ * first element in the call structure *.
+ * This will get the xid of the PREVIOUS call
+ */
+ *(u_long *)info = ntohl (*(u_long *)ct->ct_mcall);
+ break;
+ case CLSET_XID:
+ /* This will set the xid of the NEXT call */
+ *(u_long *)ct->ct_mcall = htonl (*(u_long *)info - 1);
+ /* decrement by 1 as clnttcp_call() increments once */
+ case CLGET_VERS:
+ /*
+ * This RELIES on the information that, in the call body,
+ * the version number field is the fifth field from the
+ * begining of the RPC header. MUST be changed if the
+ * call_struct is changed
+ */
+ *(u_long *)info = ntohl (*(u_long *)(ct->ct_mcall +
+ 4 * BYTES_PER_XDR_UNIT));
+ break;
+ case CLSET_VERS:
+ *(u_long *)(ct->ct_mcall + 4 * BYTES_PER_XDR_UNIT)
+ = htonl (*(u_long *)info);
+ break;
+ case CLGET_PROG:
+ /*
+ * This RELIES on the information that, in the call body,
+ * the program number field is the field from the
+ * begining of the RPC header. MUST be changed if the
+ * call_struct is changed
+ */
+ *(u_long *)info = ntohl(*(u_long *)(ct->ct_mcall +
+ 3 * BYTES_PER_XDR_UNIT));
+ break;
+ case CLSET_PROG:
+ *(u_long *)(ct->ct_mcall + 3 * BYTES_PER_XDR_UNIT)
+ = htonl(*(u_long *)info);
+ break;
+ /* The following are only possible with TI-RPC */
+ case CLGET_RETRY_TIMEOUT:
+ case CLSET_RETRY_TIMEOUT:
+ case CLGET_SVC_ADDR:
+ case CLSET_SVC_ADDR:
+ case CLSET_PUSH_TIMOD:
+ case CLSET_POP_TIMOD:
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+static void
+clnttcp_destroy (CLIENT *h)
+{
+ struct ct_data *ct =
+ (struct ct_data *) h->cl_private;
+
+ gnutls_bye (ct->ct_session, GNUTLS_SHUT_RDWR);
+ gnutls_deinit (ct->ct_session);
+
+ if (ct->ct_closeit)
+ {
+ (void) close (ct->ct_sock);
+ }
+ XDR_DESTROY (&(ct->ct_xdrs));
+ mem_free ((caddr_t) ct, sizeof (struct ct_data));
+ mem_free ((caddr_t) h, sizeof (CLIENT));
+}
+
+/*
+ * Interface between xdr serializer and tcp connection.
+ * Behaves like the system calls, read & write, but keeps some error state
+ * around for the rpc level.
+ */
+static int
+readtcp (char *ctptr, char *buf, int len)
+{
+ struct ct_data *ct = (struct ct_data *)ctptr;
+ struct pollfd fd;
+ int milliseconds = (ct->ct_wait.tv_sec * 1000) +
+ (ct->ct_wait.tv_usec / 1000);
+
+ if (len == 0)
+ return 0;
+
+ fd.fd = ct->ct_sock;
+ fd.events = POLLIN;
+ while (TRUE)
+ {
+ switch (poll(&fd, 1, milliseconds))
+ {
+ case 0:
+ ct->ct_error.re_status = RPC_TIMEDOUT;
+ return -1;
+
+ case -1:
+ if (errno == EINTR)
+ continue;
+ ct->ct_error.re_status = RPC_CANTRECV;
+ ct->ct_error.re_errno = errno;
+ return -1;
+ }
+ break;
+ }
+
+ switch (len = gnutls_record_recv (ct->ct_session, buf, len))
+ {
+ case 0:
+ /* premature eof */
+ ct->ct_error.re_errno = ECONNRESET;
+ ct->ct_error.re_status = RPC_CANTRECV;
+ len = -1; /* it's really an error */
+ break;
+
+ case -1:
+ ct->ct_error.re_errno = errno;
+ ct->ct_error.re_status = RPC_CANTRECV;
+ break;
+ }
+ return len;
+}
+
+static int
+writetcp (char *ctptr, char *buf, int len)
+{
+ struct ct_data *ct = (struct ct_data*)ctptr;
+
+ if (gnutls_record_send (ct->ct_session, buf, len) < 0)
+ {
+ ct->ct_error.re_errno = errno;
+ ct->ct_error.re_status = RPC_CANTSEND;
+ return -1;
+ }
+
+ return len;
+}
--- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000
+++ src/sunrpc/clnt_gnutls.h 2007-01-30 11:59:17.000000000 +0000
@@ -0,0 +1,20 @@
+/*
+ * clnt_gnutls.h: Interface to the SunRPC over GnuTLS.
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * See COPYING.LIB for the license of this software.
+ *
+ * Richard Jones <rjones(a)redhat.com>
+ */
+
+#ifndef __CLNT_GNUTLS_H__
+#define __CLNT_GNUTLS_H__
+
+extern CLIENT *clntgnutls_create (int pf,
+ struct sockaddr *raddr, int raddr_size,
+ gnutls_certificate_credentials_t xcred,
+ u_long prog, u_long vers,
+ int *sockp, u_int sendsz, u_int recvsz);
+
+#endif /* __CLNT_GNUTLS_H__ */
--- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000
+++ src/sunrpc/clnt_tcp2.c 2007-01-30 16:08:20.000000000 +0000
@@ -0,0 +1,544 @@
+/*
+ * clnt_tcp2.c: SunRPC TCP client. This is a very slightly modified
+ * version of clnt_tcp.c from glibc which removes some of the IPv4
+ * assumptions, so this version can be used over IPv4 or IPv6 sockets.
+ *
+ * Modifications from glibc-2.5 base by Richard Jones <rjones(a)redhat.com>.
+ */
+
+/* @(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * TCP based RPC supports 'batched calls'.
+ * A sequence of calls may be batched-up in a send buffer. The rpc call
+ * return immediately to the client even though the call was not necessarily
+ * sent. The batching occurs if the results' xdr routine is NULL (0) AND
+ * the rpc timeout value is zero (see clnt.h, rpc).
+ *
+ * Clients should NOT casually batch calls that in fact return results; that is,
+ * the server side should be aware that a call is batched and not produce any
+ * return message. Batched calls that produce many result messages can
+ * deadlock (netlock) the client and the server....
+ *
+ * Now go hang yourself.
+ */
+
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <rpc/rpc.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <rpc/pmap_clnt.h>
+#ifdef USE_IN_LIBIO
+# include <wchar.h>
+#endif
+
+#include "clnt_tcp2.h"
+
+extern u_long _create_xid (void);
+
+#define MCALL_MSG_SIZE 24
+
+struct ct_data
+ {
+ int ct_sock;
+ bool_t ct_closeit;
+ struct timeval ct_wait;
+ bool_t ct_waitset; /* wait set by clnt_control? */
+ struct rpc_err ct_error;
+ char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */
+ u_int ct_mpos; /* pos after marshal */
+ XDR ct_xdrs;
+ };
+
+static int readtcp (char *, char *, int);
+static int writetcp (char *, char *, int);
+
+static enum clnt_stat clnttcp_call (CLIENT *, u_long, xdrproc_t, caddr_t,
+ xdrproc_t, caddr_t, struct timeval);
+static void clnttcp_abort (void);
+static void clnttcp_geterr (CLIENT *, struct rpc_err *);
+static bool_t clnttcp_freeres (CLIENT *, xdrproc_t, caddr_t);
+static bool_t clnttcp_control (CLIENT *, int, char *);
+static void clnttcp_destroy (CLIENT *);
+
+static const struct clnt_ops tcp_ops =
+{
+ clnttcp_call,
+ clnttcp_abort,
+ clnttcp_geterr,
+ clnttcp_freeres,
+ clnttcp_destroy,
+ clnttcp_control
+};
+
+/*
+ * Create a client handle for a tcp/ip connection.
+ * If *sockp<0, *sockp is set to a newly created TCP socket and it is
+ * connected to raddr. If *sockp non-negative then
+ * raddr is ignored. The rpc/tcp package does buffering
+ * similar to stdio, so the client must pick send and receive buffer sizes,];
+ * 0 => use the default.
+ * If raddr->sin_port is 0, then a binder on the remote machine is
+ * consulted for the right port number.
+ * NB: *sockp is copied into a private area.
+ * NB: It is the clients responsibility to close *sockp.
+ * NB: The rpch->cl_auth is set null authentication. Caller may wish to set this
+ * something more useful.
+ */
+CLIENT *
+clnttcp2_create (int af, struct sockaddr *raddr, int raddr_size,
+ u_long prog, u_long vers,
+ int *sockp, u_int sendsz, u_int recvsz)
+{
+ CLIENT *h;
+ struct ct_data *ct;
+ struct rpc_msg call_msg;
+
+ h = (CLIENT *) mem_alloc (sizeof (*h));
+ ct = (struct ct_data *) mem_alloc (sizeof (*ct));
+ if (h == NULL || ct == NULL)
+ {
+ struct rpc_createerr *ce = &get_rpc_createerr ();
+ (void) fprintf (stderr, "clnttcp_create: out of memory\n");
+ ce->cf_stat = RPC_SYSTEMERROR;
+ ce->cf_error.re_errno = ENOMEM;
+ goto fooy;
+ }
+
+#if 0
+ // RWMJ: Note this will never be supported for portmap because the
+ // portmap protocol depends pretty fundamentally on IPv4. Don't
+ /// use portmapper anyway -- it's silly.
+ /*
+ * If no port number given ask the pmap for one
+ */
+ if (port == 0)
+ {
+ u_short port;
+ if ((port = pmap_getport (raddr, prog, vers, IPPROTO_TCP)) == 0)
+ {
+ mem_free ((caddr_t) ct, sizeof (struct ct_data));
+ mem_free ((caddr_t) h, sizeof (CLIENT));
+ return ((CLIENT *) NULL);
+ }
+ raddr->sin_port = htons (port);
+ }
+#endif
+
+ /*
+ * If no socket given, open one
+ */
+ if (*sockp < 0)
+ {
+ *sockp = socket (af, SOCK_STREAM, 0);
+ // RWMJ: Why?
+ //(void) bindresvport (*sockp, (struct sockaddr_in *) 0);
+ if ((*sockp < 0)
+ || (connect (*sockp, raddr, raddr_size) < 0))
+ {
+ struct rpc_createerr *ce = &get_rpc_createerr ();
+ ce->cf_stat = RPC_SYSTEMERROR;
+ ce->cf_error.re_errno = errno;
+ if (*sockp >= 0)
+ (void) close (*sockp);
+ goto fooy;
+ }
+ ct->ct_closeit = TRUE;
+ }
+ else
+ {
+ ct->ct_closeit = FALSE;
+ }
+
+ /*
+ * Set up private data struct
+ */
+ ct->ct_sock = *sockp;
+ ct->ct_wait.tv_usec = 0;
+ ct->ct_waitset = FALSE;
+
+ /*
+ * Initialize call message
+ */
+ call_msg.rm_xid = _create_xid ();
+ call_msg.rm_direction = CALL;
+ call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
+ call_msg.rm_call.cb_prog = prog;
+ call_msg.rm_call.cb_vers = vers;
+
+ /*
+ * pre-serialize the static part of the call msg and stash it away
+ */
+ xdrmem_create (&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE,
+ XDR_ENCODE);
+ if (!xdr_callhdr (&(ct->ct_xdrs), &call_msg))
+ {
+ if (ct->ct_closeit)
+ {
+ (void) close (*sockp);
+ }
+ goto fooy;
+ }
+ ct->ct_mpos = XDR_GETPOS (&(ct->ct_xdrs));
+ XDR_DESTROY (&(ct->ct_xdrs));
+
+ /*
+ * Create a client handle which uses xdrrec for serialization
+ * and authnone for authentication.
+ */
+ xdrrec_create (&(ct->ct_xdrs), sendsz, recvsz,
+ (caddr_t) ct, readtcp, writetcp);
+ h->cl_ops = (struct clnt_ops *) &tcp_ops;
+ h->cl_private = (caddr_t) ct;
+ h->cl_auth = authnone_create ();
+ return h;
+
+fooy:
+ /*
+ * Something goofed, free stuff and barf
+ */
+ mem_free ((caddr_t) ct, sizeof (struct ct_data));
+ mem_free ((caddr_t) h, sizeof (CLIENT));
+ return ((CLIENT *) NULL);
+}
+
+static enum clnt_stat
+clnttcp_call (h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
+ CLIENT *h;
+ u_long proc;
+ xdrproc_t xdr_args;
+ caddr_t args_ptr;
+ xdrproc_t xdr_results;
+ caddr_t results_ptr;
+ struct timeval timeout;
+{
+ struct ct_data *ct = (struct ct_data *) h->cl_private;
+ XDR *xdrs = &(ct->ct_xdrs);
+ struct rpc_msg reply_msg;
+ u_long x_id;
+ u_int32_t *msg_x_id = (u_int32_t *) (ct->ct_mcall); /* yuk */
+ bool_t shipnow;
+ int refreshes = 2;
+
+ if (!ct->ct_waitset)
+ {
+ ct->ct_wait = timeout;
+ }
+
+ shipnow =
+ (xdr_results == (xdrproc_t) 0 && ct->ct_wait.tv_sec == 0
+ && ct->ct_wait.tv_usec == 0) ? FALSE : TRUE;
+
+call_again:
+ xdrs->x_op = XDR_ENCODE;
+ ct->ct_error.re_status = RPC_SUCCESS;
+ x_id = ntohl (--(*msg_x_id));
+ if ((!XDR_PUTBYTES (xdrs, ct->ct_mcall, ct->ct_mpos)) ||
+ (!XDR_PUTLONG (xdrs, (long *) &proc)) ||
+ (!AUTH_MARSHALL (h->cl_auth, xdrs)) ||
+ (!(*xdr_args) (xdrs, args_ptr)))
+ {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ ct->ct_error.re_status = RPC_CANTENCODEARGS;
+ (void) xdrrec_endofrecord (xdrs, TRUE);
+ return (ct->ct_error.re_status);
+ }
+ if (!xdrrec_endofrecord (xdrs, shipnow))
+ return ct->ct_error.re_status = RPC_CANTSEND;
+ if (!shipnow)
+ return RPC_SUCCESS;
+ /*
+ * Hack to provide rpc-based message passing
+ */
+ if (ct->ct_wait.tv_sec == 0 && ct->ct_wait.tv_usec == 0)
+ {
+ return ct->ct_error.re_status = RPC_TIMEDOUT;
+ }
+
+
+ /*
+ * Keep receiving until we get a valid transaction id
+ */
+ xdrs->x_op = XDR_DECODE;
+ while (TRUE)
+ {
+ reply_msg.acpted_rply.ar_verf = _null_auth;
+ reply_msg.acpted_rply.ar_results.where = NULL;
+ reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
+ if (!xdrrec_skiprecord (xdrs))
+ return (ct->ct_error.re_status);
+ /* now decode and validate the response header */
+ if (!xdr_replymsg (xdrs, &reply_msg))
+ {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ continue;
+ return ct->ct_error.re_status;
+ }
+ if ((u_int32_t) reply_msg.rm_xid == (u_int32_t) x_id)
+ break;
+ }
+
+ /*
+ * process header
+ */
+ _seterr_reply (&reply_msg, &(ct->ct_error));
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ {
+ if (!AUTH_VALIDATE (h->cl_auth, &reply_msg.acpted_rply.ar_verf))
+ {
+ ct->ct_error.re_status = RPC_AUTHERROR;
+ ct->ct_error.re_why = AUTH_INVALIDRESP;
+ }
+ else if (!(*xdr_results) (xdrs, results_ptr))
+ {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ ct->ct_error.re_status = RPC_CANTDECODERES;
+ }
+ /* free verifier ... */
+ if (reply_msg.acpted_rply.ar_verf.oa_base != NULL)
+ {
+ xdrs->x_op = XDR_FREE;
+ (void) xdr_opaque_auth (xdrs,
+ &(reply_msg.acpted_rply.ar_verf));
+ }
+ } /* end successful completion */
+ else
+ {
+ /* maybe our credentials need to be refreshed ... */
+ if (refreshes-- && AUTH_REFRESH (h->cl_auth))
+ goto call_again;
+ } /* end of unsuccessful completion */
+ return ct->ct_error.re_status;
+}
+
+static void
+clnttcp_geterr (h, errp)
+ CLIENT *h;
+ struct rpc_err *errp;
+{
+ struct ct_data *ct =
+ (struct ct_data *) h->cl_private;
+
+ *errp = ct->ct_error;
+}
+
+static bool_t
+clnttcp_freeres (cl, xdr_res, res_ptr)
+ CLIENT *cl;
+ xdrproc_t xdr_res;
+ caddr_t res_ptr;
+{
+ struct ct_data *ct = (struct ct_data *) cl->cl_private;
+ XDR *xdrs = &(ct->ct_xdrs);
+
+ xdrs->x_op = XDR_FREE;
+ return (*xdr_res) (xdrs, res_ptr);
+}
+
+static void
+clnttcp_abort ()
+{
+}
+
+static bool_t
+clnttcp_control (CLIENT *cl, int request, char *info)
+{
+ struct ct_data *ct = (struct ct_data *) cl->cl_private;
+
+
+ switch (request)
+ {
+ case CLSET_FD_CLOSE:
+ ct->ct_closeit = TRUE;
+ break;
+ case CLSET_FD_NCLOSE:
+ ct->ct_closeit = FALSE;
+ break;
+ case CLSET_TIMEOUT:
+ ct->ct_wait = *(struct timeval *) info;
+ ct->ct_waitset = TRUE;
+ break;
+ case CLGET_TIMEOUT:
+ *(struct timeval *) info = ct->ct_wait;
+ break;
+ case CLGET_FD:
+ *(int *)info = ct->ct_sock;
+ break;
+ case CLGET_XID:
+ /*
+ * use the knowledge that xid is the
+ * first element in the call structure *.
+ * This will get the xid of the PREVIOUS call
+ */
+ *(u_long *)info = ntohl (*(u_long *)ct->ct_mcall);
+ break;
+ case CLSET_XID:
+ /* This will set the xid of the NEXT call */
+ *(u_long *)ct->ct_mcall = htonl (*(u_long *)info - 1);
+ /* decrement by 1 as clnttcp_call() increments once */
+ case CLGET_VERS:
+ /*
+ * This RELIES on the information that, in the call body,
+ * the version number field is the fifth field from the
+ * begining of the RPC header. MUST be changed if the
+ * call_struct is changed
+ */
+ *(u_long *)info = ntohl (*(u_long *)(ct->ct_mcall +
+ 4 * BYTES_PER_XDR_UNIT));
+ break;
+ case CLSET_VERS:
+ *(u_long *)(ct->ct_mcall + 4 * BYTES_PER_XDR_UNIT)
+ = htonl (*(u_long *)info);
+ break;
+ case CLGET_PROG:
+ /*
+ * This RELIES on the information that, in the call body,
+ * the program number field is the field from the
+ * begining of the RPC header. MUST be changed if the
+ * call_struct is changed
+ */
+ *(u_long *)info = ntohl(*(u_long *)(ct->ct_mcall +
+ 3 * BYTES_PER_XDR_UNIT));
+ break;
+ case CLSET_PROG:
+ *(u_long *)(ct->ct_mcall + 3 * BYTES_PER_XDR_UNIT)
+ = htonl(*(u_long *)info);
+ break;
+ /* The following are only possible with TI-RPC */
+ case CLGET_RETRY_TIMEOUT:
+ case CLSET_RETRY_TIMEOUT:
+ case CLGET_SVC_ADDR:
+ case CLSET_SVC_ADDR:
+ case CLSET_PUSH_TIMOD:
+ case CLSET_POP_TIMOD:
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+static void
+clnttcp_destroy (CLIENT *h)
+{
+ struct ct_data *ct =
+ (struct ct_data *) h->cl_private;
+
+ if (ct->ct_closeit)
+ {
+ (void) close (ct->ct_sock);
+ }
+ XDR_DESTROY (&(ct->ct_xdrs));
+ mem_free ((caddr_t) ct, sizeof (struct ct_data));
+ mem_free ((caddr_t) h, sizeof (CLIENT));
+}
+
+/*
+ * Interface between xdr serializer and tcp connection.
+ * Behaves like the system calls, read & write, but keeps some error state
+ * around for the rpc level.
+ */
+static int
+readtcp (char *ctptr, char *buf, int len)
+{
+ struct ct_data *ct = (struct ct_data *)ctptr;
+ struct pollfd fd;
+ int milliseconds = (ct->ct_wait.tv_sec * 1000) +
+ (ct->ct_wait.tv_usec / 1000);
+
+ if (len == 0)
+ return 0;
+
+ fd.fd = ct->ct_sock;
+ fd.events = POLLIN;
+ while (TRUE)
+ {
+ switch (poll(&fd, 1, milliseconds))
+ {
+ case 0:
+ ct->ct_error.re_status = RPC_TIMEDOUT;
+ return -1;
+
+ case -1:
+ if (errno == EINTR)
+ continue;
+ ct->ct_error.re_status = RPC_CANTRECV;
+ ct->ct_error.re_errno = errno;
+ return -1;
+ }
+ break;
+ }
+ switch (len = read (ct->ct_sock, buf, len))
+ {
+
+ case 0:
+ /* premature eof */
+ ct->ct_error.re_errno = ECONNRESET;
+ ct->ct_error.re_status = RPC_CANTRECV;
+ len = -1; /* it's really an error */
+ break;
+
+ case -1:
+ ct->ct_error.re_errno = errno;
+ ct->ct_error.re_status = RPC_CANTRECV;
+ break;
+ }
+ return len;
+}
+
+static int
+writetcp (char *ctptr, char *buf, int len)
+{
+ int i, cnt;
+ struct ct_data *ct = (struct ct_data*)ctptr;
+
+ for (cnt = len; cnt > 0; cnt -= i, buf += i)
+ {
+ if ((i = write (ct->ct_sock, buf, cnt)) == -1)
+ {
+ ct->ct_error.re_errno = errno;
+ ct->ct_error.re_status = RPC_CANTSEND;
+ return -1;
+ }
+ }
+ return len;
+}
--- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000
+++ src/sunrpc/clnt_tcp2.h 2007-01-30 15:26:09.000000000 +0000
@@ -0,0 +1,18 @@
+/*
+ * clnt_tcp2.h: Interface to the SunRPC over TCP.
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * See COPYING.LIB for the license of this software.
+ *
+ * Richard Jones <rjones(a)redhat.com>
+ */
+
+#ifndef __CLNT_TCP2_H__
+#define __CLNT_TCP2_H__
+
+extern CLIENT *clnttcp2_create (int af, struct sockaddr *raddr, int raddr_size,
+ u_long prog, u_long vers,
+ int *sockp, u_int sendsz, u_int recvsz);
+
+#endif /* __CLNT_TCP2_H__ */
--- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000
+++ src/sunrpc/create_xid.c 2007-01-30 13:21:07.000000000 +0000
@@ -0,0 +1,55 @@
+/*
+ * create_xid.c: Makes unique, unguessable identifiers. This is modified
+ * from the original source found in glibc-2.5. The original version
+ * is thread safe (but the rest of SunRPC isn't). This version is
+ * not thread safe.
+ *
+ * Modifications from glibc-2.5 base by Richard Jones <rjones(a)redhat.com>.
+ */
+
+/* Copyright (c) 1998, 2000 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Thorsten Kukuk <kukuk(a)vt.uni-paderborn.de>, 1998.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <bits/libc-lock.h>
+#include <rpc/rpc.h>
+
+static int is_initialized;
+static struct drand48_data __rpc_lrand48_data;
+
+unsigned long
+_create_xid (void)
+{
+ long int res;
+
+ if (!is_initialized)
+ {
+ struct timeval now;
+
+ gettimeofday (&now, (struct timezone *) 0);
+ srand48_r (now.tv_sec ^ now.tv_usec, &__rpc_lrand48_data);
+ is_initialized = 1;
+ }
+
+ lrand48_r (&__rpc_lrand48_data, &res);
+
+ return res;
+}
--- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000
+++ src/sunrpc/svc_gnutls.c 2007-01-30 17:03:35.000000000 +0000
@@ -0,0 +1,477 @@
+/*
+ * svc_gnutls.c: SunRPC over GnuTLS server. This is a modified version
+ * of svc_tcp.c from glibc which is written to run over GnuTLS.
+ *
+ * Modifications from glibc-2.5 base by Richard Jones <rjones(a)redhat.com>.
+ */
+
+/* @(#)svc_tcp.c 2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * svc_tcp.c, Server side for TCP/IP based RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * Actually implements two flavors of transporter -
+ * a tcp rendezvouser (a listener and connection establisher)
+ * and a record/tcp stream.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <libintl.h>
+#include <rpc/rpc.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#ifdef USE_IN_LIBIO
+# include <wchar.h>
+# include <libio/iolibio.h>
+#endif
+
+#include <gnutls/gnutls.h>
+#include "svc_gnutls.h"
+
+/*
+ * Ops vector for TCP/IP based rpc service handle
+ */
+static bool_t svctcp_recv (SVCXPRT *, struct rpc_msg *);
+static enum xprt_stat svctcp_stat (SVCXPRT *);
+static bool_t svctcp_getargs (SVCXPRT *, xdrproc_t, caddr_t);
+static bool_t svctcp_reply (SVCXPRT *, struct rpc_msg *);
+static bool_t svctcp_freeargs (SVCXPRT *, xdrproc_t, caddr_t);
+static void svctcp_destroy (SVCXPRT *);
+
+static const struct xp_ops svctcp_op =
+{
+ svctcp_recv,
+ svctcp_stat,
+ svctcp_getargs,
+ svctcp_reply,
+ svctcp_freeargs,
+ svctcp_destroy
+};
+
+/*
+ * Ops vector for TCP/IP rendezvous handler
+ */
+static bool_t rendezvous_request (SVCXPRT *, struct rpc_msg *);
+static enum xprt_stat rendezvous_stat (SVCXPRT *);
+static void svctcp_rendezvous_abort (void) __attribute__ ((__noreturn__));
+
+/* This function makes sure abort() relocation goes through PLT
+ and thus can be lazy bound. */
+static void
+svctcp_rendezvous_abort (void)
+{
+ abort ();
+};
+
+static const struct xp_ops svctcp_rendezvous_op =
+{
+ rendezvous_request,
+ rendezvous_stat,
+ (bool_t (*) (SVCXPRT *, xdrproc_t, caddr_t)) svctcp_rendezvous_abort,
+ (bool_t (*) (SVCXPRT *, struct rpc_msg *)) svctcp_rendezvous_abort,
+ (bool_t (*) (SVCXPRT *, xdrproc_t, caddr_t)) svctcp_rendezvous_abort,
+ svctcp_destroy
+};
+
+static int readtcp (char*, char *, int);
+static int writetcp (char *, char *, int);
+static SVCXPRT *makefd_xprt (int, u_int, u_int, gnutls_session_t);
+
+struct tcp_rendezvous
+ { /* kept in xprt->xp_p1 */
+ u_int sendsize;
+ u_int recvsize;
+ gnutls_certificate_credentials_t x509_cred;
+ };
+
+struct tcp_conn
+ { /* kept in xprt->xp_p1 */
+ enum xprt_stat strm_stat;
+ u_long x_id;
+ XDR xdrs;
+ char verf_body[MAX_AUTH_BYTES];
+ };
+
+/*
+ * Usage:
+ * xprt = svcgnutls_create(sock, send_buf_size, recv_buf_size,
+ * gnutls_certificate_credentials_t x509_cred);
+ *
+ * Creates, registers, and returns a (rpc) tcp based transporter.
+ * Once *xprt is initialized, it is registered as a transporter
+ * see (svc.h, xprt_register). This routine returns
+ * a NULL if a problem occurred.
+ *
+ * If sock<0 then a socket is created, else sock is used.
+ * If the socket, sock is not bound to a port then svctcp_create
+ * binds it to an arbitrary port. The routine then starts a tcp
+ * listener on the socket's associated port. In any (successful) case,
+ * xprt->xp_sock is the registered socket number and xprt->xp_port is the
+ * associated port number.
+ *
+ * Since tcp streams do buffered io similar to stdio, the caller can specify
+ * how big the send and receive buffers are via the second and third parms;
+ * 0 => use the system default.
+ */
+SVCXPRT *
+svcgnutls_create (int sock, u_int sendsize, u_int recvsize,
+ gnutls_certificate_credentials_t x509_cred)
+{
+ bool_t madesock = FALSE;
+ SVCXPRT *xprt;
+ struct tcp_rendezvous *r;
+ struct sockaddr_in addr;
+ socklen_t len = sizeof (struct sockaddr_in);
+
+ if (sock == RPC_ANYSOCK)
+ {
+ if ((sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
+ {
+ perror ("svc_gnutls.c - tcp socket creation problem");
+ return (SVCXPRT *) NULL;
+ }
+ madesock = TRUE;
+ }
+ bzero ((char *) &addr, sizeof (addr));
+ addr.sin_family = AF_INET;
+ if (bindresvport (sock, &addr))
+ {
+ addr.sin_port = 0;
+ (void) bind (sock, (struct sockaddr *) &addr, len);
+ }
+ if ((getsockname (sock, (struct sockaddr *) &addr, &len) != 0) ||
+ (listen (sock, SOMAXCONN) != 0))
+ {
+ perror ("svc_gnutls.c - cannot getsockname or listen");
+ if (madesock)
+ (void) close (sock);
+ return (SVCXPRT *) NULL;
+ }
+ r = (struct tcp_rendezvous *) mem_alloc (sizeof (*r));
+ xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT));
+ if (r == NULL || xprt == NULL)
+ {
+ (void) fprintf (stderr, "svcgnutls_create: out of memory\n");
+ mem_free (r, sizeof (*r));
+ mem_free (xprt, sizeof (SVCXPRT));
+ return NULL;
+ }
+ r->sendsize = sendsize;
+ r->recvsize = recvsize;
+ r->x509_cred = x509_cred;
+ xprt->xp_p2 = NULL;
+ xprt->xp_p1 = (caddr_t) r;
+ xprt->xp_verf = _null_auth;
+ xprt->xp_ops = &svctcp_rendezvous_op;
+ xprt->xp_port = ntohs (addr.sin_port);
+ xprt->xp_sock = sock;
+ xprt_register (xprt);
+ return xprt;
+}
+
+#if 0
+/*
+ * Like svtcp_create(), except the routine takes any *open* UNIX file
+ * descriptor as its first input.
+ */
+SVCXPRT *
+svcfd_create (int fd, u_int sendsize, u_int recvsize)
+{
+ return makefd_xprt (fd, sendsize, recvsize);
+}
+#endif
+
+static SVCXPRT *
+makefd_xprt (int fd, u_int sendsize, u_int recvsize,
+ gnutls_session_t session)
+{
+ SVCXPRT *xprt;
+ struct tcp_conn *cd;
+
+ xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT));
+ cd = (struct tcp_conn *) mem_alloc (sizeof (struct tcp_conn));
+ if (xprt == (SVCXPRT *) NULL || cd == NULL)
+ {
+ (void) fprintf (stderr, "svc_gnutls: makefd_xprt: out of memory\n");
+ mem_free (xprt, sizeof (SVCXPRT));
+ mem_free (cd, sizeof (struct tcp_conn));
+ return NULL;
+ }
+ cd->strm_stat = XPRT_IDLE;
+ xdrrec_create (&(cd->xdrs), sendsize, recvsize,
+ (caddr_t) xprt, readtcp, writetcp);
+ xprt->xp_p2 = (caddr_t) session;
+ xprt->xp_p1 = (caddr_t) cd;
+ xprt->xp_verf.oa_base = cd->verf_body;
+ xprt->xp_addrlen = 0;
+ xprt->xp_ops = &svctcp_op; /* truly deals with calls */
+ xprt->xp_port = 0; /* this is a connection, not a rendezvouser */
+ xprt->xp_sock = fd;
+ xprt_register (xprt);
+ return xprt;
+}
+
+// XXX DH_BITS must be defined the same in the server main loop too.
+#define DH_BITS 1024
+
+static gnutls_session_t
+initialize_tls_session (gnutls_certificate_credentials_t x509_cred)
+{
+ gnutls_session_t session;
+
+ gnutls_init (&session, GNUTLS_SERVER);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ gnutls_set_default_priority (session);
+
+ gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ /* request client certificate if any.
+ */
+ gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST);
+
+ gnutls_dh_set_prime_bits (session, DH_BITS);
+
+ return session;
+}
+
+static bool_t
+rendezvous_request (SVCXPRT *xprt,
+ struct rpc_msg *errmsg __attribute__((unused)))
+{
+ int sock;
+ struct tcp_rendezvous *r;
+ struct sockaddr_in addr;
+ socklen_t len;
+
+ r = (struct tcp_rendezvous *) xprt->xp_p1;
+ gnutls_session_t session = initialize_tls_session (r->x509_cred);
+
+again:
+ len = sizeof (struct sockaddr_in);
+ if ((sock = accept (xprt->xp_sock, (struct sockaddr *) &addr, &len)) < 0)
+ {
+ if (errno == EINTR)
+ goto again;
+ return FALSE;
+ }
+
+ gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) (long) sock);
+ int ret = gnutls_handshake (session);
+ if (ret < 0)
+ {
+ close (sock);
+ gnutls_deinit (session);
+ fprintf (stderr, "svc_gnutls: TLS handshake failed (%s)\n\n",
+ gnutls_strerror (ret));
+ return FALSE;
+ }
+
+ // XXX verify peer
+ fprintf (stderr, "XXX you must verify the peer\n");
+
+ /*
+ * make a new transporter (re-uses xprt)
+ */
+ xprt = makefd_xprt (sock, r->sendsize, r->recvsize, session);
+#if 0
+ memcpy (&xprt->xp_raddr, &addr, sizeof (addr));
+ xprt->xp_addrlen = len;
+#endif
+ return FALSE; /* there is never an rpc msg to be processed */
+}
+
+static enum xprt_stat
+rendezvous_stat (SVCXPRT *xprt __attribute__((unused)))
+{
+ return XPRT_IDLE;
+}
+
+static void
+svctcp_destroy (SVCXPRT *xprt)
+{
+ struct tcp_conn *cd = (struct tcp_conn *) xprt->xp_p1;
+ gnutls_session_t session = (gnutls_session_t) xprt->xp_p2;
+
+ gnutls_bye (session, GNUTLS_SHUT_WR);
+
+ xprt_unregister (xprt);
+ (void) close (xprt->xp_sock);
+ if (xprt->xp_port != 0)
+ {
+ /* a rendezvouser socket */
+ xprt->xp_port = 0;
+ }
+ else
+ {
+ /* an actual connection socket */
+ XDR_DESTROY (&(cd->xdrs));
+ }
+
+ gnutls_deinit (session);
+
+ mem_free ((caddr_t) cd, sizeof (struct tcp_conn));
+ mem_free ((caddr_t) xprt, sizeof (SVCXPRT));
+}
+
+
+/*
+ * reads data from the tcp connection.
+ * any error is fatal and the connection is closed.
+ * (And a read of zero bytes is a half closed stream => error.)
+ */
+static int
+readtcp (char *xprtptr, char *buf, int len)
+{
+ SVCXPRT *xprt = (SVCXPRT *)xprtptr;
+ gnutls_session_t session = (gnutls_session_t) xprt->xp_p2;
+ int sock = xprt->xp_sock;
+ int milliseconds = 35 * 1000;
+ struct pollfd pollfd;
+
+ do
+ {
+ pollfd.fd = sock;
+ pollfd.events = POLLIN;
+ switch (poll (&pollfd, 1, milliseconds))
+ {
+ case -1:
+ if (errno == EINTR)
+ continue;
+ /*FALLTHROUGH*/
+ case 0:
+ goto fatal_err;
+ default:
+ if ((pollfd.revents & POLLERR) || (pollfd.revents & POLLHUP)
+ || (pollfd.revents & POLLNVAL))
+ goto fatal_err;
+ break;
+ }
+ }
+ while ((pollfd.revents & POLLIN) == 0);
+
+ if ((len = gnutls_record_recv (session, buf, len)) > 0)
+ return len;
+
+ fatal_err:
+ ((struct tcp_conn *) (xprt->xp_p1))->strm_stat = XPRT_DIED;
+ return -1;
+}
+
+/*
+ * writes data to the tcp connection.
+ * Any error is fatal and the connection is closed.
+ */
+static int
+writetcp (char *xprtptr, char * buf, int len)
+{
+ SVCXPRT *xprt = (SVCXPRT *)xprtptr;
+ gnutls_session_t session = (gnutls_session_t) xprt->xp_p2;
+
+ if (gnutls_record_send (session, buf, len) < 0) {
+ ((struct tcp_conn *) (xprt->xp_p1))->strm_stat = XPRT_DIED;
+ return -1;
+ }
+
+ return len;
+}
+
+static enum xprt_stat
+svctcp_stat (SVCXPRT *xprt)
+{
+ struct tcp_conn *cd =
+ (struct tcp_conn *) (xprt->xp_p1);
+
+ if (cd->strm_stat == XPRT_DIED)
+ return XPRT_DIED;
+ if (!xdrrec_eof (&(cd->xdrs)))
+ return XPRT_MOREREQS;
+ return XPRT_IDLE;
+}
+
+static bool_t
+svctcp_recv (SVCXPRT *xprt, struct rpc_msg *msg)
+{
+ struct tcp_conn *cd = (struct tcp_conn *) (xprt->xp_p1);
+ XDR *xdrs = &(cd->xdrs);
+
+ xdrs->x_op = XDR_DECODE;
+ (void) xdrrec_skiprecord (xdrs);
+ if (xdr_callmsg (xdrs, msg))
+ {
+ cd->x_id = msg->rm_xid;
+ return TRUE;
+ }
+ cd->strm_stat = XPRT_DIED; /* XXXX */
+ return FALSE;
+}
+
+static bool_t
+svctcp_getargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
+{
+ return ((*xdr_args) (&(((struct tcp_conn *)
+ (xprt->xp_p1))->xdrs), args_ptr));
+}
+
+static bool_t
+svctcp_freeargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
+{
+ XDR *xdrs = &(((struct tcp_conn *) (xprt->xp_p1))->xdrs);
+
+ xdrs->x_op = XDR_FREE;
+ return ((*xdr_args) (xdrs, args_ptr));
+}
+
+static bool_t
+svctcp_reply (SVCXPRT *xprt, struct rpc_msg *msg)
+{
+ struct tcp_conn *cd = (struct tcp_conn *) (xprt->xp_p1);
+ XDR *xdrs = &(cd->xdrs);
+ bool_t stat;
+
+ xdrs->x_op = XDR_ENCODE;
+ msg->rm_xid = cd->x_id;
+ stat = xdr_replymsg (xdrs, msg);
+ (void) xdrrec_endofrecord (xdrs, TRUE);
+ return stat;
+}
--- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000
+++ src/sunrpc/svc_gnutls.h 2007-01-29 17:03:24.000000000 +0000
@@ -0,0 +1,17 @@
+/*
+ * svc_gnutls.h: Interface to the SunRPC over GnuTLS server.
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * See COPYING.LIB for the license of this software.
+ *
+ * Richard Jones <rjones(a)redhat.com>
+ */
+
+#ifndef __SVC_GNUTLS_H__
+#define __SVC_GNUTLS_H__
+
+extern SVCXPRT *svcgnutls_create (int sock, u_int sendsize, u_int recvsize,
+ gnutls_certificate_credentials_t x509_cred);
+
+#endif /* __SVC_GNUTLS_H__ */
--- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000
+++ src/sunrpc/svc_tcp2.c 2007-01-30 16:16:32.000000000 +0000
@@ -0,0 +1,426 @@
+/*
+ * svc_tcp2.c: SunRPC TCP server. This is a very slightly modified
+ * version of svc_tcp.c from glibc which removes some of the IPv4
+ * assumptions, so this version can be used over IPv4 or IPv6 sockets.
+ *
+ * Modifications from glibc-2.5 base by Richard Jones <rjones(a)redhat.com>.
+ */
+
+/* @(#)svc_tcp.c 2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * svc_tcp.c, Server side for TCP/IP based RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * Actually implements two flavors of transporter -
+ * a tcp rendezvouser (a listener and connection establisher)
+ * and a record/tcp stream.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <libintl.h>
+#include <rpc/rpc.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#ifdef USE_IN_LIBIO
+# include <wchar.h>
+# include <libio/iolibio.h>
+#endif
+
+#include "svc_tcp2.h"
+
+/*
+ * Ops vector for TCP/IP based rpc service handle
+ */
+static bool_t svctcp_recv (SVCXPRT *, struct rpc_msg *);
+static enum xprt_stat svctcp_stat (SVCXPRT *);
+static bool_t svctcp_getargs (SVCXPRT *, xdrproc_t, caddr_t);
+static bool_t svctcp_reply (SVCXPRT *, struct rpc_msg *);
+static bool_t svctcp_freeargs (SVCXPRT *, xdrproc_t, caddr_t);
+static void svctcp_destroy (SVCXPRT *);
+
+static const struct xp_ops svctcp_op =
+{
+ svctcp_recv,
+ svctcp_stat,
+ svctcp_getargs,
+ svctcp_reply,
+ svctcp_freeargs,
+ svctcp_destroy
+};
+
+/*
+ * Ops vector for TCP/IP rendezvous handler
+ */
+static bool_t rendezvous_request (SVCXPRT *, struct rpc_msg *);
+static enum xprt_stat rendezvous_stat (SVCXPRT *);
+static void svctcp_rendezvous_abort (void) __attribute__ ((__noreturn__));
+
+/* This function makes sure abort() relocation goes through PLT
+ and thus can be lazy bound. */
+static void
+svctcp_rendezvous_abort (void)
+{
+ abort ();
+};
+
+static const struct xp_ops svctcp_rendezvous_op =
+{
+ rendezvous_request,
+ rendezvous_stat,
+ (bool_t (*) (SVCXPRT *, xdrproc_t, caddr_t)) svctcp_rendezvous_abort,
+ (bool_t (*) (SVCXPRT *, struct rpc_msg *)) svctcp_rendezvous_abort,
+ (bool_t (*) (SVCXPRT *, xdrproc_t, caddr_t)) svctcp_rendezvous_abort,
+ svctcp_destroy
+};
+
+static int readtcp (char*, char *, int);
+static int writetcp (char *, char *, int);
+static SVCXPRT *makefd_xprt (int, u_int, u_int);
+
+struct tcp_rendezvous
+ { /* kept in xprt->xp_p1 */
+ u_int sendsize;
+ u_int recvsize;
+ };
+
+struct tcp_conn
+ { /* kept in xprt->xp_p1 */
+ enum xprt_stat strm_stat;
+ u_long x_id;
+ XDR xdrs;
+ char verf_body[MAX_AUTH_BYTES];
+ };
+
+/*
+ * Usage:
+ * xprt = svctcp_create(sock, send_buf_size, recv_buf_size);
+ *
+ * Creates, registers, and returns a (rpc) tcp based transporter.
+ * Once *xprt is initialized, it is registered as a transporter
+ * see (svc.h, xprt_register). This routine returns
+ * a NULL if a problem occurred.
+ *
+ * If sock<0 then a socket is created, else sock is used.
+ * If the socket, sock is not bound to a port then svctcp_create
+ * binds it to an arbitrary port. The routine then starts a tcp
+ * listener on the socket's associated port. In any (successful) case,
+ * xprt->xp_sock is the registered socket number and xprt->xp_port is the
+ * associated port number.
+ *
+ * Since tcp streams do buffered io similar to stdio, the caller can specify
+ * how big the send and receive buffers are via the second and third parms;
+ * 0 => use the system default.
+ */
+SVCXPRT *
+svctcp2_create (int sock, u_int sendsize, u_int recvsize)
+{
+ bool_t madesock = FALSE;
+ SVCXPRT *xprt;
+ struct tcp_rendezvous *r;
+ struct sockaddr_in addr;
+ socklen_t len = sizeof (struct sockaddr_in);
+
+ if (sock == RPC_ANYSOCK)
+ {
+ if ((sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
+ {
+ perror ("svc_tcp.c - tcp socket creation problem");
+ return (SVCXPRT *) NULL;
+ }
+ madesock = TRUE;
+
+ bzero ((char *) &addr, sizeof (addr));
+ addr.sin_family = AF_INET;
+ if (bindresvport (sock, &addr))
+ {
+ addr.sin_port = 0;
+ (void) bind (sock, (struct sockaddr *) &addr, len);
+ }
+ if ((getsockname (sock, (struct sockaddr *) &addr, &len) != 0) ||
+ (listen (sock, SOMAXCONN) != 0))
+ {
+ perror ("svc_tcp.c - cannot getsockname or listen");
+ if (madesock)
+ (void) close (sock);
+ return (SVCXPRT *) NULL;
+ }
+ }
+ r = (struct tcp_rendezvous *) mem_alloc (sizeof (*r));
+ xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT));
+ if (r == NULL || xprt == NULL)
+ {
+ (void) fprintf (stderr, "svctcp_create: out of memory\n");
+ mem_free (r, sizeof (*r));
+ mem_free (xprt, sizeof (SVCXPRT));
+ return NULL;
+ }
+ r->sendsize = sendsize;
+ r->recvsize = recvsize;
+ xprt->xp_p2 = NULL;
+ xprt->xp_p1 = (caddr_t) r;
+ xprt->xp_verf = _null_auth;
+ xprt->xp_ops = &svctcp_rendezvous_op;
+ xprt->xp_port = ntohs (addr.sin_port);
+ xprt->xp_sock = sock;
+ xprt_register (xprt);
+ return xprt;
+}
+
+#if 0
+/*
+ * Like svtcp_create(), except the routine takes any *open* UNIX file
+ * descriptor as its first input.
+ */
+SVCXPRT *
+svcfd_create (int fd, u_int sendsize, u_int recvsize)
+{
+ return makefd_xprt (fd, sendsize, recvsize);
+}
+#endif
+
+static SVCXPRT *
+makefd_xprt (int fd, u_int sendsize, u_int recvsize)
+{
+ SVCXPRT *xprt;
+ struct tcp_conn *cd;
+
+ xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT));
+ cd = (struct tcp_conn *) mem_alloc (sizeof (struct tcp_conn));
+ if (xprt == (SVCXPRT *) NULL || cd == NULL)
+ {
+ (void) fprintf (stderr, "svc_tcp: makefd_xprt: out of memory\n");
+ mem_free (xprt, sizeof (SVCXPRT));
+ mem_free (cd, sizeof (struct tcp_conn));
+ return NULL;
+ }
+ cd->strm_stat = XPRT_IDLE;
+ xdrrec_create (&(cd->xdrs), sendsize, recvsize,
+ (caddr_t) xprt, readtcp, writetcp);
+ xprt->xp_p2 = NULL;
+ xprt->xp_p1 = (caddr_t) cd;
+ xprt->xp_verf.oa_base = cd->verf_body;
+ xprt->xp_addrlen = 0;
+ xprt->xp_ops = &svctcp_op; /* truly deals with calls */
+ xprt->xp_port = 0; /* this is a connection, not a rendezvouser */
+ xprt->xp_sock = fd;
+ xprt_register (xprt);
+ return xprt;
+}
+
+static bool_t
+rendezvous_request (SVCXPRT *xprt,
+ struct rpc_msg *errmsg __attribute__((unused)))
+{
+ int sock;
+ struct tcp_rendezvous *r;
+ struct sockaddr_in addr;
+ socklen_t len;
+
+ r = (struct tcp_rendezvous *) xprt->xp_p1;
+again:
+ len = sizeof (struct sockaddr_in);
+ if ((sock = accept (xprt->xp_sock, (struct sockaddr *) &addr, &len)) < 0)
+ {
+ if (errno == EINTR)
+ goto again;
+ return FALSE;
+ }
+ /*
+ * make a new transporter (re-uses xprt)
+ */
+ xprt = makefd_xprt (sock, r->sendsize, r->recvsize);
+#if 0
+ memcpy (&xprt->xp_raddr, &addr, sizeof (addr));
+ xprt->xp_addrlen = len;
+#endif
+ return FALSE; /* there is never an rpc msg to be processed */
+}
+
+static enum xprt_stat
+rendezvous_stat (SVCXPRT *xprt __attribute__((unused)))
+{
+ return XPRT_IDLE;
+}
+
+static void
+svctcp_destroy (SVCXPRT *xprt)
+{
+ struct tcp_conn *cd = (struct tcp_conn *) xprt->xp_p1;
+
+ xprt_unregister (xprt);
+ (void) close (xprt->xp_sock);
+ if (xprt->xp_port != 0)
+ {
+ /* a rendezvouser socket */
+ xprt->xp_port = 0;
+ }
+ else
+ {
+ /* an actual connection socket */
+ XDR_DESTROY (&(cd->xdrs));
+ }
+ mem_free ((caddr_t) cd, sizeof (struct tcp_conn));
+ mem_free ((caddr_t) xprt, sizeof (SVCXPRT));
+}
+
+
+/*
+ * reads data from the tcp connection.
+ * any error is fatal and the connection is closed.
+ * (And a read of zero bytes is a half closed stream => error.)
+ */
+static int
+readtcp (char *xprtptr, char *buf, int len)
+{
+ SVCXPRT *xprt = (SVCXPRT *)xprtptr;
+ int sock = xprt->xp_sock;
+ int milliseconds = 35 * 1000;
+ struct pollfd pollfd;
+
+ do
+ {
+ pollfd.fd = sock;
+ pollfd.events = POLLIN;
+ switch (poll (&pollfd, 1, milliseconds))
+ {
+ case -1:
+ if (errno == EINTR)
+ continue;
+ /*FALLTHROUGH*/
+ case 0:
+ goto fatal_err;
+ default:
+ if ((pollfd.revents & POLLERR) || (pollfd.revents & POLLHUP)
+ || (pollfd.revents & POLLNVAL))
+ goto fatal_err;
+ break;
+ }
+ }
+ while ((pollfd.revents & POLLIN) == 0);
+
+ if ((len = read (sock, buf, len)) > 0)
+ return len;
+
+ fatal_err:
+ ((struct tcp_conn *) (xprt->xp_p1))->strm_stat = XPRT_DIED;
+ return -1;
+}
+
+/*
+ * writes data to the tcp connection.
+ * Any error is fatal and the connection is closed.
+ */
+static int
+writetcp (char *xprtptr, char * buf, int len)
+{
+ SVCXPRT *xprt = (SVCXPRT *)xprtptr;
+ int i, cnt;
+
+ for (cnt = len; cnt > 0; cnt -= i, buf += i)
+ {
+ if ((i = write (xprt->xp_sock, buf, cnt)) < 0)
+ {
+ ((struct tcp_conn *) (xprt->xp_p1))->strm_stat = XPRT_DIED;
+ return -1;
+ }
+ }
+ return len;
+}
+
+static enum xprt_stat
+svctcp_stat (SVCXPRT *xprt)
+{
+ struct tcp_conn *cd =
+ (struct tcp_conn *) (xprt->xp_p1);
+
+ if (cd->strm_stat == XPRT_DIED)
+ return XPRT_DIED;
+ if (!xdrrec_eof (&(cd->xdrs)))
+ return XPRT_MOREREQS;
+ return XPRT_IDLE;
+}
+
+static bool_t
+svctcp_recv (SVCXPRT *xprt, struct rpc_msg *msg)
+{
+ struct tcp_conn *cd = (struct tcp_conn *) (xprt->xp_p1);
+ XDR *xdrs = &(cd->xdrs);
+
+ xdrs->x_op = XDR_DECODE;
+ (void) xdrrec_skiprecord (xdrs);
+ if (xdr_callmsg (xdrs, msg))
+ {
+ cd->x_id = msg->rm_xid;
+ return TRUE;
+ }
+ cd->strm_stat = XPRT_DIED; /* XXXX */
+ return FALSE;
+}
+
+static bool_t
+svctcp_getargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
+{
+ return ((*xdr_args) (&(((struct tcp_conn *)
+ (xprt->xp_p1))->xdrs), args_ptr));
+}
+
+static bool_t
+svctcp_freeargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
+{
+ XDR *xdrs = &(((struct tcp_conn *) (xprt->xp_p1))->xdrs);
+
+ xdrs->x_op = XDR_FREE;
+ return ((*xdr_args) (xdrs, args_ptr));
+}
+
+static bool_t
+svctcp_reply (SVCXPRT *xprt, struct rpc_msg *msg)
+{
+ struct tcp_conn *cd = (struct tcp_conn *) (xprt->xp_p1);
+ XDR *xdrs = &(cd->xdrs);
+ bool_t stat;
+
+ xdrs->x_op = XDR_ENCODE;
+ msg->rm_xid = cd->x_id;
+ stat = xdr_replymsg (xdrs, msg);
+ (void) xdrrec_endofrecord (xdrs, TRUE);
+ return stat;
+}
--- /tmp/newfile 2007-01-30 17:23:46.000000000 +0000
+++ src/sunrpc/svc_tcp2.h 2007-01-30 16:14:22.000000000 +0000
@@ -0,0 +1,16 @@
+/*
+ * svc_tcp2.h: Interface to the SunRPC over TCP server.
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * See COPYING.LIB for the license of this software.
+ *
+ * Richard Jones <rjones(a)redhat.com>
+ */
+
+#ifndef __SVC_TCP2_H__
+#define __SVC_TCP2_H__
+
+extern SVCXPRT *svctcp2_create (int sock, u_int sendsize, u_int recvsize);
+
+#endif /* __SVC_TCP2_H__ */
17 years, 10 months
Re: [Libvir] what is virt-install doing differently?
by Aron Griffis
Hi Daniel,
Daniel P. Berrange wrote: [Tue Jan 23 2007, 10:49:07AM EST]
> For HVM installs, the different should only be the addition of the
> CDROM device. If you take a look in /var/log/xen/xend.log it should
> show you the final SEXPR that XenD is getting when creating the
> guest. Comparing the SEXPR seen when booting the config file, vs
> that generated by virt-intsall may shed some more light on the
> problem.
>
> Also, virt-install has a --debug flag which will make it show the libvirt
> XML description it used to boot.
Thanks for these suggestions. Yongkang looked at xend.log and found
this:
You, Yongkang wrote: [Thu Jan 25 2007, 10:26:33AM EST]
> Thanks for the information. I have check /var/log/xen/xend.log.
> Found something interesting. When using virt-install to install VTI
> domain, it seemed didn't parse the right vcpu number to qemu-dm.
> Please look at the following log:
>
> 1. The spawning qemu log, when installation by virt-intall with vcpu=4:
> ---------------------
> [2007-01-25 23:14:13 xend 2817] INFO (image:418) spawning device models: /usr/lib/xen/bin/qemu-dm ['/usr/lib/xen/bin/qemu-dm', '-d', '9', '-m', '1024', '-boot', 'c', '-acpi', '-domain-name', 'test', '-net', 'nic,vlan=1,macaddr=00:16:3e:0a:d2:86,model=rtl8139', '-net', 'tap,vlan=1,bridge=xenbr0', '-vncunused', '-vnclisten', '127.0.0.1']
> ~~~~~~~~~~~~
>
> 2. The spawning qemu log, when use command "xm create":
> ------------------------
> [2007-01-25 17:13:13 xend 2817] INFO (image:418) spawning device models: /usr/lib/xen/bin/qemu-dm ['/usr/lib/xen/bin/qemu-dm', '-d', '7', '-m', '1024', '-boot', 'd', '-vcpus', '4', '-acpi', '-domain-name', 'ExampleVTIDomain', '-net', 'nic,vlan=1,macaddr=00:16:3e:31:42:c4,model=rtl8139', '-net', 'tap,vlan=1,bridge=xenbr0', '-vncviewer']
> ~~~~~~~~~~~
>
> There is "-vcpus 4" in the 2nd log. It means qemu would create up
> a 4 vpucs VTI domain.
>
> For the situation 1, although qemu doesn't get any information about
> "vcpu=4". But xen has already prepared 2 vcpus to qemu. Will it be
> the problem?
I'm looking through the code now to see if I can find where this is
going awry, but while I'm looking, does this ring a bell for you?
Thanks,
Aron
17 years, 11 months
[Libvir] libvirtd - configuration file
by Richard W.M. Jones
Libvirtd needs a configuration file. It's very simple - just to store
the services on which it's listening, and optionally to configure port
numbers and paths.
Libvirt so far doesn't have a concept of a configuration file, except
that obviously domain descriptions come out of XML files.
Do people on list have a preference for a style of configuration file?
An alternative is to configure entirely via the command line.
Rich.
PS: Personally when administering a machine there is nothing worse than
finding an XML configuration file ...
--
Emerging Technologies, Red Hat http://et.redhat.com/~rjones/
64 Baker Street, London, W1U 7DF Mobile: +44 7866 314 421
"[Negative numbers] darken the very whole doctrines of the equations
and make dark of the things which are in their nature excessively
obvious and simple" (Francis Maseres FRS, mathematician, 1759)
17 years, 11 months
[Libvir] Problem in configuring libvirt (Xen store library not found)
by aditya aekkaldevi
Hello,
First of all I am a newbie to linux. I installed Red hat
enterprise 5 beta 2 which has xen. So in order to start xen I had to install
libvirt as it is needed by virt-install command. But I am getting an error
while configuring
libvirt software. The error is "Xen store library not found". I am
getting this error after a check for library containing xs_read failed. So
how can I configure and install libvirt now. Please
let me know the solution for this ASAP. Also, if I am doing anything
wrong, please let me know.
Thanks in advance
Aditya
17 years, 11 months
[Libvir] Automated build script
by Daniel P. Berrange
I'm going to setup libvirt to be part of an automated build system[1] which
will checkout the code, perform a build, run tests, etc, etc on a 24x7
basis. This merely requires adding a single file 'autobuild.sh' to the
top level directory to perform the neccessary build/test tasks (see
attached file).
As part of this automation I'll have it setup to run all the test suites
under valgrind which should let us identify memory leaks sooner. Finally
I also plan to add in the Makefile rules[2] neccessary to run gcov so as to
enable reports to be generated on test suite coverage.
Regards,
Dan.
[1] http://autobuild.org/
[2] Hmm, i'm sure i sent such a patch before but can't find any trace of
it in the mail archives.
--
|=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=|
|=- Perl modules: http://search.cpan.org/~danberr/ -=|
|=- Projects: http://freshmeat.net/~danielpb/ -=|
|=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|
17 years, 11 months
[Libvir] Nothing ever calls the driver ->init function, right?
by Richard W.M. Jones
... or at least I can't see it being called anywhere.
Rich.
--
Emerging Technologies, Red Hat http://et.redhat.com/~rjones/
64 Baker Street, London, W1U 7DF Mobile: +44 7866 314 421
"[Negative numbers] darken the very whole doctrines of the equations
and make dark of the things which are in their nature excessively
obvious and simple" (Francis Maseres FRS, mathematician, 1759)
17 years, 11 months
[Libvir] Issue with creating domains that use pygrub
by Chris Halstead
I'm stumped. I'm trying to automate creation of domains through libvirt and
the Python bindings, but I'm having trouble getting things to work when
using pygrub.
If I define an XML-described domain and use
<bootloader>/usr/bin/pygrub</bootloader> the domain tries to start and then
immediately crashes. Using explicit local-pathed vmlinuz/initrd without the
bootloader element works just fine. Am I missing something?
The version of libvirt on my test system in 0.1.9.
Thanks!
C. Halstead <chris(a)sourcelabs.com>
SourceLabs - http://www.sourcelabs.com
Dependable Open Source Systems
17 years, 11 months
[Libvir] Option string in getopt_long wrong?
by Richard W.M. Jones
while ((arg = getopt_long(end, argv, "d:hqtv", opt, &idx)) != -1) {
------
switch (arg) {
case 'd':
ctl->debug = atoi(optarg);
break;
case 'h':
help = 1;
break;
case 'q':
ctl->quiet = TRUE;
break;
case 't':
ctl->timing = TRUE;
break;
case 'c':
ctl->name = vshStrdup(ctl, optarg);
break;
// etc
Shurley shome mishtake?
Rich.
--
Emerging Technologies, Red Hat http://et.redhat.com/~rjones/
64 Baker Street, London, W1U 7DF Mobile: +44 7866 314 421
"[Negative numbers] darken the very whole doctrines of the equations
and make dark of the things which are in their nature excessively
obvious and simple" (Francis Maseres FRS, mathematician, 1759)
17 years, 11 months
[Libvir] Virtual networking
by Mark McLoughlin
Hi,
Dan and I have been discussing how to "fix networking", not just Xen's
networking but also getting something sane wrt. QEMU/KVM etc.
Comments very welcome on the writeup below. The libvirt stuff is
towards the end, but I think all of it is probably useful to this list.
Cheers,
Mark.
Virtual Networking
The ability to manage virtual machines is something which is receiving a
lot of focus right now. Xen, KVM, QEMU and others provide the
infrastructure required to run a virtual machine, and each can provide
guests with a virtual network interface. This proposal addresses the
problem of how guests are networked together.
We aim:
* To make virtual networking "just work".
Guests should be able to communicate with each other, their host
and the Internet without any fuss or configuration. This should
be the case even with laptops and offline machines.
* To allow a greater flexibily with how guests are networked.
It should be possible to isolate groups of guests in different
networks, allow guests on different physical machines to
communicate, firewall guests' networks from physical networks or
allow guests to appear just like physical machines on physical
networks.
* To make networking virtual machines analogous with networking
physical machines.
* To support inter-networking between virtualisation technologies.
User Visible Concepts
=====================
It's important to consider the manner in which we expose the
functionality of virtual networking. What concepts will be exposing
through the UI? Are those concepts well defined and consistent? Are
those concepts more complex than neccessary? Or are the too simple to be
able to support the functionality we want?
Real world, or "physical", concepts[1]:
* Network - a number of interconnected machines.
* Network Interface - hardware which enables a machine to connect
to a network.
* Bridge - hardware which allows enables the interconnection of
machines to form a network. Bridges can also be connected to
other bridges to form a larger network.
* Router - hardware which connects two or more distinct networks,
allowing machines on different networks to communicate with one
another. Sometimes a router and a bridge are available as a
combined piece of hardware - the bridge forms a network and the
router connects that network to another distinct network.
* Firewall - software on a router which can be used to control how
machines on an "external" network (e.g. the Internet) can
communicate with machines on an "internal" network. For a given
type of connection, you can choose to disallow connections of a
that type or forward them to a specific internal machine. Can
also be used to control how internal machines can communicate
with external machines.
With virtual networking, we will be exposing the following "virtual"
concepts:
* Virtual Network - a number of interconnected virtual machines.
* Virtual Network Interface - a network interface in a virtual
machine.
* Virtual Bridge - allows the interconnection of virtual machines
to form a virtual network. A virtual bridge may be configured to
also act as a virtual router and firewall. A virtual bridge may
also be connected to another virtual bridge (perhaps on another
physical machine) to create a larger virtual network.
(Note, unprivileged users may create any of the above)
Finally, where the physical world meets the virtual world:
* Shared Physical Interface - if a physical interface is
configured to be "shared", then any number of virtual interfaces
may be connected to it allowing virtual machines to be connected
to the same physical network which the physical interface is
connected to.
Only privileged users may configure a physical interface to be
shared and/or connect guests to it.
There are a few problems with all of the above:
1. The distinction between a bridge and a router requires a lot of
technical knowledge to fully understand. However, the model of
e.g. a LinkSys router is familiar to a lot of people - a box
which allows you to network your machines together and connect
that network to (and firewall off) the Internet.
2. This "shared physical interface" notion is very "makey upey". We
could perhaps talk about the idea in terms of connecting a
physical interface to a virtual bridge, but it exposes the
bridge vs. router distinction more than we'd like.
3. Guests are connected to a specific physical interface, whereas
perhaps users wish guests to be connected to "the network" -
i.e. if NetworkManager switched from wireless to wired while
remaining on the same subnet, perhaps we'd like to automatically
switch the bridge to the new network. In reality, though,
bridged networking is only really sane for machines on a fairly
static network connection.
[1] - Yes, these definitions aren't entirely accurate, but they describe
the kind of understanding a moderately technical user might have of the
concepts.
Example Networks
================
Below are some example networks users may configure and an explanation
of how that network would be implemented in practice.
1. A privileged user creates two (Xen) guests, each with a Virtual
Network Interface. Without any special networking configuration,
these two guests are connected to a default Virtual Network
which contains a combined Virtual Bridge/Router/Firewall.
+-----------+ D +-----------+
| Guest | N D H | Guest |
| A | A N C | B |
| +---+ | T S P | +---+ |
| |NIC| | ^ ^ ^ | |NIC| |
+---+-+-+---+ +---+---+ +---+-+-+---+
^ | ^
| +--------+ +---+---+ +--------+ |
+-->+ vif1.0 +----+ vnbr0 +----+ vif2.0 +<--+
+--------+ +-------+ +--------+
Notes:
* "vnbr0" is a bridge device with it's own IP address on
the same subnet as the guests.
* IP forwarding is enabled in Dom0. Masquerading and DNAT
is implemented using iptables.
* We run a DHCP server and a DNS proxy in Dom0 (e.g.
dnsmasq)
2. A privileged user does exactly the same thing as (1), but with
QEMU guests.
D
N D H
A N C
T S P
^ ^ ^
+---+---+
|
+---+---+
+-----------+ | vnbr0 | +-----------+
| Guest | +---+---+ | Guest |
| A | | | B |
| +---+ | +---+---+ | +---+ |
| |NIC| | | vtap0 | | |NIC| |
+---+-+-+---+ +---+---+ +---+-+-+---+
^ +-------+ | +-------+ ^
| | | +---+---+ | | |
+------>+ VLAN0 +-+ VDE +-+ VLAN0 +<------+
| | +-------+ | |
+-------+ +-------+
Notes:
* VDE is a userspace ethernet bridge implemented using
vde_switch
* "vtap0" is a TAP device created by vde_switch
* Everything else is the same as (1)
* This could be done without vde_switch by having Guest A
create vtap0 and have Guest B connect directly to Guest
A's VLAN. However, if Guest A is shut down, Guest B's
network would go down.
3. An unprivileged user does exactly the same thing as (2).
+-----------+ +-----------+
| Guest | +----+----+ | Guest |
| A | |userspace| | B |
| +---+ | | network | | +---+ |
| |NIC| | | stack | | |NIC| |
+---+-+-+---+ +----+----+ +---+-+-+---+
^ +-------+ | +-------+ ^
| | | +---+---+ | | |
+------>+ VLAN0 +-+ VDE +-+ VLAN0 +<------+
| | +-------+ | |
+-------+ +-------+
Notes:
* Similar to (2) except there is can be no TAP device or
bridge
* The userspace network stack is implemented using
slirpvde to provide a DHCP server and DNS proxy to the
network, but also effectively a SNAT and DNAT router.
* slirpvde implements ethernet, ip, tcp, udp, icmp, dhcp,
tftp (etc.) in userspace. Completely crazy, but since
the kernel apparently has no secure way to allow
unprivileged users to leverage the kernel's network
stack for this, then it must be done in userspace.
4. Same as (2), except the user also creates two Xen guests.
+-----------+ D +-----------+
| Guest | N D H | Guest |
| A | A N C | B |
| +---+ | T S P | +---+ |
| |NIC| | ^ ^ ^ | |NIC| |
+---+-+-+---+ +---+---+ +---+-+-+---+
^ | ^
| +--------+ +---+---+ +--------+ |
+-->+ vif1.0 +----+ vnbr0 +----+ vif2.0 +<--+
+--------+ +---+---+ +--------+
|
+---+---+
| vtap0 |
+---+---+
|
+-------+ +--+--+ +-------+
+---->+ VLAN0 +----+ VDE +---+ VLAN0 +<-----+
| +-------+ +-----+ +-------+ |
V V
+---+-+-+---+ +---+-+-+---+
| |NIC| | | |NIC| |
| +---+ | | +---+ |
| Guest | | Guest |
| C | | D |
+-----------+ +-----------+
Notes:
* In this case we could do away with VDE and have each
QEMU guest use its own TAP device.
5. Same as (3) except Guests A and C are connected to a Shared
Physical Interface.
+-----------+ | D +-----------+
| Guest | ^ | N D H | Guest |
| A | | | A N C | B |
| +---+ | +---+---+ | T S P | +---+ |
| |NIC| | | eth0 | | ^ ^ ^ | |NIC| |
+---+-+-+---+ +---+---+ | +---+---+ +---+-+-+---+
^ | | | ^
| +--------+ +---+---+ | +---+---+ +--------+ |
+>+ vif1.0 +-+ ebr0 + | + vnbr0 +-+ vif2.0 +<-+
+--------+ +---+---+ | +---+---+ +--------+
| | |
+---+---+ | +---+---+
| vtap1 | | | vtap0 |
+---+---+ | +---+---+
| | |
+-------+ +--+--+ | +--+--+ +-------+
+->+ VLAN0 +--+ VDE + | + VDE +--+ VLAN0 +<-+
| +-------+ +-----+ | +-----+ +-------+ |
V | V
+---+-+-+---+ | +---+-+-+---+
| |NIC| | | | |NIC| |
| +---+ | | | +---+ |
| Guest | | | Guest |
| C | | | D |
+-----------+ | +-----------+
Notes:
* The idea here is that when the admin configures eth0 to
be shareable, eth0 is configured as an addressless NIC
enslaved to a bridge which has the MAC address and IP
address that eth0 should have
* Again, VDE is redundant here.
6. Same as 2) except the QEMU guests are on a Virtual Network on
another physical machine which is, in turn, connected to the
Virtual Network on the first physical machine
+-----------+ D +-----------+
| Guest | N D H | Guest |
| A | A N C | B |
| +---+ | T S P | +---+ |
| |NIC| | ^ ^ ^ | |NIC| |
+---+-+-+---+ +---+---+ +---+-+-+---+
^ | ^
| +--------+ +---+---+ +--------+ |
+-->+ vif1.0 +----+ vnbr0 +----+ vif2.0 +<--+
+--------+ +---+---+ +--------+
|
+---+---+
| vtap0 |
+---+---+
|
+--+--+
| VDE |
+--+--+
|
First Physical Machine V
-------------------------------------------------------------
Second Physical Machine ^
|
+-------+ +--+--+ +-------+
+---->+ VLAN0 +----+ VDE +---+ VLAN0 +<-----+
| +-------+ +-----+ +-------+ |
V V
+---+-+-+---+ +---+-+-+---+
| |NIC| | | |NIC| |
| +---+ | | +---+ |
| Guest | | Guest |
| C | | D |
+-----------+ +-----------+
Notes:
* What's going on here is that the two VDEs are connected
over the network, either via a plan socket or perhaps
encapsulated in another protocol like SSH or TLS
One interesting thing to note from all of those examples is that
although QEMU's networking options are very interesting, it doesn't
actually make sense for a network to be implemented inside a guest. The
network needs to be external to any guests, and so we use VDE to offer
similar networking options to the ones QEMU provides. All QEMU needs to
be able to do is to connect to VDE.
User Interface
==============
This isn't meant a UI specification, but just some notes on how this
stuff might be exposed in virt-manager.
* Networks List:
* Name
* Virtual/Physical
* Status
* Activity/traffic
* Virtual Network Configuration:
* Name
* List of connected guests
* Allow other Virtual Networks to connect to this
(defaults to no)
* Connect to other Virtual Network (defaults to none)
* DHCP enabled - DHCP configuration:
* IP range (optional)
* Router IP address (optional)
* Guest IP address/hostname assignment (optional)
* Forwarding enabled - firewall configuration:
* Incoming ports list and destination guest+port
for each (defaults to empty)
* Blocked outgoing ports lists (defaults to empty)
* Virtual NICs list:
* Guest interface name
* Virtual Network/Shared Physical Interface
* Hostname (defaults to guest name)
* IP address (if assigned)
* MAC address (if assigned)
* Virtual NIC Configuration:
* Random MAC address, or user-supplied MAC address.
* Virtual Network or Shared Physical Interface to connect
to.
Implementation
==============
Parity with the current state of networking with Xen will be achieved
by:
* Implementing "shared physical interface" support in Fedora's
initscripts and network configuration tool. It boils down to
configuring the interface (e.g. eth0) something like:
ifcfg-peth0:
DEVICE=peth0
ONBOOT=yes
Bridge=eth0
HWADDR=00:30:48:30:73:19
ifcfg-eth0
DEVICE=eth0
Type=Bridge
ONBOOT=yes
BOOTPROTO=dhcp
* Fixing Xen so that netloop is no longer required. Upstream have
ideas about how to make Xen automatically copy any frames that
are destined for Dom0 so that the netback driver doesn't run out
of shared pages if Dom0 doesn't process the frames quickly
enough.
* Create new network/vif scripts for Xen which will connect guests
to a shared physical interface's bridge.
Virtual Networks will be implemented in libvirt. First, there will be an
XML description of Virtual Networks e.g.:
<network id="0">
<name>Foo</name>
<uuid>596a5d2171f48fb2e068e2386a5c413e</uuid>
<listen address="172.31.0.5" port="1234" />
<connections>
<connection address="172.31.0.6" port="4321" />
</conections>
<dhcp enabled="true">
<ip address="10.0.0.1"
netmask="255.255.255.0"
start="10.0.0.128"
end="10.0.0.254" />
</dhcp>
<forwarding enabled="true">
<incoming default="deny">
<allow port="123" domain="foobar" destport="321" />
</incoming>
<outgoing default="allow">
<deny port="25" />
</outgoing>
</forwarding>
<network>
In a manner similar to libvirt's QEMU support, there will be a daemon to
manage Virtual Networks. The daemon will have access to a store of
network definitions. The daemon will be responsible for managing the
bridge devices, vde_switch/dhcp/dnses processes and the iptables rules
needed for SNAT/DNAT etc.
virsh command line interface would look like:
$> virsh network-create foo.xml
$> virsh network-dumpxml > foo.xml
$> virsh network-define foo.xml
$> virsh network-list
$> virsh network-start Foo
$> virsh network-stop Foo
$> virsh network-restart Foo
The libvirt API for virtual networks would be modelled on the API for
virtual machines:
/*
* Virtual Networks API
*/
/**
* virNetwork:
*
* a virNetwork is a private structure representing a virtual network.
*/
typedef struct _virNetwork virNetwork;
/**
* virNetworkPtr:
*
* a virNetworkPtr is pointer to a virNetwork private structure, this is the
* type used to reference a virtual network in the API.
*/
typedef virNetwork *virNetworkPtr;
/**
* virNetworkCreateFlags:
*
* Flags OR'ed together to provide specific behaviour when creating a
* Network.
*/
typedef enum {
VIR_NETWORK_NONE = 0
} virNetworkCreateFlags;
/*
* List active networks
*/
int virConnectNumOfNetworks (virConnectPtr conn);
int virConnectListNetworks (virConnectPtr conn,
int *ids,
int maxids);
/*
* List inactive networks
*/
int virConnectNumOfDefinedNetworks (virConnectPtr conn);
int virConnectListDefinedNetworks (virConnectPtr conn,
const char **names,
int maxnames);
/*
* Lookup network by name, id or uuid
*/
virNetworkPtr virNetworkLookupByName (virConnectPtr conn,
const char *name);
virNetworkPtr virNetworkLookupByID (virConnectPtr conn,
int id);
virNetworkPtr virNetworkLookupByUUID (virConnectPtr conn,
const unsigned char *uuid);
virNetworkPtr virNetworkLookupByUUIDString (virConnectPtr conn,
const char *uuid);
/*
* Create active transient network
*/
virNetworkPtr virNetworkCreateXML (virConnectPtr conn,
const char *xmlDesc,
unsigned int flags);
/*
* Define inactive persistent network
*/
virNetworkPtr virNetworkDefineXML (virConnectPtr conn,
const char *xmlDesc);
/*
* Delete persistent network
*/
int virNetworkUndefine (virNetworkPtr network);
/*
* Activate persistent network
*/
int virNetworkCreate (virNetworkPtr network);
/*
* Network destroy/free
*/
int virNetworkDestroy (virNetworkPtr network);
int virNetworkFree (virNetworkPtr network);
/*
* Network informations
*/
const char* virNetworkGetName (virNetworkPtr network);
unsigned int virNetworkGetID (virNetworkPtr network);
int virNetworkGetUUID (virNetworkPtr network,
unsigned char *uuid);
int virNetworkGetUUIDString (virNetworkPtr network,
char *buf);
char * virNetworkGetXMLDesc (virNetworkPtr network,
int flags);
Discussion points on the XML format and API:
* The XML format isn't thought out at all, but briefly:
* The <listen> and <connections> elements describe
networks connected across physical machine boundaries.
* The <dhcp> element describes the configuration of the
DHCP server on the network.
* The <forwarding> element describes how incoming and
outgoing connections are forwarded.
* Since virConnect is supposed to be a connection to a specific
hypervisor, does it make sense to create networks (which should
be hypervisor agnostic) through virConnect?
* Are we needlessly replicating any mistakes from the domains API
here? e.g. is the transient vs. persistent distinction useful
for networks?
* Is a UUID useful for networks? Yes, because it distinguishes
between networks of the same name on different hosts?
* Where is the connection between domains and networks in either
the API or the XML format? How is a domain associated with a
network? You put a bridge name in the <network>l definition
and use that in the domains <interface> definition? Or you put
the network name in the interface definition and have libvirt
look up the bridge name when creating the guest?
* Should it be possible to stop/start/restart a network? What for?
If something breaks the user restarts it to see if that will fix
it?
17 years, 11 months
[Libvir] Add 'console' and 'vncdisplay' commands to virsh
by Daniel P. Berrange
The attached patch adds two new commands to virsh:
- console - connects to the guest's serial console
- vncdisplay - outputs the ip address & port number of a guest vnc display
The former is another stage in eliminating the need to run 'xm' - replacing
the Xen specific 'xm console' code, with the added advantage of working
with any libvirt backend driver.
The vncdisplay is intended to make it easier for people to launch a
VNC viewer process. It prints out a IP address & port number in a format
suitable for passing to vncviewer on the command line, eg
vncviewer `virsh vncdisplay myguest`
Regards,
Dan.
--
|=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=|
|=- Perl modules: http://search.cpan.org/~danberr/ -=|
|=- Projects: http://freshmeat.net/~danielpb/ -=|
|=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|
17 years, 11 months