#include <linux/vm_sockets.h>
#endif /* CONFIG_AF_VSOCK */
+#include "qemu-common.h"
#include "monitor/monitor.h"
#include "qapi/clone-visitor.h"
#include "qapi/error.h"
+#include "qapi/qapi-visit-sockets.h"
#include "qemu/sockets.h"
#include "qemu/main-loop.h"
-#include "qapi/clone-visitor.h"
#include "qapi/qobject-input-visitor.h"
#include "qapi/qobject-output-visitor.h"
-#include "qapi-visit.h"
#include "qemu/cutils.h"
#ifndef AI_ADDRCONFIG
return NETWORK_ADDRESS_FAMILY_UNKNOWN;
}
+bool fd_is_socket(int fd)
+{
+ int optval;
+ socklen_t optlen = sizeof(optval);
+ return !qemu_getsockopt(fd, SOL_SOCKET, SO_TYPE, &optval, &optlen);
+}
+
+
/*
* Matrix we're trying to apply
*
static int inet_listen_saddr(InetSocketAddress *saddr,
int port_offset,
- bool update_addr,
Error **errp)
{
struct addrinfo ai,*res,*e;
char uaddr[INET6_ADDRSTRLEN+1];
char uport[33];
int rc, port_min, port_max, p;
- int slisten = 0;
+ int slisten = -1;
int saved_errno = 0;
bool socket_created = false;
Error *err = NULL;
/* create socket + bind/listen */
for (e = res; e != NULL; e = e->ai_next) {
getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
- uaddr,INET6_ADDRSTRLEN,uport,32,
- NI_NUMERICHOST | NI_NUMERICSERV);
+ uaddr,INET6_ADDRSTRLEN,uport,32,
+ NI_NUMERICHOST | NI_NUMERICSERV);
- slisten = create_fast_reuse_socket(e);
- if (slisten < 0) {
- continue;
- }
-
- socket_created = true;
port_min = inet_getport(e);
port_max = saddr->has_to ? saddr->to + port_offset : port_min;
for (p = port_min; p <= port_max; p++) {
inet_setport(e, p);
- rc = try_bind(slisten, saddr, e);
- if (rc) {
- if (errno == EADDRINUSE) {
+
+ slisten = create_fast_reuse_socket(e);
+ if (slisten < 0) {
+ /* First time we expect we might fail to create the socket
+ * eg if 'e' has AF_INET6 but ipv6 kmod is not loaded.
+ * Later iterations should always succeed if first iteration
+ * worked though, so treat that as fatal.
+ */
+ if (p == port_min) {
continue;
} else {
- error_setg_errno(errp, errno, "Failed to bind socket");
+ error_setg_errno(errp, errno,
+ "Failed to recreate failed listening socket");
goto listen_failed;
}
}
- if (!listen(slisten, 1)) {
- goto listen_ok;
- }
- if (errno != EADDRINUSE) {
- error_setg_errno(errp, errno, "Failed to listen on socket");
- goto listen_failed;
+ socket_created = true;
+
+ rc = try_bind(slisten, saddr, e);
+ if (rc < 0) {
+ if (errno != EADDRINUSE) {
+ error_setg_errno(errp, errno, "Failed to bind socket");
+ goto listen_failed;
+ }
+ } else {
+ if (!listen(slisten, 1)) {
+ goto listen_ok;
+ }
+ if (errno != EADDRINUSE) {
+ error_setg_errno(errp, errno, "Failed to listen on socket");
+ goto listen_failed;
+ }
}
/* Someone else managed to bind to the same port and beat us
* to listen on it! Socket semantics does not allow us to
* socket to allow bind attempts for subsequent ports:
*/
closesocket(slisten);
- slisten = create_fast_reuse_socket(e);
- if (slisten < 0) {
- error_setg_errno(errp, errno,
- "Failed to recreate failed listening socket");
- goto listen_failed;
- }
+ slisten = -1;
}
}
error_setg_errno(errp, errno,
return -1;
listen_ok:
- if (update_addr) {
- g_free(saddr->host);
- saddr->host = g_strdup(uaddr);
- g_free(saddr->port);
- saddr->port = g_strdup_printf("%d",
- inet_getport(e) - port_offset);
- saddr->has_ipv6 = saddr->ipv6 = e->ai_family == PF_INET6;
- saddr->has_ipv4 = saddr->ipv4 = e->ai_family != PF_INET6;
- }
freeaddrinfo(res);
return slisten;
}
}
/* compatibility wrapper */
+static int inet_parse_flag(const char *flagname, const char *optstr, bool *val,
+ Error **errp)
+{
+ char *end;
+ size_t len;
+
+ end = strstr(optstr, ",");
+ if (end) {
+ if (end[1] == ',') { /* Reject 'ipv6=on,,foo' */
+ error_setg(errp, "error parsing '%s' flag '%s'", flagname, optstr);
+ return -1;
+ }
+ len = end - optstr;
+ } else {
+ len = strlen(optstr);
+ }
+ if (len == 0 || (len == 3 && strncmp(optstr, "=on", len) == 0)) {
+ *val = true;
+ } else if (len == 4 && strncmp(optstr, "=off", len) == 0) {
+ *val = false;
+ } else {
+ error_setg(errp, "error parsing '%s' flag '%s'", flagname, optstr);
+ return -1;
+ }
+ return 0;
+}
+
int inet_parse(InetSocketAddress *addr, const char *str, Error **errp)
{
const char *optstr, *h;
char port[33];
int to;
int pos;
+ char *begin;
memset(addr, 0, sizeof(*addr));
addr->has_to = true;
addr->to = to;
}
- if (strstr(optstr, ",ipv4")) {
- addr->ipv4 = addr->has_ipv4 = true;
+ begin = strstr(optstr, ",ipv4");
+ if (begin) {
+ if (inet_parse_flag("ipv4", begin + 5, &addr->ipv4, errp) < 0) {
+ return -1;
+ }
+ addr->has_ipv4 = true;
}
- if (strstr(optstr, ",ipv6")) {
- addr->ipv6 = addr->has_ipv6 = true;
+ begin = strstr(optstr, ",ipv6");
+ if (begin) {
+ if (inet_parse_flag("ipv6", begin + 5, &addr->ipv6, errp) < 0) {
+ return -1;
+ }
+ addr->has_ipv6 = true;
}
return 0;
}
#ifndef _WIN32
static int unix_listen_saddr(UnixSocketAddress *saddr,
- bool update_addr,
Error **errp)
{
struct sockaddr_un un;
int sock, fd;
char *pathbuf = NULL;
const char *path;
+ size_t pathlen;
sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
path = pathbuf = g_strdup_printf("%s/qemu-socket-XXXXXX", tmpdir);
}
- if (strlen(path) > sizeof(un.sun_path)) {
+ pathlen = strlen(path);
+ if (pathlen > sizeof(un.sun_path)) {
error_setg(errp, "UNIX socket path '%s' is too long", path);
error_append_hint(errp, "Path must be less than %zu bytes\n",
sizeof(un.sun_path));
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
- strncpy(un.sun_path, path, sizeof(un.sun_path));
+ memcpy(un.sun_path, path, pathlen);
if (bind(sock, (struct sockaddr*) &un, sizeof(un)) < 0) {
error_setg_errno(errp, errno, "Failed to bind socket to %s", path);
goto err;
}
- if (update_addr && pathbuf) {
- g_free(saddr->path);
- saddr->path = pathbuf;
- } else {
- g_free(pathbuf);
- }
+ g_free(pathbuf);
return sock;
err:
{
struct sockaddr_un un;
int sock, rc;
+ size_t pathlen;
if (saddr->path == NULL) {
error_setg(errp, "unix connect: no path specified");
return -1;
}
- if (strlen(saddr->path) > sizeof(un.sun_path)) {
+ pathlen = strlen(saddr->path);
+ if (pathlen > sizeof(un.sun_path)) {
error_setg(errp, "UNIX socket path '%s' is too long", saddr->path);
error_append_hint(errp, "Path must be less than %zu bytes\n",
sizeof(un.sun_path));
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
- strncpy(un.sun_path, saddr->path, sizeof(un.sun_path));
+ memcpy(un.sun_path, saddr->path, pathlen);
/* connect to peer */
do {
#else
static int unix_listen_saddr(UnixSocketAddress *saddr,
- bool update_addr,
Error **errp)
{
error_setg(errp, "unix sockets are not available on windows");
#endif
/* compatibility wrapper */
-int unix_listen(const char *str, char *ostr, int olen, Error **errp)
+int unix_listen(const char *str, Error **errp)
{
- char *path, *optstr;
- int sock, len;
UnixSocketAddress *saddr;
+ int sock;
saddr = g_new0(UnixSocketAddress, 1);
-
- optstr = strchr(str, ',');
- if (optstr) {
- len = optstr - str;
- if (len) {
- path = g_malloc(len+1);
- snprintf(path, len+1, "%.*s", len, str);
- saddr->path = path;
- }
- } else {
- saddr->path = g_strdup(str);
- }
-
- sock = unix_listen_saddr(saddr, true, errp);
-
- if (sock != -1 && ostr) {
- snprintf(ostr, olen, "%s%s", saddr->path, optstr ? optstr : "");
- }
-
+ saddr->path = g_strdup(str);
+ sock = unix_listen_saddr(saddr, errp);
qapi_free_UnixSocketAddress(saddr);
return sock;
}
return NULL;
}
+static int socket_get_fd(const char *fdstr, Error **errp)
+{
+ int fd;
+ if (cur_mon) {
+ fd = monitor_get_fd(cur_mon, fdstr, errp);
+ if (fd < 0) {
+ return -1;
+ }
+ } else {
+ if (qemu_strtoi(fdstr, NULL, 10, &fd) < 0) {
+ error_setg_errno(errp, errno,
+ "Unable to parse FD number %s",
+ fdstr);
+ return -1;
+ }
+ }
+ if (!fd_is_socket(fd)) {
+ error_setg(errp, "File descriptor '%s' is not a socket", fdstr);
+ close(fd);
+ return -1;
+ }
+ return fd;
+}
+
int socket_connect(SocketAddress *addr, Error **errp)
{
int fd;
break;
case SOCKET_ADDRESS_TYPE_FD:
- fd = monitor_get_fd(cur_mon, addr->u.fd.str, errp);
+ fd = socket_get_fd(addr->u.fd.str, errp);
break;
case SOCKET_ADDRESS_TYPE_VSOCK:
switch (addr->type) {
case SOCKET_ADDRESS_TYPE_INET:
- fd = inet_listen_saddr(&addr->u.inet, 0, false, errp);
+ fd = inet_listen_saddr(&addr->u.inet, 0, errp);
break;
case SOCKET_ADDRESS_TYPE_UNIX:
- fd = unix_listen_saddr(&addr->u.q_unix, false, errp);
+ fd = unix_listen_saddr(&addr->u.q_unix, errp);
break;
case SOCKET_ADDRESS_TYPE_FD:
- fd = monitor_get_fd(cur_mon, addr->u.fd.str, errp);
+ fd = socket_get_fd(addr->u.fd.str, errp);
break;
case SOCKET_ADDRESS_TYPE_VSOCK:
SocketAddress *addr;
addr = socket_local_address(fd, errp);
+ if (!addr) {
+ return;
+ }
if (addr->type == SOCKET_ADDRESS_TYPE_UNIX
&& addr->u.q_unix.path) {