mirror of
https://github.com/qemu/qemu.git
synced 2026-04-24 15:13:12 +00:00
Use common qemu_set_blocking() instead. Note that pre-patch the behavior of Win32 and Linux realizations are inconsistent: we ignore failure for Win32, and assert success for Linux. How do we convert the callers? 1. Most of callers call qemu_socket_set_nonblock() on a freshly created socket fd, in conditions when we may simply report an error. Seems correct switching to error handling both for Windows (pre-patch error is ignored) and Linux (pre-patch we assert success). Anyway, we normally don't expect errors in these cases. Still in tests let's use &error_abort for simplicity. What are exclusions? 2. hw/virtio/vhost-user.c - we are inside #ifdef CONFIG_LINUX, so no damage in switching to error handling from assertion. 3. io/channel-socket.c: here we convert both old calls to qemu_socket_set_nonblock() and qemu_socket_set_block() to one new call. Pre-patch we assert success for Linux in qemu_socket_set_nonblock(), and ignore all other errors here. So, for Windows switch is a bit dangerous: we may get new errors or crashes(when error_abort is passed) in cases where we have silently ignored the error before (was it correct in all such cases, if they were?) Still, there is no other way to stricter API than take this risk. 4. util/vhost-user-server - compiled only for Linux (see util/meson.build), so we are safe, switching from assertion to &error_abort. Note: In qga/channel-posix.c we use g_warning(), where g_printerr() would actually be a better choice. Still let's for now follow common style of qga, where g_warning() is commonly used to print such messages, and no call to g_printerr(). Converting everything to use g_printerr() should better be another series. Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
176 lines
3.9 KiB
C
176 lines
3.9 KiB
C
/*
|
|
* Helper functions for tests using sockets
|
|
*
|
|
* Copyright 2015-2018 Red Hat, Inc.
|
|
*
|
|
* 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) version 3 of the License.
|
|
*
|
|
* 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; if not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qapi/error.h"
|
|
#include "qemu/sockets.h"
|
|
#include "socket-helpers.h"
|
|
|
|
#ifndef AI_ADDRCONFIG
|
|
# define AI_ADDRCONFIG 0
|
|
#endif
|
|
#ifndef EAI_ADDRFAMILY
|
|
# define EAI_ADDRFAMILY 0
|
|
#endif
|
|
|
|
/*
|
|
* @hostname: a DNS name or numeric IP address
|
|
*
|
|
* Check whether it is possible to bind & connect to ports
|
|
* on the DNS name or IP address @hostname. If an IP address
|
|
* is used, it must not be a wildcard address.
|
|
*
|
|
* Returns 0 on success, -1 on error with errno set
|
|
*/
|
|
static int socket_can_bind_connect(const char *hostname, int family)
|
|
{
|
|
int lfd = -1, cfd = -1, afd = -1;
|
|
struct addrinfo ai, *res = NULL;
|
|
struct sockaddr_storage ss;
|
|
socklen_t sslen = sizeof(ss);
|
|
int soerr;
|
|
socklen_t soerrlen = sizeof(soerr);
|
|
bool check_soerr = false;
|
|
int rc;
|
|
int ret = -1;
|
|
|
|
memset(&ai, 0, sizeof(ai));
|
|
ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
|
|
ai.ai_family = family;
|
|
ai.ai_socktype = SOCK_STREAM;
|
|
|
|
/* lookup */
|
|
rc = getaddrinfo(hostname, NULL, &ai, &res);
|
|
if (rc != 0) {
|
|
if (rc == EAI_ADDRFAMILY || rc == EAI_FAMILY || rc == EAI_NONAME) {
|
|
errno = EADDRNOTAVAIL;
|
|
} else {
|
|
errno = EINVAL;
|
|
}
|
|
goto cleanup;
|
|
}
|
|
|
|
lfd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
|
if (lfd < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
cfd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
|
if (cfd < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (bind(lfd, res->ai_addr, res->ai_addrlen) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (listen(lfd, 1) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (getsockname(lfd, (struct sockaddr *)&ss, &sslen) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
qemu_set_blocking(cfd, false, &error_abort);
|
|
|
|
if (connect(cfd, (struct sockaddr *)&ss, sslen) < 0) {
|
|
if (errno == EINPROGRESS) {
|
|
check_soerr = true;
|
|
} else {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
sslen = sizeof(ss);
|
|
afd = accept(lfd, (struct sockaddr *)&ss, &sslen);
|
|
if (afd < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (check_soerr) {
|
|
if (getsockopt(cfd, SOL_SOCKET, SO_ERROR, &soerr, &soerrlen) < 0) {
|
|
goto cleanup;
|
|
}
|
|
if (soerr) {
|
|
errno = soerr;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
if (afd != -1) {
|
|
close(afd);
|
|
}
|
|
if (cfd != -1) {
|
|
close(cfd);
|
|
}
|
|
if (lfd != -1) {
|
|
close(lfd);
|
|
}
|
|
if (res) {
|
|
freeaddrinfo(res);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
int socket_check_protocol_support(bool *has_ipv4, bool *has_ipv6)
|
|
{
|
|
*has_ipv4 = *has_ipv6 = false;
|
|
|
|
if (socket_can_bind_connect("127.0.0.1", PF_INET) < 0) {
|
|
if (errno != EADDRNOTAVAIL) {
|
|
return -1;
|
|
}
|
|
} else {
|
|
*has_ipv4 = true;
|
|
}
|
|
|
|
if (socket_can_bind_connect("::1", PF_INET6) < 0) {
|
|
if (errno != EADDRNOTAVAIL) {
|
|
return -1;
|
|
}
|
|
} else {
|
|
*has_ipv6 = true;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void socket_check_afunix_support(bool *has_afunix)
|
|
{
|
|
int fd;
|
|
|
|
fd = socket(PF_UNIX, SOCK_STREAM, 0);
|
|
|
|
#ifdef _WIN32
|
|
*has_afunix = (fd != (int)INVALID_SOCKET);
|
|
#else
|
|
*has_afunix = (fd >= 0);
|
|
#endif
|
|
|
|
if (*has_afunix) {
|
|
close(fd);
|
|
}
|
|
}
|