[Libvir] [patch 0/9] Preliminary patches for virtual networks support
by markmc@redhat.com
Hi,
Yesterday I went ahead and committed the virtual networks
support in time for DV to make a release. I thought it'd be worth
sending them here so that people can look over them and hopefully
sport any problems.
So, here's the first batch - various fixes to the qemu
support and small bits of re-factoring in other parts of libvirt.
Cheers,
Mark.
--
17 years, 10 months
[Libvir] [PATCH] Add Require gnutls-devel for SRPM build
by Atsushi SAKAI
Hi,
This patch adds a Require gnutls-devel for "libvirt.spec" file.
Signed-off-by: Atsushi SAKAI <sakaia(a)jp.fujitsu.com>
Anyway, libvirt SRPM cannot build on IA64.
We are investigating on this.
Is threre any good idea?
Thanks
Atsushi SAKAI
17 years, 10 months
[Libvir] [PATCH] Remove init from virDriver structure.
by Richard W.M. Jones
--
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)
From 8f04c42e183fc0e339fdef993ec10833a056e791 Mon Sep 17 00:00:00 2001
From: Richard Jones <rjones(a)redhat.com>
Date: Fri, 16 Feb 2007 14:55:18 +0000
Subject: [PATCH] Remove init from virDriver structure.
init was never called, and only xen_internal tried to use it.
---
src/driver.h | 3 ---
src/proxy_internal.c | 1 -
src/qemu_internal.c | 1 -
src/test.c | 1 -
src/xen_internal.c | 5 ++---
src/xend_internal.c | 1 -
src/xm_internal.c | 1 -
src/xs_internal.c | 1 -
8 files changed, 2 insertions(+), 12 deletions(-)
diff --git a/src/driver.h b/src/driver.h
index c467ad0..7d79b11 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -33,8 +33,6 @@ typedef enum {
} virDrvOpenFlag;
typedef int
- (*virDrvInit) (void);
-typedef int
(*virDrvOpen) (virConnectPtr conn,
const char *name,
int flags);
@@ -150,7 +148,6 @@ struct _virDriver {
int no; /* the number virDrvNo */
const char * name; /* the name of the driver */
unsigned long ver; /* the version of the backend */
- virDrvInit init;
virDrvOpen open;
virDrvClose close;
virDrvGetType type;
diff --git a/src/proxy_internal.c b/src/proxy_internal.c
index 1e6e453..c7c325b 100644
--- a/src/proxy_internal.c
+++ b/src/proxy_internal.c
@@ -46,7 +46,6 @@ static virDriver xenProxyDriver = {
VIR_DRV_XEN_PROXY,
"XenProxy",
0,
- NULL, /* init */
xenProxyOpen, /* open */
xenProxyClose, /* close */
NULL, /* type */
diff --git a/src/qemu_internal.c b/src/qemu_internal.c
index a65b073..3fcf10f 100644
--- a/src/qemu_internal.c
+++ b/src/qemu_internal.c
@@ -1097,7 +1097,6 @@ static virDriver qemuDriver = {
VIR_DRV_QEMU,
"QEMU",
LIBVIR_VERSION_NUMBER,
- NULL, /* init */
qemuOpen, /* open */
qemuClose, /* close */
NULL, /* type */
diff --git a/src/test.c b/src/test.c
index 4025f99..5052cbd 100644
--- a/src/test.c
+++ b/src/test.c
@@ -90,7 +90,6 @@ static virDriver testDriver = {
VIR_DRV_TEST,
"Test",
LIBVIR_VERSION_NUMBER,
- NULL, /* init */
testOpen, /* open */
testClose, /* close */
NULL, /* type */
diff --git a/src/xen_internal.c b/src/xen_internal.c
index f885c66..32c550e 100644
--- a/src/xen_internal.c
+++ b/src/xen_internal.c
@@ -411,7 +411,6 @@ typedef struct xen_op_v2_dom xen_op_v2_dom;
static const char * xenHypervisorGetType(virConnectPtr conn);
static unsigned long xenHypervisorGetMaxMemory(virDomainPtr domain);
#endif
-static int xenHypervisorInit(void);
#ifndef PROXY
static virDriver xenHypervisorDriver = {
@@ -420,7 +419,6 @@ static virDriver xenHypervisorDriver = {
(DOM0_INTERFACE_VERSION >> 24) * 1000000 +
((DOM0_INTERFACE_VERSION >> 16) & 0xFF) * 1000 +
(DOM0_INTERFACE_VERSION & 0xFFFF),
- xenHypervisorInit, /* init */
xenHypervisorOpen, /* open */
xenHypervisorClose, /* close */
xenHypervisorGetType, /* type */
@@ -1127,7 +1125,8 @@ virXen_getvcpusinfo(int handle, int id, unsigned int vcpu, virVcpuInfoPtr ipt,
* Initialize the hypervisor layer. Try to detect the kind of interface
* used i.e. pre or post changeset 10277
*/
-int xenHypervisorInit(void)
+int
+xenHypervisorInit(void)
{
int fd, ret, cmd;
hypercall_t hc;
diff --git a/src/xend_internal.c b/src/xend_internal.c
index 2ec32a8..d78cbba 100644
--- a/src/xend_internal.c
+++ b/src/xend_internal.c
@@ -62,7 +62,6 @@ static virDriver xenDaemonDriver = {
(DOM0_INTERFACE_VERSION >> 24) * 1000000 +
((DOM0_INTERFACE_VERSION >> 16) & 0xFF) * 1000 +
(DOM0_INTERFACE_VERSION & 0xFFFF),
- NULL, /* init */
xenDaemonOpen, /* open */
xenDaemonClose, /* close */
xenDaemonGetType, /* type */
diff --git a/src/xm_internal.c b/src/xm_internal.c
index ccc24b1..2e1d88e 100644
--- a/src/xm_internal.c
+++ b/src/xm_internal.c
@@ -69,7 +69,6 @@ static virDriver xenXMDriver = {
(DOM0_INTERFACE_VERSION >> 24) * 1000000 +
((DOM0_INTERFACE_VERSION >> 16) & 0xFF) * 1000 +
(DOM0_INTERFACE_VERSION & 0xFFFF),
- NULL, /* init */
xenXMOpen, /* open */
xenXMClose, /* close */
xenXMGetType, /* type */
diff --git a/src/xs_internal.c b/src/xs_internal.c
index 9dca5b2..bf7e36e 100644
--- a/src/xs_internal.c
+++ b/src/xs_internal.c
@@ -40,7 +40,6 @@ static virDriver xenStoreDriver = {
(DOM0_INTERFACE_VERSION >> 24) * 1000000 +
((DOM0_INTERFACE_VERSION >> 16) & 0xFF) * 1000 +
(DOM0_INTERFACE_VERSION & 0xFFFF),
- NULL, /* init */
xenStoreOpen, /* open */
xenStoreClose, /* close */
NULL, /* type */
--
1.4.4.2
17 years, 10 months
[Libvir] Release of libvirt-0.2.0
by Daniel Veillard
That's it, it's out !
This include the KVM and QEmu support, and the networking that was discussed
for some time now. Since that's a serious set of changes I bumped the
intermediate version number, beware !
Available as usual from
ftp://libvirt.org/libvirt/
* Various internal cleanups (Mark McLoughlin, Richard Jones, Daniel Berrange, Karel Zak)
* Bug fixes:
- avoid a crash in connect (Daniel Berrange)
- virsh args parsing (Richard Jones)
* Add support for QEmu and KVM virtualization (Daniel Berrange)
* Add support for network configuration (Mark McLoughlin)
* Minor improvements:
- regression testing (Daniel Berrange)
- localization string updates
Thanks to all who contributed, enjoy !
Daniel
--
Red Hat Virtualization group http://redhat.com/virtualization/
Daniel Veillard | virtualization library http://libvirt.org/
veillard(a)redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/
http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/
17 years, 10 months
[Libvir] libmm
by Richard W.M. Jones
Does anyone object to adding a dep to libmm
(http://www.ossp.org/man/man.cgi/pkg/lib/mm/mm.pod) to libvirt? It
simplifies sharing of data between preforked libvirtd instances, and
allows us to persist that data (while at the same time not requiring it).
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, 10 months
[Libvir] PATCH 0/2 QEMU driver
by Daniel P. Berrange
What follows is yet another iteration of the QEMU patches. Based on recent
discussions about a generic remote management daemon, this patch set has
been stripped to remove all TCP functionality, and TLS encryption. It is
now local only unix domain sockets, with no encyption. THe idea being that
the generic libvirtd will be able to expose this over the network securely.
The patchset is split into two halves - the driver and the daemon. There
will be further explanation alongside each patch.
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, 10 months
[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
[Libvir] Features and roadmap tentative draft
by Daniel Veillard
We had a meeting this week in the Red Hat team and looked at the
work ahead for libvirt. We identified a set of feature that we would
like to get in, the time frame (mostly based on Fedora releases schedules)
and tried to identify remaining issues:
Features when Issues
---------------------------------------------------------------------------
KEmu & KVM FC7 Wire API , the way the process is controlled
mid feb will probably change, but requires QEmu upstream
extensions of the monitor
remote access FC8 Naming (URIs), Authentication and wire protocol
inetd hook, XML format slight change for dhcp
Virtual Network FC7 Sharing QEmu, root priviledge for the daemon
isolation
Migration FC8 API design
Access Control ??? depends on Xen Security Module, KVM equivalent
Storage ??? Early concept, will be discussed separately
on the mailing-list
Sync virsh shutdown FC7 Easy to add in virsh
I expect to make a new libvirt release with the KEmu and KVM support as
well as a first version of the virtual network support next week. Then
the focus will be on remote support and adding migration to the API.
Other various issues raised:
- URL/drivers, making sure that the last proposal for URI used in remote
connection are okay
- python bindings: disable automatic generation, handle manually, extend
- one Xen driver: currently implemented on top of 3 drivers, clean it up
- go though errors, cleaning up duplicates
- compile without Xen, once KEmu and KVM support gets added
- documentation cleanup: generation and CVS, checkout on libvirt.org
- export the config parser API at the shared library with __ prefix
since that's used by the daemons but not
- semantic of virConnect(NULL) is a bit fuzzy, Xen or default engine ?
- Need to plug libxml2 memory routines for memory debug
Of course the details will be discussed on the list, but at least
it gets a bit clearer where we expect to spend time, but as always
other features and feedback is more than welcome, it's needed, so
if there is any issue or comments on our viewpoint of the roadmap
and feature, please share them :-)
Daniel
--
Red Hat Virtualization group http://redhat.com/virtualization/
Daniel Veillard | virtualization library http://libvirt.org/
veillard(a)redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/
http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/
17 years, 10 months