summaryrefslogtreecommitdiffstats
path: root/agents/virt/common
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--agents/virt/common/Makefile.am24
-rw-r--r--agents/virt/common/bcast.c347
-rw-r--r--agents/virt/common/debug.c38
-rw-r--r--agents/virt/common/fdops.c202
-rw-r--r--agents/virt/common/ip_lookup.c326
-rw-r--r--agents/virt/common/log.c204
-rw-r--r--agents/virt/common/mcast.c388
-rw-r--r--agents/virt/common/simple_auth.c466
-rw-r--r--agents/virt/common/tcp.c386
9 files changed, 2381 insertions, 0 deletions
diff --git a/agents/virt/common/Makefile.am b/agents/virt/common/Makefile.am
new file mode 100644
index 0000000..d5e4314
--- /dev/null
+++ b/agents/virt/common/Makefile.am
@@ -0,0 +1,24 @@
+###############################################################################
+###############################################################################
+##
+## Copyright (C) 2009-2019 Red Hat, Inc.
+##
+## This copyrighted material is made available to anyone wishing to use,
+## modify, copy, or redistribute it subject to the terms and conditions
+## of the GNU General Public License v.2.
+##
+###############################################################################
+###############################################################################
+
+MAINTAINERCLEANFILES = Makefile.in
+
+noinst_LTLIBRARIES = libfence_virt.la
+
+libfence_virt_la_SOURCES = mcast.c ip_lookup.c simple_auth.c tcp.c \
+ debug.c fdops.c log.c
+
+libfence_virt_la_CFLAGS = $(VIRT_AM_CFLAGS) $(nss_CFLAGS) $(AM_CFLAGS) -Wno-cast-align
+
+libfence_virt_la_LDFLAGS = $(VIRT_AM_LDFLAGS) $(VIRT_COMMON_LDFLAGS)
+
+libfence_virt_la_LIBADD = $(nss_LIBS)
diff --git a/agents/virt/common/bcast.c b/agents/virt/common/bcast.c
new file mode 100644
index 0000000..2b498b7
--- /dev/null
+++ b/agents/virt/common/bcast.c
@@ -0,0 +1,347 @@
+/*
+ * Author: Lon Hohberger <lhh at redhat.com>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+
+/* Local includes */
+#include "bcast.h"
+#include "debug.h"
+
+LOGSYS_DECLARE_SUBSYS ("XVM", SYSLOGLEVEL);
+
+/**
+ Sets up a multicast receive socket
+ */
+int
+ipv4_bcast_recv_sk(char *addr, int port)
+{
+ int sock, val;
+ struct sockaddr_in sin;
+
+ /* Store broadcast address */
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = PF_INET;
+ sin.sin_port = htons(port);
+ if (inet_pton(PF_INET, addr,
+ (void *)&sin.sin_addr.s_addr) < 0) {
+ log_printf(LOG_ERR, "Invalid broadcast address: %s\n", addr);
+ return -1;
+ }
+
+ dbg_printf(4, "Setting up ipv4 broadcast receive (%s:%d)\n", addr, port);
+ sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ log_printf(LOG_ERR, "socket: %s\n", strerror(errno));
+ close(sock);
+ sock = -1;
+ return 1;
+ }
+
+ val = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, 1, sizeof(val)) < 0) {
+ log_printf(LOG_ERR, "setsockopt: %s\n", strerror(errno));
+ close(sock);
+ return 1;
+ }
+
+ /*
+ * Bind broadcast address
+ */
+ if (bind(sock, (struct sockaddr *) &sin,
+ sizeof(struct sockaddr_in)) < 0) {
+ printf("bind failed: %s\n", strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock);
+ return sock;
+}
+
+
+/**
+ Set up multicast send socket
+ */
+int
+ipv4_bcast_send_sk(char *send_addr, char *addr, int port, struct sockaddr *tgt,
+ socklen_t tgt_len, int ttl)
+{
+ int val;
+ struct ip_mreq mreq;
+ struct sockaddr_in mcast;
+ struct sockaddr_in src;
+ int sock;
+
+ if (tgt_len < sizeof(struct sockaddr_in)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Store multicast address */
+ mcast.sin_family = PF_INET;
+ mcast.sin_port = htons(port);
+ if (inet_pton(PF_INET, addr,
+ (void *)&mcast.sin_addr.s_addr) < 0) {
+ printf("Invalid multicast address: %s\n", addr);
+ return -1;
+ }
+ mreq.imr_multiaddr.s_addr = mcast.sin_addr.s_addr;
+
+ /* Store sending address */
+ src.sin_family = PF_INET;
+ src.sin_port = htons(port);
+ if (inet_pton(PF_INET, send_addr,
+ (void *)&src.sin_addr.s_addr) < 0) {
+ printf("Invalid source address: %s\n", send_addr);
+ return -1;
+ }
+ mreq.imr_interface.s_addr = src.sin_addr.s_addr;
+
+
+ /*************************
+ * SET UP MULTICAST SEND *
+ *************************/
+ dbg_printf(4, "Setting up ipv4 multicast send (%s:%d)\n", addr, port);
+ sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ /*
+ * Join Multicast group.
+ */
+ dbg_printf(4, "Joining IP Multicast group (pass 1)\n");
+ if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq)) == -1) {
+ printf("Failed to add multicast membership to transmit "
+ "socket %s: %s\n", addr, strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * Join Multicast group.
+ */
+ dbg_printf(4, "Joining IP Multicast group (pass 2)\n");
+ if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &src.sin_addr,
+ sizeof(src.sin_addr)) == -1) {
+ printf("Failed to bind multicast transmit socket to "
+ "%s: %s\n", addr, strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * set time to live to 2 hops.
+ */
+ dbg_printf(4, "Setting TTL to %d for fd%d\n", ttl, sock);
+ val = ttl;
+ if (setsockopt(sock, SOL_IP, IP_MULTICAST_TTL, &val,
+ sizeof(val)))
+ printf("warning: setting TTL failed %s\n", strerror(errno));
+
+ memcpy((struct sockaddr_in *)tgt, &mcast, sizeof(struct sockaddr_in));
+
+ dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock);
+ return sock;
+}
+
+
+
+/**
+ Sets up a multicast receive (ipv6) socket
+ */
+int
+ipv6_recv_sk(char *addr, int port)
+{
+ int sock, val;
+ struct ipv6_mreq mreq;
+ struct sockaddr_in6 sin;
+
+ memset(&mreq, 0, sizeof(mreq));
+ memset(&sin, 0, sizeof(sin));
+ sin.sin6_family = PF_INET6;
+ sin.sin6_port = htons(port);
+ if (inet_pton(PF_INET6, addr,
+ (void *)&sin.sin6_addr) < 0) {
+ printf("Invalid multicast address: %s\n", addr);
+ return -1;
+ }
+
+ memcpy(&mreq.ipv6mr_multiaddr, &sin.sin6_addr,
+ sizeof(struct in6_addr));
+
+
+ /********************************
+ * SET UP MULTICAST RECV SOCKET *
+ ********************************/
+ dbg_printf(4, "Setting up ipv6 multicast receive (%s:%d)\n", addr, port);
+ sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock < 0) {
+ printf("socket: %s\n", strerror(errno));
+ close(sock);
+ sock = -1;
+ return 1;
+ }
+
+ /*
+ * When using Multicast, bind to the LOCAL address, not the MULTICAST
+ * address.
+ */
+ memset(&sin, 0, sizeof(sin));
+ sin.sin6_family = PF_INET6;
+ sin.sin6_port = htons(port);
+ sin.sin6_addr = in6addr_any;
+ if (bind(sock, (struct sockaddr *) &sin,
+ sizeof(struct sockaddr_in6)) < 0) {
+ printf("bind failed: %s\n", strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ dbg_printf(4, "Disabling IP Multicast loopback\n");
+ val = 1;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
+ sizeof(val)) != 0) {
+ printf("Failed to disable multicast loopback\n");
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * Join multicast group
+ */
+ dbg_printf(4, "Joining IP Multicast group\n");
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq)) == -1) {
+ printf("Failed to add multicast to socket %s: %s\n",
+ addr, strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock);
+ return sock;
+}
+
+
+/**
+ Set up ipv6 multicast send socket
+ */
+int
+ipv6_send_sk(char *send_addr, char *addr, int port, struct sockaddr *tgt,
+ socklen_t tgt_len, int ttl)
+{
+ int val;
+ struct ipv6_mreq mreq;
+ struct sockaddr_in6 mcast;
+ struct sockaddr_in6 src;
+ int sock;
+
+ if (tgt_len < sizeof(struct sockaddr_in6)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(&mreq, 0, sizeof(mreq));
+
+ /* Store multicast address */
+ mcast.sin6_family = PF_INET6;
+ mcast.sin6_port = htons(port);
+ if (inet_pton(PF_INET6, addr,
+ (void *)&mcast.sin6_addr) < 0) {
+ printf("Invalid multicast address: %s\n", addr);
+ return -1;
+ }
+
+ memcpy(&mreq.ipv6mr_multiaddr, &mcast.sin6_addr,
+ sizeof(struct in6_addr));
+
+ /* Store sending address */
+ src.sin6_family = PF_INET6;
+ src.sin6_port = htons(port);
+ if (inet_pton(PF_INET6, send_addr,
+ (void *)&src.sin6_addr) < 0) {
+ printf("Invalid source address: %s\n", send_addr);
+ return -1;
+ }
+
+ /*************************
+ * SET UP MULTICAST SEND *
+ *************************/
+ dbg_printf(4, "Setting up ipv6 multicast send (%s:%d)\n", addr, port);
+ sock = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ dbg_printf(4, "Disabling IP Multicast loopback\n");
+ val = 1;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
+ sizeof(val)) != 0) {
+ printf("Failed to disable multicast loopback\n");
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * Join Multicast group.
+ */
+ dbg_printf(4, "Joining IP Multicast group\n");
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq)) == -1) {
+ printf("Failed to add multicast membership to transmit "
+ "socket %s: %s\n", addr, strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * Join Multicast group (part 2)
+ */
+ /*
+ if (setsockopt(sock, IPPROTO_IPV6, IP_MULTICAST_IF, &src.sin6_addr,
+ sizeof(src.sin6_addr)) == -1) {
+ printf("Failed to bind multicast transmit socket to "
+ "%s: %s\n", addr, strerror(errno));
+ close(sock);
+ return -1;
+ }
+ */
+
+ /*
+ * set time to live to 2 hops.
+ */
+ val = ttl;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val,
+ sizeof(val)))
+ printf("warning: setting TTL failed %s\n", strerror(errno));
+
+ memcpy((struct sockaddr_in *)tgt, &mcast, sizeof(struct sockaddr_in6));
+
+ dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock);
+ return sock;
+}
diff --git a/agents/virt/common/debug.c b/agents/virt/common/debug.c
new file mode 100644
index 0000000..5cf5359
--- /dev/null
+++ b/agents/virt/common/debug.c
@@ -0,0 +1,38 @@
+/*
+ Copyright Red Hat, Inc. 2006
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include "debug.h"
+
+static int _debug = 0;
+
+void
+dset(int threshold)
+{
+ _debug = threshold;
+ dbg_printf(3, "Debugging threshold is now %d\n", threshold);
+}
+
+int
+dget(void)
+{
+ return _debug;
+}
diff --git a/agents/virt/common/fdops.c b/agents/virt/common/fdops.c
new file mode 100644
index 0000000..329e9b7
--- /dev/null
+++ b/agents/virt/common/fdops.c
@@ -0,0 +1,202 @@
+/*
+ Copyright Red Hat, Inc. 2002-2003
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+/** @file
+ * Wrapper functions around read/write/select to retry in the event
+ * of interrupts.
+ */
+
+#include "config.h"
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "fdops.h"
+
+/**
+ * This is a wrapper around select which will retry in the case we receive
+ * EINTR. This is necessary for _read_retry, since it wouldn't make sense
+ * to have _read_retry terminate if and only if two EINTRs were received
+ * in a row - one during the read() call, one during the select call...
+ *
+ * See select(2) for description of parameters.
+ */
+int
+_select_retry(int fdmax, fd_set * rfds, fd_set * wfds, fd_set * xfds,
+ struct timeval *timeout)
+{
+ int rv;
+
+ while (1) {
+ rv = select(fdmax, rfds, wfds, xfds, timeout);
+ if (rv == -1) {
+ /* return on EBADF/EINVAL/ENOMEM; continue on EINTR/EAGAIN/ENOMEM */
+ if (errno == EINTR || errno == EAGAIN || errno == ENOMEM)
+ continue;
+ }
+ return rv;
+ }
+}
+
+/**
+ * Retries a write in the event of a non-blocked interrupt signal.
+ *
+ * @param fd File descriptor to which we are writing.
+ * @param buf Data buffer to send.
+ * @param count Number of bytes in buf to send.
+ * @param timeout (struct timeval) telling us how long we should retry.
+ * @return The number of bytes written to the file descriptor,
+ * or -1 on error (with errno set appropriately).
+ */
+ssize_t
+_write_retry(int fd, void *buf, int count, struct timeval * timeout)
+{
+ int n, total = 0, remain = count, rv = 0;
+ fd_set wfds, xfds;
+ char *tmp_buf = (char *)buf;
+
+ while (total < count) {
+
+ /* Create the write FD set of 1... */
+ FD_ZERO(&wfds);
+ FD_SET(fd, &wfds);
+ FD_ZERO(&xfds);
+ FD_SET(fd, &xfds);
+
+ /* wait for the fd to be available for writing */
+ rv = _select_retry(fd + 1, NULL, &wfds, &xfds, timeout);
+ if (rv == -1)
+ return -1;
+ else if (rv == 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ if (FD_ISSET(fd, &xfds)) {
+ errno = EPIPE;
+ return -1;
+ }
+
+ /*
+ * Attempt to write to fd
+ */
+ n = write(fd, tmp_buf + total, remain);
+
+ /*
+ * When we know our fd was select()ed and we receive 0 bytes
+ * when we write, the fd was closed.
+ */
+ if ((n == 0) && (rv == 1)) {
+ errno = EPIPE;
+ return -1;
+ }
+
+ if (n == -1) {
+ if ((errno == EAGAIN) || (errno == EINTR)) {
+ /*
+ * Not ready?
+ */
+ continue;
+ }
+
+ /* Other errors: EIO, EINVAL, etc */
+ return -1;
+ }
+
+ total += n;
+ remain -= n;
+ }
+
+ return total;
+}
+
+/**
+ * Retry reads until we (a) time out or (b) get our data. Of course, if
+ * timeout is NULL, it'll wait forever.
+ *
+ * @param sockfd File descriptor we want to read from.
+ * @param buf Preallocated buffer into which we will read data.
+ * @param count Number of bytes to read.
+ * @param timeout (struct timeval) describing how long we should retry.
+ * @return The number of bytes read on success, or -1 on failure.
+ Note that we will always return (count) or (-1).
+ */
+ssize_t
+_read_retry(int sockfd, void *buf, int count, struct timeval * timeout)
+{
+ int n, total = 0, remain = count, rv = 0;
+ fd_set rfds, xfds;
+ char *tmp_buf = (char *)buf;
+
+ while (total < count) {
+ FD_ZERO(&rfds);
+ FD_SET(sockfd, &rfds);
+ FD_ZERO(&xfds);
+ FD_SET(sockfd, &xfds);
+
+ /*
+ * Select on the socket, in case it closes while we're not
+ * looking...
+ */
+ rv = _select_retry(sockfd + 1, &rfds, NULL, &xfds, timeout);
+ if (rv == -1)
+ return -1;
+ else if (rv == 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ if (FD_ISSET(sockfd, &xfds)) {
+ errno = EPIPE;
+ return -1;
+ }
+
+ /*
+ * Attempt to read off the socket
+ */
+ n = read(sockfd, tmp_buf + total, remain);
+
+ /*
+ * When we know our socket was select()ed and we receive 0 bytes
+ * when we read, the socket was closed.
+ */
+ if ((n == 0) && (rv == 1)) {
+ errno = EPIPE;
+ return -1;
+ }
+
+ if (n == -1) {
+ if ((errno == EAGAIN) || (errno == EINTR)) {
+ /*
+ * Not ready? Wait for data to become available
+ */
+ continue;
+ }
+
+ /* Other errors: EPIPE, EINVAL, etc */
+ return -1;
+ }
+
+ total += n;
+ remain -= n;
+ }
+
+ return total;
+}
diff --git a/agents/virt/common/ip_lookup.c b/agents/virt/common/ip_lookup.c
new file mode 100644
index 0000000..aded8ea
--- /dev/null
+++ b/agents/virt/common/ip_lookup.c
@@ -0,0 +1,326 @@
+/*
+ Copyright Red Hat, Inc. 2004, 2006
+
+ The Magma Cluster API 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 Magma Cluster API 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 this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+ */
+/** @file
+ * Build lists of IPs on the system, excepting loopback ipv6 link-local
+ */
+
+#include "config.h"
+
+#include <asm/types.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <netdb.h>
+
+/* Local includes */
+#include "ip_lookup.h"
+#include "debug.h"
+
+static int
+send_addr_dump(int fd, int family)
+{
+ struct nlmsghdr *nh;
+ struct rtgenmsg *g;
+ char buf[256];
+ struct sockaddr_nl addr;
+
+ memset(&addr,0,sizeof(addr));
+ addr.nl_family = PF_NETLINK;
+
+ memset(buf, 0, sizeof(buf));
+ nh = (struct nlmsghdr *)buf;
+ g = (struct rtgenmsg *)(buf + sizeof(struct nlmsghdr));
+
+ nh->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
+ nh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
+ nh->nlmsg_type = RTM_GETADDR;
+ g->rtgen_family = family;
+
+ return sendto(fd, buf, nh->nlmsg_len, 0, (struct sockaddr *)&addr,
+ sizeof(addr));
+}
+
+
+static int
+add_ip(ip_list_t *ipl, char *ipaddr, char family)
+{
+ ip_addr_t *ipa;
+
+ if (family == PF_INET6) {
+ /* Avoid loopback */
+ if (!strcmp(ipaddr, "::1"))
+ return -1;
+
+ /* Avoid link-local addresses */
+ if (!strncmp(ipaddr, "fe80", 4))
+ return -1;
+ if (!strncmp(ipaddr, "fe90", 4))
+ return -1;
+ if (!strncmp(ipaddr, "fea0", 4))
+ return -1;
+ if (!strncmp(ipaddr, "feb0", 4))
+ return -1;
+ }
+
+ ipa = calloc(1, sizeof(*ipa));
+ if (!ipa)
+ return -1;
+ ipa->ipa_family = family;
+ ipa->ipa_address = strdup(ipaddr);
+
+ dbg_printf(4, "Adding IP %s to list (family %d)\n", ipaddr, family);
+ TAILQ_INSERT_TAIL(ipl, ipa, ipa_entries);
+
+ return 0;
+}
+
+
+static int
+add_ip_addresses(int family, ip_list_t *ipl)
+{
+ /* List ipv4 addresses */
+ struct nlmsghdr *nh;
+ struct ifaddrmsg *ifa;
+ struct rtattr *rta, *nrta;
+ struct nlmsgerr *err;
+ char buf[10240];
+ char outbuf[256];
+ char label[256];
+ int x, fd, len;
+
+ dbg_printf(5, "Connecting to Netlink...\n");
+ fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (fd < 0) {
+ perror("socket");
+ exit(1);
+ }
+
+ dbg_printf(5, "Sending address dump request\n");
+ if (send_addr_dump(fd, family) < 0) {
+ perror("sendto");
+ close(fd);
+ return -1;
+ }
+ memset(buf, 0, sizeof(buf));
+
+ dbg_printf(5, "Waiting for response\n");
+ x = recvfrom(fd, buf, sizeof(buf), 0, NULL, 0);
+ if (x < 0) {
+ perror("recvfrom");
+ close(fd);
+ return -1;
+ }
+
+ dbg_printf(5, "Received %d bytes\n", x);
+
+ nh = (struct nlmsghdr *)buf;
+ while (NLMSG_OK(nh, x)) {
+
+ switch(nh->nlmsg_type) {
+ case NLMSG_DONE:
+ close(fd);
+ return 0;
+
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr*)NLMSG_DATA(nh);
+ if (nh->nlmsg_len <
+ NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+ fprintf(stderr, "ERROR truncated");
+ } else {
+ errno = -err->error;
+ perror("RTNETLINK answers");
+ }
+ close(fd);
+ return -1;
+
+ case RTM_NEWADDR:
+ break;
+
+ default:
+ nh = NLMSG_NEXT(nh, x);
+ continue;
+ }
+
+ /* RTM_NEWADDR */
+ len = NLMSG_PAYLOAD(nh,0);
+ ifa = NLMSG_DATA(nh);
+
+ /* Make sure we got the type we expect back */
+ if (ifa->ifa_family != family) {
+ nh = NLMSG_NEXT(nh, x);
+ continue;
+ }
+
+ rta = IFA_RTA(ifa);
+ len -= sizeof(struct ifaddrmsg);
+ do {
+ /* Make sure we've got a valid rtaddr field */
+ if (!RTA_OK(rta, len)) {
+ dbg_printf(5, "!RTA_OK(rta, len)\n");
+ break;
+ }
+
+ if (rta->rta_type == IFA_ADDRESS) {
+ inet_ntop(family, RTA_DATA(rta), outbuf,
+ sizeof(outbuf) );
+ add_ip(ipl, outbuf, family);
+ }
+
+ if (rta->rta_type == IFA_LABEL) {
+ memset(label, 0, sizeof(label));
+ strncpy(label, (char *)RTA_DATA(rta), sizeof(label) - 1);
+ dbg_printf(5, "Skipping label: %s\n",
+ label);
+ }
+
+ nrta = RTA_NEXT(rta, len);
+ len -= (nrta - rta);
+ rta = nrta;
+ } while (RTA_OK(rta, len));
+
+ nh = NLMSG_NEXT(nh, x);
+ }
+
+ dbg_printf(5, "Closing Netlink connection\n");
+ close(fd);
+ return 0;
+}
+
+
+int
+ip_search(ip_list_t *ipl, char *ip_name)
+{
+ ip_addr_t *ipa;
+
+ dbg_printf(5, "Looking for IP address %s in IP list %p...", ip_name, ipl);
+ ipa = ipl->tqh_first;
+ for (ipa = ipl->tqh_first; ipa; ipa = ipa->ipa_entries.tqe_next) {
+ if (!strcmp(ip_name, ipa->ipa_address)) {
+ dbg_printf(4,"Found\n");
+ return 0;
+ }
+ }
+ dbg_printf(5, "Not found\n");
+ return 1;
+}
+
+
+int
+ip_free_list(ip_list_t *ipl)
+{
+ ip_addr_t *ipa;
+
+ dbg_printf(5, "Tearing down IP list @ %p\n", ipl);
+ while ((ipa = ipl->tqh_first)) {
+ TAILQ_REMOVE(ipl, ipa, ipa_entries);
+ free(ipa->ipa_address);
+ free(ipa);
+ }
+ return 0;
+}
+
+
+int
+ip_build_list(ip_list_t *ipl)
+{
+ dbg_printf(5, "Build IP address list\n");
+ TAILQ_INIT(ipl);
+ if (add_ip_addresses(PF_INET6, ipl) < 0) {
+ ip_free_list(ipl);
+ return -1;
+ }
+ if (add_ip_addresses(PF_INET, ipl) < 0) {
+ ip_free_list(ipl);
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ Look up the interface name which corresponds to the given hostname and
+ return the list of matching attrinfo structures. We do this by looking
+ up all the possible physical and virtual network interfaces on the machine
+ and checking the hostname/IP mappings for each active IP address incurred.
+
+ @param nodename Interface name
+ @param ret_ai Structure pointer to allocate & return.
+ @return -1 on failure or 0 on success.
+ */
+int
+ip_lookup(char *nodename, struct addrinfo **ret_ai)
+{
+ char ip_name[256];
+ struct addrinfo *ai = NULL;
+ struct addrinfo *n;
+ void *p;
+ ip_list_t ipl;
+ int ret = -1;
+
+ dbg_printf(5, "Looking for IP matching %s\n", nodename);
+ /* Build list of IP addresses configured locally */
+ if (ip_build_list(&ipl) < 0)
+ return -1;
+
+ /* Get list of addresses for the host-name/ip */
+ if (getaddrinfo(nodename, NULL, NULL, &ai) != 0)
+ return -1;
+
+
+ /* Traverse list of addresses for given host-name/ip */
+ for (n = ai; n; n = n->ai_next) {
+ if (n->ai_family != PF_INET && n->ai_family != PF_INET6)
+ continue;
+
+ if (n->ai_family == PF_INET)
+ p = &(((struct sockaddr_in *)n->ai_addr)->sin_addr);
+ else
+ p = &(((struct sockaddr_in6 *)n->ai_addr)->sin6_addr);
+
+ if (!inet_ntop(n->ai_family, p, ip_name,
+ sizeof(ip_name)))
+ continue;
+
+ /* Search local interfaces for this IP address */
+ if (ip_search(&ipl, ip_name) != 0)
+ continue;
+
+ /* Found it */
+ ret = 0;
+ break;
+ }
+
+ /* Clean up */
+ if (!ret_ai)
+ freeaddrinfo(ai);
+ else
+ *ret_ai = ai;
+
+ ip_free_list(&ipl);
+
+ return ret;
+}
+
diff --git a/agents/virt/common/log.c b/agents/virt/common/log.c
new file mode 100644
index 0000000..61018ed
--- /dev/null
+++ b/agents/virt/common/log.c
@@ -0,0 +1,204 @@
+/**
+ * Syslog wrapper that does not block
+ *
+ * Lon Hohberger, 2009
+ */
+
+#include "config.h"
+
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/syslog.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include "list.h"
+
+struct log_entry {
+ list_head();
+ char *message;
+ int sev;
+ int bufsz;
+};
+
+#define MAX_QUEUE_LENGTH 10
+#define LOGLEN 256
+
+static struct log_entry *_log_entries = NULL;
+static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t log_cond = PTHREAD_COND_INITIALIZER;
+static int log_size = 0;
+static int dropped = 0;
+static pthread_t thread_id = 0;
+
+void __real_syslog(int severity, const char *fmt, ...);
+void __wrap_syslog(int severity, const char *fmt, ...);
+void __wrap_closelog(void);
+
+static void *
+_log_thread(void *arg)
+{
+ struct timeval tv;
+ struct timespec ts;
+ struct log_entry *entry;
+
+ do {
+ gettimeofday(&tv, NULL);
+ ts.tv_sec = tv.tv_sec + 10;
+ ts.tv_nsec = tv.tv_usec;
+
+ pthread_mutex_lock(&log_mutex);
+
+ while (!(entry = _log_entries)) {
+ if (pthread_cond_timedwait(&log_cond,
+ &log_mutex,
+ &ts) == ETIMEDOUT)
+ goto out;
+ }
+
+ list_remove(&_log_entries, entry);
+ --log_size;
+ if (log_size < 0)
+ raise(SIGSEGV);
+ pthread_mutex_unlock(&log_mutex);
+
+ __real_syslog(entry->sev, entry->message);
+ free(entry->message);
+ free(entry);
+ } while (1);
+
+out:
+ thread_id = (pthread_t)0;
+ pthread_mutex_unlock(&log_mutex);
+ return NULL;
+}
+
+
+static int
+insert_entry(int sev, char *buf, int bufsz)
+{
+ struct log_entry *lent;
+ pthread_attr_t attrs;
+
+ lent = malloc(sizeof(*lent));
+ if (!lent)
+ return -1;
+ lent->sev = sev;
+ lent->message = buf;
+ lent->bufsz = bufsz;
+
+ pthread_mutex_lock(&log_mutex);
+ if (log_size >= MAX_QUEUE_LENGTH) {
+ free(lent->message);
+ free(lent);
+
+ ++dropped;
+ lent = (struct log_entry *)(le(_log_entries)->le_prev);
+
+ lent->sev = LOG_WARNING;
+ snprintf(lent->message, lent->bufsz,
+ "%d message(s) lost due to syslog load\n",
+ dropped + 1);
+ /* Dropped +1 because we overwrote a message to
+ * give the 'dropped' message */
+ } else {
+ ++log_size;
+ dropped = 0;
+ list_insert(&_log_entries, lent);
+ }
+
+ if (!thread_id) {
+ pthread_attr_init(&attrs);
+ pthread_attr_setinheritsched(&attrs, PTHREAD_INHERIT_SCHED);
+
+ if (pthread_create(&thread_id, &attrs, _log_thread, NULL) < 0)
+ thread_id = 0;
+ pthread_mutex_unlock(&log_mutex);
+ } else {
+ pthread_mutex_unlock(&log_mutex);
+ pthread_cond_signal(&log_cond);
+ }
+
+ return 0;
+}
+
+
+__attribute__((__format__ (__printf__, 2, 0)))
+void
+__wrap_syslog(int severity, const char *fmt, ...)
+{
+ va_list args;
+ char *logmsg;
+
+ logmsg = malloc(LOGLEN);
+ if (!logmsg)
+ return;
+ memset(logmsg, 0, LOGLEN);
+
+ va_start(args, fmt);
+ vsnprintf(logmsg + strlen(logmsg), LOGLEN - strlen(logmsg),
+ fmt, args);
+ va_end(args);
+
+ insert_entry(severity, logmsg, LOGLEN);
+
+ return;
+}
+
+
+void __real_closelog(void);
+
+void
+__wrap_closelog(void)
+{
+ struct log_entry *lent;
+#ifdef DEBUG
+ int lost = 0;
+#endif
+
+ if (thread_id != 0) {
+ pthread_cancel(thread_id);
+ pthread_join(thread_id, NULL);
+ thread_id = 0;
+ }
+ __real_closelog();
+ while (_log_entries) {
+#ifdef DEBUG
+ ++lost;
+#endif
+ lent = _log_entries;
+ list_remove(&_log_entries, lent);
+ free(lent->message);
+ free(lent);
+ }
+
+#ifdef DEBUG
+ printf("%d lost\n", lost);
+#endif
+}
+
+
+#ifdef STANDALONE
+int
+main(int argc, char**argv)
+{
+ int x;
+
+ for (x = 0; x < 100; x++) {
+ syslog(1, "Yo %d\n", x);
+ }
+ sleep(1);
+
+ closelog();
+
+ return 0;
+}
+
+#endif
diff --git a/agents/virt/common/mcast.c b/agents/virt/common/mcast.c
new file mode 100644
index 0000000..cc79c64
--- /dev/null
+++ b/agents/virt/common/mcast.c
@@ -0,0 +1,388 @@
+/*
+ Copyright Red Hat, Inc. 2003, 2004, 2006
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+/*
+ * Author: Lon Hohberger <lhh at redhat.com>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+
+/* Local includes */
+#include "mcast.h"
+#include "debug.h"
+
+/**
+ Sets up a multicast receive socket
+ */
+int
+ipv4_recv_sk(char *addr, int port, unsigned int ifindex)
+{
+ int sock;
+ struct ip_mreqn mreq;
+ struct sockaddr_in sin;
+
+ memset(&mreq, 0, sizeof(mreq));
+ memset(&sin, 0, sizeof(sin));
+
+ /* Store multicast address */
+ if (inet_pton(PF_INET, addr,
+ (void *)&mreq.imr_multiaddr.s_addr) < 0) {
+ printf("Invalid multicast address: %s\n", addr);
+ return -1;
+ }
+
+ /********************************
+ * SET UP MULTICAST RECV SOCKET *
+ ********************************/
+ dbg_printf(4, "Setting up ipv4 multicast receive (%s:%d)\n", addr, port);
+ sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ printf("socket: %s\n", strerror(errno));
+ sock = -1;
+ return 1;
+ }
+
+ /*
+ * When using Multicast, bind to the LOCAL address, not the MULTICAST
+ * address.
+ */
+ sin.sin_family = PF_INET;
+ sin.sin_port = htons(port);
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ if (bind(sock, (struct sockaddr *) &sin,
+ sizeof(struct sockaddr_in)) < 0) {
+ printf("bind failed: %s\n", strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * Join multicast group
+ */
+ /* mreq.imr_multiaddr.s_addr is set above */
+ if (ifindex == 0) {
+ dbg_printf(4, "Setting mcast addr to INADDR_ANY due to ifindex of 0\n");
+ mreq.imr_address.s_addr = htonl(INADDR_ANY);
+ } else {
+ mreq.imr_ifindex = ifindex;
+ }
+ dbg_printf(4, "Joining multicast group\n");
+ if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &mreq, sizeof(mreq)) == -1) {
+ printf("Failed to bind multicast receive socket to "
+ "%s: %s\n", addr, strerror(errno));
+ printf("Check network configuration.\n");
+ close(sock);
+ return -1;
+ }
+
+ dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock);
+ return sock;
+}
+
+
+/**
+ Set up multicast send socket
+ */
+int
+ipv4_send_sk(char *send_addr, char *addr, int port, struct sockaddr *tgt,
+ socklen_t tgt_len)
+{
+ int val;
+ struct ip_mreq mreq;
+ struct sockaddr_in mcast;
+ struct sockaddr_in src;
+ int sock;
+
+ if (tgt_len < sizeof(struct sockaddr_in)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(&mcast, 0, sizeof(mcast));
+ memset(&src, 0, sizeof(src));
+
+ /* Store multicast address */
+ mcast.sin_family = PF_INET;
+ mcast.sin_port = htons(port);
+ if (inet_pton(PF_INET, addr,
+ (void *)&mcast.sin_addr.s_addr) < 0) {
+ printf("Invalid multicast address: %s\n", addr);
+ return -1;
+ }
+ mreq.imr_multiaddr.s_addr = mcast.sin_addr.s_addr;
+
+ /* Store sending address */
+ src.sin_family = PF_INET;
+ src.sin_port = htons(port);
+ if (inet_pton(PF_INET, send_addr,
+ (void *)&src.sin_addr.s_addr) < 0) {
+ printf("Invalid source address: %s\n", send_addr);
+ return -1;
+ }
+ mreq.imr_interface.s_addr = src.sin_addr.s_addr;
+
+
+ /*************************
+ * SET UP MULTICAST SEND *
+ *************************/
+ dbg_printf(4, "Setting up ipv4 multicast send (%s:%d)\n", addr, port);
+ sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ /*
+ * Join Multicast group.
+ */
+ dbg_printf(4, "Joining IP Multicast group (pass 1)\n");
+ if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq)) == -1) {
+ printf("Failed to add multicast membership to transmit "
+ "socket %s: %s\n", addr, strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * Join Multicast group.
+ */
+ dbg_printf(4, "Joining IP Multicast group (pass 2)\n");
+ if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &src.sin_addr,
+ sizeof(src.sin_addr)) == -1) {
+ printf("Failed to bind multicast transmit socket to "
+ "%s: %s\n", addr, strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * set time to live to 2 hops.
+ */
+ dbg_printf(4, "Setting TTL to 2 for fd%d\n", sock);
+ val = 2;
+ if (setsockopt(sock, SOL_IP, IP_MULTICAST_TTL, &val,
+ sizeof(val)))
+ printf("warning: setting TTL failed %s\n", strerror(errno));
+
+ memcpy((struct sockaddr_in *)tgt, &mcast, sizeof(struct sockaddr_in));
+
+ dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock);
+ return sock;
+}
+
+
+
+/**
+ Sets up a multicast receive (ipv6) socket
+ */
+int
+ipv6_recv_sk(char *addr, int port, unsigned int ifindex)
+{
+ int sock, val;
+ struct ipv6_mreq mreq;
+ struct sockaddr_in6 sin;
+
+ memset(&mreq, 0, sizeof(mreq));
+ memset(&sin, 0, sizeof(sin));
+ sin.sin6_family = PF_INET6;
+ sin.sin6_port = htons(port);
+ if (inet_pton(PF_INET6, addr,
+ (void *)&sin.sin6_addr) < 0) {
+ printf("Invalid multicast address: %s\n", addr);
+ return -1;
+ }
+
+ memcpy(&mreq.ipv6mr_multiaddr, &sin.sin6_addr,
+ sizeof(struct in6_addr));
+
+ mreq.ipv6mr_interface = ifindex;
+
+ /********************************
+ * SET UP MULTICAST RECV SOCKET *
+ ********************************/
+ dbg_printf(4, "Setting up ipv6 multicast receive (%s:%d)\n", addr, port);
+ sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock < 0) {
+ printf("socket: %s\n", strerror(errno));
+ sock = -1;
+ return 1;
+ }
+
+ /*
+ * When using Multicast, bind to the LOCAL address, not the MULTICAST
+ * address.
+ */
+ memset(&sin, 0, sizeof(sin));
+ sin.sin6_family = PF_INET6;
+ sin.sin6_port = htons(port);
+ sin.sin6_addr = in6addr_any;
+ if (bind(sock, (struct sockaddr *) &sin,
+ sizeof(struct sockaddr_in6)) < 0) {
+ printf("bind failed: %s\n", strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ dbg_printf(4, "Disabling IP Multicast loopback\n");
+ val = 1;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
+ sizeof(val)) != 0) {
+ printf("Failed to disable multicast loopback\n");
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * Join multicast group
+ */
+ dbg_printf(4, "Joining IP Multicast group\n");
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq)) == -1) {
+ printf("Failed to add multicast to socket %s: %s\n",
+ addr, strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock);
+ return sock;
+}
+
+
+/**
+ Set up ipv6 multicast send socket
+ */
+int
+ipv6_send_sk(char *send_addr, char *addr, int port, struct sockaddr *tgt,
+ socklen_t tgt_len)
+{
+ int val;
+ struct ipv6_mreq mreq;
+ struct sockaddr_in6 mcast;
+ struct sockaddr_in6 src;
+ int sock;
+
+ if (tgt_len < sizeof(struct sockaddr_in6)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(&mcast, 0, sizeof(mcast));
+ memset(&src, 0, sizeof(src));
+ memset(&mreq, 0, sizeof(mreq));
+
+ /* Store multicast address */
+ mcast.sin6_family = PF_INET6;
+ mcast.sin6_port = htons(port);
+ if (inet_pton(PF_INET6, addr,
+ (void *)&mcast.sin6_addr) < 0) {
+ printf("Invalid multicast address: %s\n", addr);
+ return -1;
+ }
+
+ memcpy(&mreq.ipv6mr_multiaddr, &mcast.sin6_addr,
+ sizeof(struct in6_addr));
+
+ /* Store sending address */
+ src.sin6_family = PF_INET6;
+ src.sin6_port = htons(port);
+ if (inet_pton(PF_INET6, send_addr,
+ (void *)&src.sin6_addr) < 0) {
+ printf("Invalid source address: %s\n", send_addr);
+ return -1;
+ }
+
+ /*************************
+ * SET UP MULTICAST SEND *
+ *************************/
+ dbg_printf(4, "Setting up ipv6 multicast send (%s:%d)\n", addr, port);
+ sock = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ dbg_printf(4, "Disabling IP Multicast loopback\n");
+ val = 1;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
+ sizeof(val)) != 0) {
+ printf("Failed to disable multicast loopback\n");
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * Join Multicast group.
+ */
+ dbg_printf(4, "Joining IP Multicast group\n");
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq)) == -1) {
+ printf("Failed to add multicast membership to transmit "
+ "socket %s: %s\n", addr, strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ /*
+ * Join Multicast group (part 2)
+ */
+ /*
+ if (setsockopt(sock, IPPROTO_IPV6, IP_MULTICAST_IF, &src.sin6_addr,
+ sizeof(src.sin6_addr)) == -1) {
+ printf("Failed to bind multicast transmit socket to "
+ "%s: %s\n", addr, strerror(errno));
+ close(sock);
+ return -1;
+ }
+ */
+
+ /*
+ * set time to live to 2 hops.
+ */
+ val = 2;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val,
+ sizeof(val)))
+ printf("warning: setting TTL failed %s\n", strerror(errno));
+
+ memcpy((struct sockaddr_in *)tgt, &mcast, sizeof(struct sockaddr_in6));
+
+ dbg_printf(4, "%s: success, fd = %d\n", __FUNCTION__, sock);
+ return sock;
+}
diff --git a/agents/virt/common/simple_auth.c b/agents/virt/common/simple_auth.c
new file mode 100644
index 0000000..9f694c4
--- /dev/null
+++ b/agents/virt/common/simple_auth.c
@@ -0,0 +1,466 @@
+/*
+ Copyright Red Hat, Inc. 2006
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sechash.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+
+/* Local includes */
+#include "xvm.h"
+#include "fdops.h"
+#include "simple_auth.h"
+#include "debug.h"
+
+
+static void
+print_hash(unsigned char *hash, size_t hashlen)
+{
+ int x;
+
+ for (x = 0; x < hashlen; x++)
+ printf("%02x", (hash[x]&0xff));
+}
+
+
+static int
+sha_sign(fence_req_t *req, void *key, size_t key_len)
+{
+ unsigned char hash[SHA512_LENGTH];
+ HASHContext *h;
+ HASH_HashType ht;
+ unsigned int rlen;
+ int devrand;
+ int ret;
+
+ switch(req->hashtype) {
+ case HASH_SHA1:
+ ht = HASH_AlgSHA1;
+ break;
+ case HASH_SHA256:
+ ht = HASH_AlgSHA256;
+ break;
+ case HASH_SHA512:
+ ht = HASH_AlgSHA512;
+ break;
+ default:
+ dbg_printf(1, "Unknown hash type: %d\n", req->hashtype);
+ return -1;
+ }
+
+ dbg_printf(4, "Opening /dev/urandom\n");
+ devrand = open("/dev/urandom", O_RDONLY);
+ if (devrand < 0) {
+ dbg_printf(1, "Error: open: /dev/urandom: %s", strerror(errno));
+ return -1;
+ }
+
+ ret = _read_retry(devrand, req->random, sizeof(req->random), NULL);
+ if (ret <= 0) {
+ dbg_printf(1, "Error: read: /dev/urandom: %s", strerror(errno));
+ close(devrand);
+ return -1;
+ }
+ close(devrand);
+
+ memset(hash, 0, sizeof(hash));
+ h = HASH_Create(ht);
+ if (!h)
+ return -1;
+
+ HASH_Begin(h);
+ HASH_Update(h, key, key_len);
+ HASH_Update(h, (void *)req, sizeof(*req));
+ HASH_End(h, hash, &rlen, sizeof(hash));
+ HASH_Destroy(h);
+
+ memcpy(req->hash, hash, sizeof(req->hash));
+ return 0;
+}
+
+
+static int
+sha_verify(fence_req_t *req, void *key, size_t key_len)
+{
+ unsigned char hash[SHA512_LENGTH];
+ unsigned char pkt_hash[SHA512_LENGTH];
+ HASHContext *h = NULL;
+ HASH_HashType ht;
+ unsigned int rlen;
+ int ret;
+
+ switch(req->hashtype) {
+ case HASH_SHA1:
+ ht = HASH_AlgSHA1;
+ break;
+ case HASH_SHA256:
+ ht = HASH_AlgSHA256;
+ break;
+ case HASH_SHA512:
+ ht = HASH_AlgSHA512;
+ break;
+ default:
+ dbg_printf(3, "%s: no-op (HASH_NONE)\n", __FUNCTION__);
+ return 0;
+ }
+
+ if (!key || !key_len) {
+ dbg_printf(3, "%s: Hashing requested when we have no key data\n",
+ __FUNCTION__);
+ return 0;
+ }
+
+ memset(hash, 0, sizeof(hash));
+ h = HASH_Create(ht);
+ if (!h)
+ return 0;
+
+ memcpy(pkt_hash, req->hash, sizeof(pkt_hash));
+ memset(req->hash, 0, sizeof(req->hash));
+
+ HASH_Begin(h);
+ HASH_Update(h, key, key_len);
+ HASH_Update(h, (void *)req, sizeof(*req));
+ HASH_End(h, hash, &rlen, sizeof(hash));
+ HASH_Destroy(h);
+
+ memcpy(req->hash, pkt_hash, sizeof(req->hash));
+
+ ret = !memcmp(hash, pkt_hash, sizeof(hash));
+ if (!ret) {
+ printf("Hash mismatch:\nPKT = ");
+ print_hash(pkt_hash, sizeof(pkt_hash));
+ printf("\nEXP = ");
+ print_hash(hash, sizeof(hash));
+ printf("\n");
+ }
+
+ return ret;
+}
+
+
+int
+sign_request(fence_req_t *req, void *key, size_t key_len)
+{
+ memset(req->hash, 0, sizeof(req->hash));
+ switch(req->hashtype) {
+ case HASH_NONE:
+ dbg_printf(3, "%s: no-op (HASH_NONE)\n", __FUNCTION__);
+ return 0;
+ case HASH_SHA1:
+ case HASH_SHA256:
+ case HASH_SHA512:
+ return sha_sign(req, key, key_len);
+ default:
+ break;
+ }
+ return -1;
+}
+
+
+int
+verify_request(fence_req_t *req, fence_hash_t min,
+ void *key, size_t key_len)
+{
+ if (req->hashtype < min) {
+ printf("Hash type not strong enough (%d < %d)\n",
+ req->hashtype, min);
+ return 0;
+ }
+ switch(req->hashtype) {
+ case HASH_NONE:
+ return 1;
+ case HASH_SHA1:
+ case HASH_SHA256:
+ case HASH_SHA512:
+ return sha_verify(req, key, key_len);
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+static int
+sha_challenge(int fd, fence_auth_type_t auth, void *key,
+ size_t key_len, int timeout)
+{
+ fd_set rfds;
+ struct timeval tv;
+ unsigned char hash[MAX_HASH_LENGTH];
+ unsigned char challenge[MAX_HASH_LENGTH];
+ unsigned char response[MAX_HASH_LENGTH];
+ int devrand;
+ int ret;
+ HASHContext *h;
+ HASH_HashType ht;
+ unsigned int rlen;
+
+ devrand = open("/dev/urandom", O_RDONLY);
+ if (devrand < 0) {
+ dbg_printf(1, "Error: open /dev/urandom: %s", strerror(errno));
+ return 0;
+ }
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ ret = _read_retry(devrand, challenge, sizeof(challenge), &tv);
+ if (ret < 0) {
+ dbg_printf(1, "Error: read: /dev/urandom: %s", strerror(errno));
+ close(devrand);
+ return 0;
+ }
+ close(devrand);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ ret = _write_retry(fd, challenge, sizeof(challenge), &tv);
+ if (ret < 0) {
+ dbg_printf(2, "Error: write: %s", strerror(errno));
+ return 0;
+ }
+
+ switch(auth) {
+ case HASH_SHA1:
+ ht = HASH_AlgSHA1;
+ break;
+ case HASH_SHA256:
+ ht = HASH_AlgSHA256;
+ break;
+ case HASH_SHA512:
+ ht = HASH_AlgSHA512;
+ break;
+ default:
+ return 0;
+ }
+
+ memset(hash, 0, sizeof(hash));
+ h = HASH_Create(ht);
+ if (!h)
+ return 0;
+
+ HASH_Begin(h);
+ HASH_Update(h, key, key_len);
+ HASH_Update(h, challenge, sizeof(challenge));
+ HASH_End(h, hash, &rlen, sizeof(hash));
+ HASH_Destroy(h);
+
+ memset(response, 0, sizeof(response));
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ if (_select_retry(fd + 1, &rfds, NULL, NULL, &tv) <= 0) {
+ dbg_printf(0, "Error: select: %s\n", strerror(errno));
+ return 0;
+ }
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ ret = _read_retry(fd, response, sizeof(response), &tv);
+ if (ret < 0) {
+ dbg_printf(0, "Error reading challenge response: %s", strerror(errno));
+ return 0;
+ } else if (ret < sizeof(response)) {
+ dbg_printf(0,
+ "read data from socket is too short(actual: %d, expected: %zu)\n",
+ ret, sizeof(response));
+ return 0;
+ }
+
+ ret = !memcmp(response, hash, sizeof(response));
+ if (!ret) {
+ printf("Hash mismatch:\nC = ");
+ print_hash(challenge, sizeof(challenge));
+ printf("\nH = ");
+ print_hash(hash, sizeof(hash));
+ printf("\nR = ");
+ print_hash(response, sizeof(response));
+ printf("\n");
+ }
+
+ return ret;
+}
+
+
+static int
+sha_response(int fd, fence_auth_type_t auth, void *key,
+ size_t key_len, int timeout)
+{
+ fd_set rfds;
+ struct timeval tv;
+ unsigned char challenge[MAX_HASH_LENGTH];
+ unsigned char hash[MAX_HASH_LENGTH];
+ HASHContext *h;
+ HASH_HashType ht;
+ unsigned int rlen;
+ int ret;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ if (_select_retry(fd + 1, &rfds, NULL, NULL, &tv) <= 0) {
+ dbg_printf(2, "Error: select: %s\n", strerror(errno));
+ return 0;
+ }
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ if (_read_retry(fd, challenge, sizeof(challenge), &tv) < 0) {
+ dbg_printf(2, "Error reading challenge hash: %s\n", strerror(errno));
+ return 0;
+ }
+
+ switch(auth) {
+ case AUTH_SHA1:
+ ht = HASH_AlgSHA1;
+ break;
+ case AUTH_SHA256:
+ ht = HASH_AlgSHA256;
+ break;
+ case AUTH_SHA512:
+ ht = HASH_AlgSHA512;
+ break;
+ default:
+ dbg_printf(3, "%s: no-op (AUTH_NONE)\n", __FUNCTION__);
+ return 0;
+ }
+
+ memset(hash, 0, sizeof(hash));
+ h = HASH_Create(ht); /* */
+ if (!h)
+ return 0;
+
+ HASH_Begin(h);
+ HASH_Update(h, key, key_len);
+ HASH_Update(h, challenge, sizeof(challenge));
+ HASH_End(h, hash, &rlen, sizeof(hash));
+ HASH_Destroy(h);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ ret = _write_retry(fd, hash, sizeof(hash), &tv);
+ if (ret < 0) {
+ perror("write");
+ return 0;
+ } else if (ret < sizeof(hash)) {
+ dbg_printf(2,
+ "Only part of hash is written(actual: %d, expected: %zu)\n",
+ ret,
+ sizeof(hash));
+ return 0;
+ }
+
+ return 1;
+}
+
+
+int
+sock_challenge(int fd, fence_auth_type_t auth, void *key, size_t key_len,
+ int timeout)
+{
+ switch(auth) {
+ case AUTH_NONE:
+ dbg_printf(3, "%s: no-op (AUTH_NONE)\n", __FUNCTION__);
+ return 1;
+ case AUTH_SHA1:
+ case AUTH_SHA256:
+ case AUTH_SHA512:
+ return sha_challenge(fd, auth, key, key_len, timeout);
+ default:
+ break;
+ }
+ return -1;
+}
+
+
+int
+sock_response(int fd, fence_auth_type_t auth, void *key, size_t key_len,
+ int timeout)
+{
+ switch(auth) {
+ case AUTH_NONE:
+ dbg_printf(3, "%s: no-op (AUTH_NONE)\n", __FUNCTION__);
+ return 1;
+ case AUTH_SHA1:
+ case AUTH_SHA256:
+ case AUTH_SHA512:
+ return sha_response(fd, auth, key, key_len, timeout);
+ default:
+ break;
+ }
+ return -1;
+}
+
+
+int
+read_key_file(char *file, char *key, size_t max_len)
+{
+ int fd;
+ int nread, remain = max_len;
+ char *p;
+
+ dbg_printf(3, "Reading in key file %s into %p (%d max size)\n",
+ file, key, (int)max_len);
+ fd = open(file, O_RDONLY);
+ if (fd < 0) {
+ dbg_printf(2, "Error opening key file: %s\n", strerror(errno));
+ return -1;
+ }
+
+ memset(key, 0, max_len);
+ p = key;
+ remain = max_len;
+
+ while (remain) {
+ nread = read(fd, p, remain);
+ if (nread < 0) {
+ if (errno == EINTR)
+ continue;
+ dbg_printf(2, "Error from read: %s\n", strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ if (nread == 0) {
+ dbg_printf(3, "Stopped reading @ %d bytes\n",
+ (int)max_len-remain);
+ break;
+ }
+
+ p += nread;
+ remain -= nread;
+ }
+
+ close(fd);
+ dbg_printf(3, "Actual key length = %d bytes\n", (int)max_len-remain);
+
+ return (int)(max_len - remain);
+}
diff --git a/agents/virt/common/tcp.c b/agents/virt/common/tcp.c
new file mode 100644
index 0000000..5796770
--- /dev/null
+++ b/agents/virt/common/tcp.c
@@ -0,0 +1,386 @@
+/*
+ Copyright Red Hat, Inc. 2002-2004, 2006
+ Copyright Mission Critical Linux, 2000
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+/** @file
+ *
+ * @author Lon H. Hohberger <lhh at redhat.com>
+ * @author Jeff Moyer <jmoyer at redhat.com>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "tcp.h"
+#include "debug.h"
+
+static int connect_nb(int fd, struct sockaddr *dest, socklen_t len, int timeout);
+static int get_addr(const char *hostname, int family, struct sockaddr_storage *addr);
+
+/**
+ Set close-on-exec bit option for a socket.
+
+ @param fd Socket to set CLOEXEC flag
+ @return 0 on success, -1 on failure
+ @see fcntl
+ */
+static int
+set_cloexec(int fd)
+{
+ int flags = fcntl(fd, F_GETFD, 0);
+ flags |= FD_CLOEXEC;
+ return fcntl(fd, F_SETFD, flags);
+}
+
+
+/**
+ Bind to a port on the local IPv6 stack
+
+ @param addr_str Address to listen on, NULL for inaddr6_any
+ @param port Port to bind to
+ @param backlog same as backlog for listen(2)
+ @return 0 on success, -1 on failure
+ @see ipv4_bind
+ */
+int
+ipv6_listen(const char *addr_str, uint16_t port, int backlog)
+{
+ struct sockaddr_in6 _sin6;
+ int fd, opt=1;
+
+ dbg_printf(4, "%s: Setting up ipv6 listen socket for %s:%d\n",
+ __FUNCTION__, addr_str, port);
+
+ memset(&_sin6, 0, sizeof(_sin6));
+ _sin6.sin6_family = PF_INET6;
+ _sin6.sin6_port = htons(port);
+ _sin6.sin6_flowinfo = 0;
+
+ if (addr_str == NULL) {
+ _sin6.sin6_addr = in6addr_any;
+ } else {
+ struct sockaddr_storage ss;
+
+ if (get_addr(addr_str, AF_INET6, &ss) == -1) {
+ dbg_printf(4, "%s: Can't get addr for %s\n",
+ __FUNCTION__, addr_str);
+ return -1;
+ }
+
+ memcpy(&_sin6.sin6_addr,
+ &((struct sockaddr_in6 *)&ss)->sin6_addr, sizeof(_sin6.sin6_addr));
+ }
+
+ fd = socket(PF_INET6, SOCK_STREAM, 0);
+ if (fd < 0)
+ return -1;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof (opt)) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (set_cloexec(fd) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (bind(fd, (struct sockaddr *)&_sin6, sizeof(_sin6)) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (listen(fd, backlog) < 0){
+ close(fd);
+ return -1;
+ }
+
+ dbg_printf(4, "%s: Success; fd = %d\n", __FUNCTION__, fd);
+ return fd;
+}
+
+
+/**
+ Bind to a port on the local IPv4 stack
+
+ @param addr_str Address to listen on, NULL for inaddr_any
+ @param port Port to bind to
+ @param backlog same as backlog for listen(2)
+ @return 0 on success, -1 on failure
+ @see ipv6_bind
+ */
+int
+ipv4_listen(const char *addr_str, uint16_t port, int backlog)
+{
+ struct sockaddr_in _sin;
+ int fd, opt=1;
+
+ dbg_printf(4, "%s: Setting up ipv4 listen socket for %s:%d\n",
+ __FUNCTION__, addr_str, port);
+
+ _sin.sin_family = PF_INET;
+ _sin.sin_port = htons(port);
+
+ if (addr_str == NULL) {
+ _sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ } else {
+ struct sockaddr_storage ss;
+
+ if (get_addr(addr_str, AF_INET, &ss) == -1) {
+ dbg_printf(4, "%s: Can't get addr for %s\n",
+ __FUNCTION__, addr_str);
+ return -1;
+ }
+
+ memcpy(&_sin.sin_addr,
+ &((struct sockaddr_in *)&ss)->sin_addr, sizeof(_sin.sin_addr));
+ }
+
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0)
+ return -1;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof (opt)) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (set_cloexec(fd) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (bind(fd, (struct sockaddr *)&_sin, sizeof(_sin)) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (listen(fd, backlog) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ dbg_printf(4, "%s: Success; fd = %d\n", __FUNCTION__, fd);
+ return fd;
+}
+
+
+
+/**
+ Connect via ipv6 socket to a given IP address and port.
+
+ @param in6_addr IPv6 address to connect to
+ @param port Port to connect to
+ @param timeout Timeout, in seconds, to wait for a completed
+ connection
+ @return 0 on success, -1 on failure
+ @see connect_nb, ipv4_connect
+ */
+int
+ipv6_connect(struct in6_addr *in6_addr, uint16_t port, int timeout)
+{
+ struct sockaddr_in6 _sin6;
+ int fd, ret;
+
+ dbg_printf(4, "%s: Connecting to client\n", __FUNCTION__);
+ fd = socket(PF_INET6, SOCK_STREAM, 0);
+ if (fd < 0)
+ return -1;
+
+ memset(&_sin6, 0, sizeof(_sin6));
+ _sin6.sin6_family = PF_INET6;
+ _sin6.sin6_port = htons(port);
+ _sin6.sin6_flowinfo = 0;
+ memcpy(&_sin6.sin6_addr, in6_addr, sizeof(_sin6.sin6_addr));
+
+ ret = connect_nb(fd, (struct sockaddr *)&_sin6, sizeof(_sin6), timeout);
+ if (ret < 0) {
+ close(fd);
+ return -1;
+ }
+ dbg_printf(4, "%s: Success; fd = %d\n", __FUNCTION__, fd);
+ return fd;
+}
+
+
+/**
+ Connect via ipv4 socket to a given IP address and port.
+
+ @param in_addr IPv4 address to connect to
+ @param port Port to connect to
+ @param timeout Timeout, in seconds, to wait for a completed
+ connection
+ @return 0 on success, -1 on failure
+ @see connect_nb, ipv6_connect
+ */
+int
+ipv4_connect(struct in_addr *in_addr, uint16_t port, int timeout)
+{
+ struct sockaddr_in _sin;
+ int fd, ret;
+
+ dbg_printf(4, "%s: Connecting to client\n", __FUNCTION__);
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0)
+ return -1;
+
+ _sin.sin_family = PF_INET;
+ _sin.sin_port = htons(port);
+ memcpy(&_sin.sin_addr, in_addr, sizeof(_sin.sin_addr));
+
+ ret = connect_nb(fd, (struct sockaddr *)&_sin, sizeof(_sin), timeout);
+ if (ret < 0) {
+ close(fd);
+ return -1;
+ }
+
+ dbg_printf(4, "%s: Success; fd = %d\n", __FUNCTION__, fd);
+ return fd;
+}
+
+
+/**
+ Connect in a non-blocking fashion to the designated address.
+
+ @param fd File descriptor to connect
+ @param dest sockaddr (ipv4 or ipv6) to connect to.
+ @param len Length of dest
+ @param timeout Timeout, in seconds, to wait for a completed
+ connection.
+ @return 0 on success, -1 on failure.
+ */
+static int
+connect_nb(int fd, struct sockaddr *dest, socklen_t len, int timeout)
+{
+ int ret, flags = 1, err;
+ unsigned l;
+ fd_set rfds, wfds;
+ struct timeval tv;
+
+ /*
+ * Use TCP Keepalive
+ */
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags,
+ sizeof(flags))<0) {
+ return -1;
+ }
+
+ /*
+ Set up non-blocking connect
+ */
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags < 0) {
+ return -1;
+ }
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
+ return -1;
+ }
+
+ ret = connect(fd, dest, len);
+
+ if ((ret < 0) && (errno != EINPROGRESS))
+ return -1;
+
+ if (ret != 0) {
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ FD_ZERO(&wfds);
+ FD_SET(fd, &wfds);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ if (select(fd + 1, &rfds, &wfds, NULL, &tv) == 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ /* XXX check for -1 from select */
+
+ if (FD_ISSET(fd, &rfds) || FD_ISSET(fd, &wfds)) {
+ l = sizeof(err);
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR,
+ (void *)&err, &l) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (err != 0) {
+ close(fd);
+ errno = err;
+ return -1;
+ }
+
+ if (fcntl(fd, F_SETFL, flags) < 0) {
+ close(fd);
+ return -1;
+ }
+ return 0;
+ }
+ }
+
+ errno = EIO;
+ return -1;
+}
+
+static int
+get_addr(const char *hostname, int family, struct sockaddr_storage *addr)
+{
+ struct addrinfo *res;
+ size_t len;
+ struct addrinfo ai;
+
+ memset(&ai, 0, sizeof(ai));
+ ai.ai_family = family;
+
+ if (getaddrinfo(hostname, NULL, &ai, &res) != 0)
+ return -1;
+
+ switch (res->ai_addr->sa_family) {
+ case AF_INET:
+ len = sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ len = sizeof(struct sockaddr_in6);
+ break;
+ default:
+ goto out_fail;
+ }
+
+ if (len < (size_t) res->ai_addrlen)
+ goto out_fail;
+
+ memcpy(addr, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+
+ return 0;
+
+out_fail:
+ freeaddrinfo(res);
+ return -1;
+}