summaryrefslogtreecommitdiffstats
path: root/util/qemu-sockets.c
diff options
context:
space:
mode:
authorPeter Maydell2017-10-17 12:29:51 +0200
committerPeter Maydell2017-10-17 12:29:52 +0200
commitdabc50e4c68c1be046d4a42908af0f9df69f910a (patch)
tree91f9f2dcdd15564650b79f8ad0bf5ad953995634 /util/qemu-sockets.c
parentMerge remote-tracking branch 'remotes/gkurz/tags/for-upstream' into staging (diff)
parentio: fix mem leak in websock error path (diff)
downloadqemu-dabc50e4c68c1be046d4a42908af0f9df69f910a.tar.gz
qemu-dabc50e4c68c1be046d4a42908af0f9df69f910a.tar.xz
qemu-dabc50e4c68c1be046d4a42908af0f9df69f910a.zip
Merge remote-tracking branch 'remotes/berrange/tags/pull-qio-2017-10-16-1' into staging
Merge QIO 2017/10/16 v1 # gpg: Signature made Mon 16 Oct 2017 17:10:54 BST # gpg: using RSA key 0xBE86EBB415104FDF # gpg: Good signature from "Daniel P. Berrange <dan@berrange.com>" # gpg: aka "Daniel P. Berrange <berrange@redhat.com>" # Primary key fingerprint: DAF3 A6FD B26B 6291 2D0E 8E3F BE86 EBB4 1510 4FDF * remotes/berrange/tags/pull-qio-2017-10-16-1: io: fix mem leak in websock error path io: add trace points for websocket HTTP protocol headers io: cope with websock 'Connection' header having multiple values io: get rid of bounce buffering in websock write path io: pass a struct iovec into qio_channel_websock_encode io: get rid of qio_channel_websock_encode helper method io: simplify websocket ping reply handling io: monitor encoutput buffer size from websocket GSource sockets: Handle race condition between binds to the same port sockets: factor out create_fast_reuse_socket sockets: factor out a new try_bind() function Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'util/qemu-sockets.c')
-rw-r--r--util/qemu-sockets.c138
1 files changed, 90 insertions, 48 deletions
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index d149383bb9..b47fb45885 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -149,6 +149,54 @@ int inet_ai_family_from_address(InetSocketAddress *addr,
return PF_UNSPEC;
}
+static int create_fast_reuse_socket(struct addrinfo *e)
+{
+ int slisten = qemu_socket(e->ai_family, e->ai_socktype, e->ai_protocol);
+ if (slisten < 0) {
+ return -1;
+ }
+ socket_set_fast_reuse(slisten);
+ return slisten;
+}
+
+static int try_bind(int socket, InetSocketAddress *saddr, struct addrinfo *e)
+{
+#ifndef IPV6_V6ONLY
+ return bind(socket, e->ai_addr, e->ai_addrlen);
+#else
+ /*
+ * Deals with first & last cases in matrix in comment
+ * for inet_ai_family_from_address().
+ */
+ int v6only =
+ ((!saddr->has_ipv4 && !saddr->has_ipv6) ||
+ (saddr->has_ipv4 && saddr->ipv4 &&
+ saddr->has_ipv6 && saddr->ipv6)) ? 0 : 1;
+ int stat;
+
+ rebind:
+ if (e->ai_family == PF_INET6) {
+ qemu_setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, &v6only,
+ sizeof(v6only));
+ }
+
+ stat = bind(socket, e->ai_addr, e->ai_addrlen);
+ if (!stat) {
+ return 0;
+ }
+
+ /* If we got EADDRINUSE from an IPv6 bind & v6only is unset,
+ * it could be that the IPv4 port is already claimed, so retry
+ * with v6only set
+ */
+ if (e->ai_family == PF_INET6 && errno == EADDRINUSE && !v6only) {
+ v6only = 1;
+ goto rebind;
+ }
+ return stat;
+#endif
+}
+
static int inet_listen_saddr(InetSocketAddress *saddr,
int port_offset,
bool update_addr,
@@ -158,7 +206,10 @@ static int inet_listen_saddr(InetSocketAddress *saddr,
char port[33];
char uaddr[INET6_ADDRSTRLEN+1];
char uport[33];
- int slisten, rc, port_min, port_max, p;
+ int rc, port_min, port_max, p;
+ int slisten = 0;
+ int saved_errno = 0;
+ bool socket_created = false;
Error *err = NULL;
memset(&ai,0, sizeof(ai));
@@ -210,75 +261,66 @@ static int inet_listen_saddr(InetSocketAddress *saddr,
return -1;
}
- /* create socket + bind */
+ /* 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);
- slisten = qemu_socket(e->ai_family, e->ai_socktype, e->ai_protocol);
+
+ slisten = create_fast_reuse_socket(e);
if (slisten < 0) {
- if (!e->ai_next) {
- error_setg_errno(errp, errno, "Failed to create socket");
- }
continue;
}
- socket_set_fast_reuse(slisten);
-
+ 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++) {
-#ifdef IPV6_V6ONLY
- /*
- * Deals with first & last cases in matrix in comment
- * for inet_ai_family_from_address().
- */
- int v6only =
- ((!saddr->has_ipv4 && !saddr->has_ipv6) ||
- (saddr->has_ipv4 && saddr->ipv4 &&
- saddr->has_ipv6 && saddr->ipv6)) ? 0 : 1;
-#endif
inet_setport(e, p);
-#ifdef IPV6_V6ONLY
- rebind:
- if (e->ai_family == PF_INET6) {
- qemu_setsockopt(slisten, IPPROTO_IPV6, IPV6_V6ONLY, &v6only,
- sizeof(v6only));
+ rc = try_bind(slisten, saddr, e);
+ if (rc) {
+ if (errno == EADDRINUSE) {
+ continue;
+ } else {
+ error_setg_errno(errp, errno, "Failed to bind socket");
+ goto listen_failed;
+ }
}
-#endif
- if (bind(slisten, e->ai_addr, e->ai_addrlen) == 0) {
- goto listen;
+ if (!listen(slisten, 1)) {
+ goto listen_ok;
}
-
-#ifdef IPV6_V6ONLY
- /* If we got EADDRINUSE from an IPv6 bind & V6ONLY is unset,
- * it could be that the IPv4 port is already claimed, so retry
- * with V6ONLY set
- */
- if (e->ai_family == PF_INET6 && errno == EADDRINUSE && !v6only) {
- v6only = 1;
- goto rebind;
+ if (errno != EADDRINUSE) {
+ error_setg_errno(errp, errno, "Failed to listen on socket");
+ goto listen_failed;
}
-#endif
-
- if (p == port_max) {
- if (!e->ai_next) {
- error_setg_errno(errp, errno, "Failed to bind socket");
- }
+ /* Someone else managed to bind to the same port and beat us
+ * to listen on it! Socket semantics does not allow us to
+ * recover from this situation, so we need to recreate the
+ * 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;
}
}
+ }
+ error_setg_errno(errp, errno,
+ socket_created ?
+ "Failed to find an available port" :
+ "Failed to create a socket");
+listen_failed:
+ saved_errno = errno;
+ if (slisten >= 0) {
closesocket(slisten);
}
freeaddrinfo(res);
+ errno = saved_errno;
return -1;
-listen:
- if (listen(slisten,1) != 0) {
- error_setg_errno(errp, errno, "Failed to listen on socket");
- closesocket(slisten);
- freeaddrinfo(res);
- return -1;
- }
+listen_ok:
if (update_addr) {
g_free(saddr->host);
saddr->host = g_strdup(uaddr);