-----BEGIN PGP SIGNATURE-----
iQIcBAABCAAGBQJaqBR+AAoJEL6G67QVEE/fqHQP/2qAzY8BVheN28qVMB5UoHh7 fz3IqgearImhtYJNgfp3GHTOZTgvdSONI7o+q7yw27ORh4c0zPNQpm9dBmzm9H7x yCojRhD0gH2X1mwoLjEfCkdJpPBNP0/DP6JwNkwS/Mg6GwRQbIiN6W7OPyjtp5fF ImlCMB9ap8aQmoCaVqbRE7B/8YpjLg5/YY3kqhz5K8NMuOl4W3/icLD/0z35M3et cSos831a+UZqUOBKjlj7MSFYaLX40gqKvkZ5bGkh1HAPxDh/jRiU/eaMZmJDSVgY 6PuhUv0sg3F0FHd6udfQVP8ogbQf0bDyLyeP4cHiWHAWjvHTg+th2mMuFGQ9T4qs 5E0ZEiTO+iliWthvcqrtDQNlR447BnghBL5AVRCuSBLZyLX6EGlQPRlP/bLbNYDu e2tZ4pl095vDjuDHpu0Aq7CPUw4lS3Oyipvsr2QBMs4bBsZ57UjKoiT5a/7Z5Hhk D0r9JFXCjWYB56IiG4NMEXqeuLo+K0n9F1lLrHfSnH2b7YrKFAGYdDCw2Omxu8kB j5wfnnhst20x2yEOSMjPyHCKIRKyxDOFvPgvjD+9s9MMBuLHtKx+jeOcJA+9DnyK S/50+bEVEv/ef/s1Nc4wSRdI26WMwh5veTKVoSi9BoQd8BGsp+ucb/jZBQR20emI CKHxvYuR/2ySra0P9KI8 =QeRU -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/berrange/tags/socket-next-pull-request' into staging # gpg: Signature made Tue 13 Mar 2018 18:12:14 GMT # gpg: using RSA key BE86EBB415104FDF # 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/socket-next-pull-request: char: allow passing pre-opened socket file descriptor at startup char: refactor parsing of socket address information sockets: allow SocketAddress 'fd' to reference numeric file descriptors sockets: check that the named file descriptor is a socket sockets: move fd_is_socket() into common sockets code sockets: strengthen test suite IP protocol availability checks sockets: pull code for testing IP availability out of specific test cutils: add qemu_strtoi & qemu_strtoui parsers for int/unsigned int types char: don't silently skip tn3270 protocol init when TLS is enabled Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
58888f8cdd
|
@ -663,8 +663,7 @@ static void tcp_chr_tls_handshake(QIOTask *task,
|
|||
if (qio_task_propagate_error(task, NULL)) {
|
||||
tcp_chr_disconnect(chr);
|
||||
} else {
|
||||
/* tn3270 does not support TLS yet */
|
||||
if (s->do_telnetopt && !s->is_tn3270) {
|
||||
if (s->do_telnetopt) {
|
||||
tcp_chr_telnet_init(chr);
|
||||
} else {
|
||||
tcp_chr_connect(chr);
|
||||
|
@ -1009,25 +1008,36 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
|
|||
const char *path = qemu_opt_get(opts, "path");
|
||||
const char *host = qemu_opt_get(opts, "host");
|
||||
const char *port = qemu_opt_get(opts, "port");
|
||||
const char *fd = qemu_opt_get(opts, "fd");
|
||||
const char *tls_creds = qemu_opt_get(opts, "tls-creds");
|
||||
SocketAddressLegacy *addr;
|
||||
ChardevSocket *sock;
|
||||
|
||||
if ((!!path + !!fd + !!host) != 1) {
|
||||
error_setg(errp,
|
||||
"Exactly one of 'path', 'fd' or 'host' required");
|
||||
return;
|
||||
}
|
||||
|
||||
backend->type = CHARDEV_BACKEND_KIND_SOCKET;
|
||||
if (!path) {
|
||||
if (!host) {
|
||||
error_setg(errp, "chardev: socket: no host given");
|
||||
return;
|
||||
}
|
||||
if (!port) {
|
||||
error_setg(errp, "chardev: socket: no port given");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (path) {
|
||||
if (tls_creds) {
|
||||
error_setg(errp, "TLS can only be used over TCP socket");
|
||||
return;
|
||||
}
|
||||
} else if (host) {
|
||||
if (!port) {
|
||||
error_setg(errp, "chardev: socket: no port given");
|
||||
return;
|
||||
}
|
||||
} else if (fd) {
|
||||
/* We don't know what host to validate against when in client mode */
|
||||
if (tls_creds && !is_listen) {
|
||||
error_setg(errp, "TLS can not be used with pre-opened client FD");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
sock = backend->u.socket.data = g_new0(ChardevSocket, 1);
|
||||
|
@ -1053,7 +1063,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
|
|||
addr->type = SOCKET_ADDRESS_LEGACY_KIND_UNIX;
|
||||
q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
|
||||
q_unix->path = g_strdup(path);
|
||||
} else {
|
||||
} else if (host) {
|
||||
addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET;
|
||||
addr->u.inet.data = g_new(InetSocketAddress, 1);
|
||||
*addr->u.inet.data = (InetSocketAddress) {
|
||||
|
@ -1066,6 +1076,12 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
|
|||
.has_ipv6 = qemu_opt_get(opts, "ipv6"),
|
||||
.ipv6 = qemu_opt_get_bool(opts, "ipv6", 0),
|
||||
};
|
||||
} else if (fd) {
|
||||
addr->type = SOCKET_ADDRESS_LEGACY_KIND_FD;
|
||||
addr->u.fd.data = g_new(String, 1);
|
||||
addr->u.fd.data->str = g_strdup(fd);
|
||||
} else {
|
||||
g_assert_not_reached();
|
||||
}
|
||||
sock->addr = addr;
|
||||
}
|
||||
|
|
|
@ -807,6 +807,9 @@ QemuOptsList qemu_chardev_opts = {
|
|||
},{
|
||||
.name = "port",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},{
|
||||
.name = "fd",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},{
|
||||
.name = "localaddr",
|
||||
.type = QEMU_OPT_STRING,
|
||||
|
|
|
@ -126,6 +126,10 @@ time_t mktimegm(struct tm *tm);
|
|||
int qemu_fdatasync(int fd);
|
||||
int fcntl_setfl(int fd, int flag);
|
||||
int qemu_parse_fd(const char *param);
|
||||
int qemu_strtoi(const char *nptr, const char **endptr, int base,
|
||||
int *result);
|
||||
int qemu_strtoui(const char *nptr, const char **endptr, int base,
|
||||
unsigned int *result);
|
||||
int qemu_strtol(const char *nptr, const char **endptr, int base,
|
||||
long *result);
|
||||
int qemu_strtoul(const char *nptr, const char **endptr, int base,
|
||||
|
|
|
@ -12,6 +12,7 @@ int inet_aton(const char *cp, struct in_addr *ia);
|
|||
#include "qapi/qapi-types-sockets.h"
|
||||
|
||||
/* misc helpers */
|
||||
bool fd_is_socket(int fd);
|
||||
int qemu_socket(int domain, int type, int protocol);
|
||||
int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
|
||||
int socket_set_cork(int fd, int v);
|
||||
|
|
|
@ -24,19 +24,6 @@
|
|||
#include "io/channel-socket.h"
|
||||
|
||||
|
||||
static bool fd_is_socket(int fd)
|
||||
{
|
||||
int optval;
|
||||
socklen_t optlen;
|
||||
optlen = sizeof(optval);
|
||||
return qemu_getsockopt(fd,
|
||||
SOL_SOCKET,
|
||||
SO_TYPE,
|
||||
(char *)&optval,
|
||||
&optlen) == 0;
|
||||
}
|
||||
|
||||
|
||||
QIOChannel *qio_channel_new_fd(int fd,
|
||||
Error **errp)
|
||||
{
|
||||
|
|
|
@ -123,6 +123,13 @@
|
|||
#
|
||||
# @unix: Unix domain socket
|
||||
#
|
||||
# @vsock: VMCI address
|
||||
#
|
||||
# @fd: decimal is for file descriptor number, otherwise a file descriptor name.
|
||||
# Named file descriptors are permitted in monitor commands, in combination
|
||||
# with the 'getfd' command. Decimal file descriptors are permitted at
|
||||
# startup or other contexts where no monitor context is active.
|
||||
#
|
||||
# Since: 2.9
|
||||
##
|
||||
{ 'enum': 'SocketAddressType',
|
||||
|
|
1
tests/.gitignore
vendored
1
tests/.gitignore
vendored
|
@ -86,6 +86,7 @@ test-thread-pool
|
|||
test-throttle
|
||||
test-timed-average
|
||||
test-uuid
|
||||
test-util-sockets
|
||||
test-visitor-serialization
|
||||
test-vmstate
|
||||
test-write-threshold
|
||||
|
|
|
@ -146,6 +146,7 @@ ifneq (,$(findstring qemu-ga,$(TOOLS)))
|
|||
check-unit-$(CONFIG_LINUX) += tests/test-qga$(EXESUF)
|
||||
endif
|
||||
check-unit-y += tests/test-timed-average$(EXESUF)
|
||||
check-unit-y += tests/test-util-sockets$(EXESUF)
|
||||
check-unit-y += tests/test-io-task$(EXESUF)
|
||||
check-unit-y += tests/test-io-channel-socket$(EXESUF)
|
||||
check-unit-y += tests/test-io-channel-file$(EXESUF)
|
||||
|
@ -713,9 +714,11 @@ tests/test-crypto-tlscredsx509$(EXESUF): tests/test-crypto-tlscredsx509.o \
|
|||
tests/test-crypto-tlssession.o-cflags := $(TASN1_CFLAGS)
|
||||
tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \
|
||||
tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o $(test-crypto-obj-y)
|
||||
tests/test-util-sockets$(EXESUF): tests/test-util-sockets.o \
|
||||
tests/socket-helpers.o $(test-util-obj-y)
|
||||
tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y)
|
||||
tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \
|
||||
tests/io-channel-helpers.o $(test-io-obj-y)
|
||||
tests/io-channel-helpers.o tests/socket-helpers.o $(test-io-obj-y)
|
||||
tests/tpm-crb-test$(EXESUF): tests/tpm-crb-test.o tests/tpm-emu.o $(test-io-obj-y)
|
||||
tests/tpm-tis-test$(EXESUF): tests/tpm-tis-test.o tests/tpm-emu.o $(test-io-obj-y)
|
||||
tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \
|
||||
|
|
149
tests/socket-helpers.c
Normal file
149
tests/socket-helpers.c
Normal file
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* 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 "qemu-common.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
|
||||
|
||||
int socket_can_bind_connect(const char *hostname)
|
||||
{
|
||||
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 = AF_UNSPEC;
|
||||
ai.ai_socktype = SOCK_STREAM;
|
||||
|
||||
/* lookup */
|
||||
rc = getaddrinfo(hostname, NULL, &ai, &res);
|
||||
if (rc != 0) {
|
||||
if (rc == EAI_ADDRFAMILY ||
|
||||
rc == EAI_FAMILY) {
|
||||
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_nonblock(cfd);
|
||||
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 (qemu_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") < 0) {
|
||||
if (errno != EADDRNOTAVAIL) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
*has_ipv4 = true;
|
||||
}
|
||||
|
||||
if (socket_can_bind_connect("::1") < 0) {
|
||||
if (errno != EADDRNOTAVAIL) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
*has_ipv6 = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
42
tests/socket-helpers.h
Normal file
42
tests/socket-helpers.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @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
|
||||
*/
|
||||
int socket_can_bind_connect(const char *hostname);
|
||||
|
||||
/*
|
||||
* @has_ipv4: set to true on return if IPv4 is available
|
||||
* @has_ipv6: set to true on return if IPv6 is available
|
||||
*
|
||||
* Check whether IPv4 and/or IPv6 are available for use.
|
||||
* On success, @has_ipv4 and @has_ipv6 will be set to
|
||||
* indicate whether the respective protocols are available.
|
||||
*
|
||||
* Returns 0 on success, -1 on fatal error
|
||||
*/
|
||||
int socket_check_protocol_support(bool *has_ipv4, bool *has_ipv6);
|
|
@ -302,9 +302,8 @@ static int socket_can_read_hello(void *opaque)
|
|||
return 10;
|
||||
}
|
||||
|
||||
static void char_socket_test(void)
|
||||
static void char_socket_test_common(Chardev *chr)
|
||||
{
|
||||
Chardev *chr = qemu_chr_new("server", "tcp:127.0.0.1:0,server,nowait");
|
||||
Chardev *chr_client;
|
||||
QObject *addr;
|
||||
QDict *qdict;
|
||||
|
@ -359,6 +358,47 @@ static void char_socket_test(void)
|
|||
object_unparent(OBJECT(chr));
|
||||
}
|
||||
|
||||
|
||||
static void char_socket_basic_test(void)
|
||||
{
|
||||
Chardev *chr = qemu_chr_new("server", "tcp:127.0.0.1:0,server,nowait");
|
||||
|
||||
char_socket_test_common(chr);
|
||||
}
|
||||
|
||||
|
||||
static void char_socket_fdpass_test(void)
|
||||
{
|
||||
Chardev *chr;
|
||||
char *optstr;
|
||||
QemuOpts *opts;
|
||||
int fd;
|
||||
SocketAddress *addr = g_new0(SocketAddress, 1);
|
||||
|
||||
addr->type = SOCKET_ADDRESS_TYPE_INET;
|
||||
addr->u.inet.host = g_strdup("127.0.0.1");
|
||||
addr->u.inet.port = g_strdup("0");
|
||||
|
||||
fd = socket_listen(addr, &error_abort);
|
||||
g_assert(fd >= 0);
|
||||
|
||||
qapi_free_SocketAddress(addr);
|
||||
|
||||
optstr = g_strdup_printf("socket,id=cdev,fd=%d,server,nowait", fd);
|
||||
|
||||
opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"),
|
||||
optstr, true);
|
||||
g_free(optstr);
|
||||
g_assert_nonnull(opts);
|
||||
|
||||
chr = qemu_chr_new_from_opts(opts, &error_abort);
|
||||
|
||||
qemu_opts_del(opts);
|
||||
|
||||
char_socket_test_common(chr);
|
||||
}
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
static void char_pipe_test(void)
|
||||
{
|
||||
|
@ -775,7 +815,8 @@ int main(int argc, char **argv)
|
|||
#ifndef _WIN32
|
||||
g_test_add_func("/char/file-fifo", char_file_fifo_test);
|
||||
#endif
|
||||
g_test_add_func("/char/socket", char_socket_test);
|
||||
g_test_add_func("/char/socket/basic", char_socket_basic_test);
|
||||
g_test_add_func("/char/socket/fdpass", char_socket_fdpass_test);
|
||||
g_test_add_func("/char/udp", char_udp_test);
|
||||
#ifdef HAVE_CHARDEV_SERIAL
|
||||
g_test_add_func("/char/serial", char_serial_test);
|
||||
|
|
|
@ -223,6 +223,583 @@ static void test_parse_uint_full_correct(void)
|
|||
g_assert_cmpint(i, ==, 123);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoi_correct(void)
|
||||
{
|
||||
const char *str = "12345 foo";
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoi(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 12345);
|
||||
g_assert(endptr == str + 5);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoi_null(void)
|
||||
{
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoi(NULL, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
g_assert(endptr == NULL);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoi_empty(void)
|
||||
{
|
||||
const char *str = "";
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoi(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
g_assert(endptr == str);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoi_whitespace(void)
|
||||
{
|
||||
const char *str = " \t ";
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoi(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
g_assert(endptr == str);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoi_invalid(void)
|
||||
{
|
||||
const char *str = " xxxx \t abc";
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoi(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
g_assert(endptr == str);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoi_trailing(void)
|
||||
{
|
||||
const char *str = "123xxx";
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoi(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 123);
|
||||
g_assert(endptr == str + 3);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoi_octal(void)
|
||||
{
|
||||
const char *str = "0123";
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoi(str, &endptr, 8, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 0123);
|
||||
g_assert(endptr == str + strlen(str));
|
||||
|
||||
res = 999;
|
||||
endptr = &f;
|
||||
err = qemu_strtoi(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 0123);
|
||||
g_assert(endptr == str + strlen(str));
|
||||
}
|
||||
|
||||
static void test_qemu_strtoi_decimal(void)
|
||||
{
|
||||
const char *str = "0123";
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoi(str, &endptr, 10, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 123);
|
||||
g_assert(endptr == str + strlen(str));
|
||||
|
||||
str = "123";
|
||||
res = 999;
|
||||
endptr = &f;
|
||||
err = qemu_strtoi(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 123);
|
||||
g_assert(endptr == str + strlen(str));
|
||||
}
|
||||
|
||||
static void test_qemu_strtoi_hex(void)
|
||||
{
|
||||
const char *str = "0123";
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoi(str, &endptr, 16, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 0x123);
|
||||
g_assert(endptr == str + strlen(str));
|
||||
|
||||
str = "0x123";
|
||||
res = 999;
|
||||
endptr = &f;
|
||||
err = qemu_strtoi(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 0x123);
|
||||
g_assert(endptr == str + strlen(str));
|
||||
}
|
||||
|
||||
static void test_qemu_strtoi_max(void)
|
||||
{
|
||||
char *str = g_strdup_printf("%d", INT_MAX);
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoi(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, INT_MAX);
|
||||
g_assert(endptr == str + strlen(str));
|
||||
g_free(str);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoi_overflow(void)
|
||||
{
|
||||
char *str = g_strdup_printf("%lld", (long long)INT_MAX + 1ll);
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoi(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, -ERANGE);
|
||||
g_assert_cmpint(res, ==, INT_MAX);
|
||||
g_assert(endptr == str + strlen(str));
|
||||
g_free(str);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoi_underflow(void)
|
||||
{
|
||||
char *str = g_strdup_printf("%lld", (long long)INT_MIN - 1ll);
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoi(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, -ERANGE);
|
||||
g_assert_cmpint(res, ==, INT_MIN);
|
||||
g_assert(endptr == str + strlen(str));
|
||||
g_free(str);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoi_negative(void)
|
||||
{
|
||||
const char *str = " \t -321";
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoi(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, -321);
|
||||
g_assert(endptr == str + strlen(str));
|
||||
}
|
||||
|
||||
static void test_qemu_strtoi_full_correct(void)
|
||||
{
|
||||
const char *str = "123";
|
||||
int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoi(str, NULL, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 123);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoi_full_null(void)
|
||||
{
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoi(NULL, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
g_assert(endptr == NULL);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoi_full_empty(void)
|
||||
{
|
||||
const char *str = "";
|
||||
int res = 999L;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoi(str, NULL, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoi_full_negative(void)
|
||||
{
|
||||
const char *str = " \t -321";
|
||||
int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoi(str, NULL, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, -321);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoi_full_trailing(void)
|
||||
{
|
||||
const char *str = "123xxx";
|
||||
int res;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoi(str, NULL, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoi_full_max(void)
|
||||
{
|
||||
char *str = g_strdup_printf("%d", INT_MAX);
|
||||
int res;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoi(str, NULL, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, INT_MAX);
|
||||
g_free(str);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoui_correct(void)
|
||||
{
|
||||
const char *str = "12345 foo";
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
unsigned int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoui(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpuint(res, ==, 12345);
|
||||
g_assert(endptr == str + 5);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoui_null(void)
|
||||
{
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
unsigned int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoui(NULL, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
g_assert(endptr == NULL);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoui_empty(void)
|
||||
{
|
||||
const char *str = "";
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
unsigned int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoui(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
g_assert(endptr == str);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoui_whitespace(void)
|
||||
{
|
||||
const char *str = " \t ";
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
unsigned int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoui(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
g_assert(endptr == str);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoui_invalid(void)
|
||||
{
|
||||
const char *str = " xxxx \t abc";
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
unsigned int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoui(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
g_assert(endptr == str);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoui_trailing(void)
|
||||
{
|
||||
const char *str = "123xxx";
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
unsigned int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoui(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpuint(res, ==, 123);
|
||||
g_assert(endptr == str + 3);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoui_octal(void)
|
||||
{
|
||||
const char *str = "0123";
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
unsigned int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoui(str, &endptr, 8, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpuint(res, ==, 0123);
|
||||
g_assert(endptr == str + strlen(str));
|
||||
|
||||
res = 999;
|
||||
endptr = &f;
|
||||
err = qemu_strtoui(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpuint(res, ==, 0123);
|
||||
g_assert(endptr == str + strlen(str));
|
||||
}
|
||||
|
||||
static void test_qemu_strtoui_decimal(void)
|
||||
{
|
||||
const char *str = "0123";
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
unsigned int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoui(str, &endptr, 10, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpuint(res, ==, 123);
|
||||
g_assert(endptr == str + strlen(str));
|
||||
|
||||
str = "123";
|
||||
res = 999;
|
||||
endptr = &f;
|
||||
err = qemu_strtoui(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpuint(res, ==, 123);
|
||||
g_assert(endptr == str + strlen(str));
|
||||
}
|
||||
|
||||
static void test_qemu_strtoui_hex(void)
|
||||
{
|
||||
const char *str = "0123";
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
unsigned int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoui(str, &endptr, 16, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmphex(res, ==, 0x123);
|
||||
g_assert(endptr == str + strlen(str));
|
||||
|
||||
str = "0x123";
|
||||
res = 999;
|
||||
endptr = &f;
|
||||
err = qemu_strtoui(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmphex(res, ==, 0x123);
|
||||
g_assert(endptr == str + strlen(str));
|
||||
}
|
||||
|
||||
static void test_qemu_strtoui_max(void)
|
||||
{
|
||||
char *str = g_strdup_printf("%u", UINT_MAX);
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
unsigned int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoui(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmphex(res, ==, UINT_MAX);
|
||||
g_assert(endptr == str + strlen(str));
|
||||
g_free(str);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoui_overflow(void)
|
||||
{
|
||||
char *str = g_strdup_printf("%lld", (long long)UINT_MAX + 1ll);
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
unsigned int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoui(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, -ERANGE);
|
||||
g_assert_cmphex(res, ==, UINT_MAX);
|
||||
g_assert(endptr == str + strlen(str));
|
||||
g_free(str);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoui_underflow(void)
|
||||
{
|
||||
char *str = g_strdup_printf("%lld", (long long)INT_MIN - 1ll);
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
unsigned int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoui(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, -ERANGE);
|
||||
g_assert_cmpuint(res, ==, (unsigned int)-1);
|
||||
g_assert(endptr == str + strlen(str));
|
||||
g_free(str);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoui_negative(void)
|
||||
{
|
||||
const char *str = " \t -321";
|
||||
char f = 'X';
|
||||
const char *endptr = &f;
|
||||
unsigned int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoui(str, &endptr, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpuint(res, ==, (unsigned int)-321);
|
||||
g_assert(endptr == str + strlen(str));
|
||||
}
|
||||
|
||||
static void test_qemu_strtoui_full_correct(void)
|
||||
{
|
||||
const char *str = "123";
|
||||
unsigned int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoui(str, NULL, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpuint(res, ==, 123);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoui_full_null(void)
|
||||
{
|
||||
unsigned int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoui(NULL, NULL, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoui_full_empty(void)
|
||||
{
|
||||
const char *str = "";
|
||||
unsigned int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoui(str, NULL, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
}
|
||||
static void test_qemu_strtoui_full_negative(void)
|
||||
{
|
||||
const char *str = " \t -321";
|
||||
unsigned int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoui(str, NULL, 0, &res);
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpuint(res, ==, (unsigned int)-321);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoui_full_trailing(void)
|
||||
{
|
||||
const char *str = "123xxx";
|
||||
unsigned int res;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoui(str, NULL, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
}
|
||||
|
||||
static void test_qemu_strtoui_full_max(void)
|
||||
{
|
||||
char *str = g_strdup_printf("%u", UINT_MAX);
|
||||
unsigned int res = 999;
|
||||
int err;
|
||||
|
||||
err = qemu_strtoui(str, NULL, 0, &res);
|
||||
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmphex(res, ==, UINT_MAX);
|
||||
g_free(str);
|
||||
}
|
||||
|
||||
static void test_qemu_strtol_correct(void)
|
||||
{
|
||||
const char *str = "12345 foo";
|
||||
|
@ -1612,6 +2189,86 @@ int main(int argc, char **argv)
|
|||
g_test_add_func("/cutils/parse_uint_full/correct",
|
||||
test_parse_uint_full_correct);
|
||||
|
||||
/* qemu_strtoi() tests */
|
||||
g_test_add_func("/cutils/qemu_strtoi/correct",
|
||||
test_qemu_strtoi_correct);
|
||||
g_test_add_func("/cutils/qemu_strtoi/null",
|
||||
test_qemu_strtoi_null);
|
||||
g_test_add_func("/cutils/qemu_strtoi/empty",
|
||||
test_qemu_strtoi_empty);
|
||||
g_test_add_func("/cutils/qemu_strtoi/whitespace",
|
||||
test_qemu_strtoi_whitespace);
|
||||
g_test_add_func("/cutils/qemu_strtoi/invalid",
|
||||
test_qemu_strtoi_invalid);
|
||||
g_test_add_func("/cutils/qemu_strtoi/trailing",
|
||||
test_qemu_strtoi_trailing);
|
||||
g_test_add_func("/cutils/qemu_strtoi/octal",
|
||||
test_qemu_strtoi_octal);
|
||||
g_test_add_func("/cutils/qemu_strtoi/decimal",
|
||||
test_qemu_strtoi_decimal);
|
||||
g_test_add_func("/cutils/qemu_strtoi/hex",
|
||||
test_qemu_strtoi_hex);
|
||||
g_test_add_func("/cutils/qemu_strtoi/max",
|
||||
test_qemu_strtoi_max);
|
||||
g_test_add_func("/cutils/qemu_strtoi/overflow",
|
||||
test_qemu_strtoi_overflow);
|
||||
g_test_add_func("/cutils/qemu_strtoi/underflow",
|
||||
test_qemu_strtoi_underflow);
|
||||
g_test_add_func("/cutils/qemu_strtoi/negative",
|
||||
test_qemu_strtoi_negative);
|
||||
g_test_add_func("/cutils/qemu_strtoi_full/correct",
|
||||
test_qemu_strtoi_full_correct);
|
||||
g_test_add_func("/cutils/qemu_strtoi_full/null",
|
||||
test_qemu_strtoi_full_null);
|
||||
g_test_add_func("/cutils/qemu_strtoi_full/empty",
|
||||
test_qemu_strtoi_full_empty);
|
||||
g_test_add_func("/cutils/qemu_strtoi_full/negative",
|
||||
test_qemu_strtoi_full_negative);
|
||||
g_test_add_func("/cutils/qemu_strtoi_full/trailing",
|
||||
test_qemu_strtoi_full_trailing);
|
||||
g_test_add_func("/cutils/qemu_strtoi_full/max",
|
||||
test_qemu_strtoi_full_max);
|
||||
|
||||
/* qemu_strtoui() tests */
|
||||
g_test_add_func("/cutils/qemu_strtoui/correct",
|
||||
test_qemu_strtoui_correct);
|
||||
g_test_add_func("/cutils/qemu_strtoui/null",
|
||||
test_qemu_strtoui_null);
|
||||
g_test_add_func("/cutils/qemu_strtoui/empty",
|
||||
test_qemu_strtoui_empty);
|
||||
g_test_add_func("/cutils/qemu_strtoui/whitespace",
|
||||
test_qemu_strtoui_whitespace);
|
||||
g_test_add_func("/cutils/qemu_strtoui/invalid",
|
||||
test_qemu_strtoui_invalid);
|
||||
g_test_add_func("/cutils/qemu_strtoui/trailing",
|
||||
test_qemu_strtoui_trailing);
|
||||
g_test_add_func("/cutils/qemu_strtoui/octal",
|
||||
test_qemu_strtoui_octal);
|
||||
g_test_add_func("/cutils/qemu_strtoui/decimal",
|
||||
test_qemu_strtoui_decimal);
|
||||
g_test_add_func("/cutils/qemu_strtoui/hex",
|
||||
test_qemu_strtoui_hex);
|
||||
g_test_add_func("/cutils/qemu_strtoui/max",
|
||||
test_qemu_strtoui_max);
|
||||
g_test_add_func("/cutils/qemu_strtoui/overflow",
|
||||
test_qemu_strtoui_overflow);
|
||||
g_test_add_func("/cutils/qemu_strtoui/underflow",
|
||||
test_qemu_strtoui_underflow);
|
||||
g_test_add_func("/cutils/qemu_strtoui/negative",
|
||||
test_qemu_strtoui_negative);
|
||||
g_test_add_func("/cutils/qemu_strtoui_full/correct",
|
||||
test_qemu_strtoui_full_correct);
|
||||
g_test_add_func("/cutils/qemu_strtoui_full/null",
|
||||
test_qemu_strtoui_full_null);
|
||||
g_test_add_func("/cutils/qemu_strtoui_full/empty",
|
||||
test_qemu_strtoui_full_empty);
|
||||
g_test_add_func("/cutils/qemu_strtoui_full/negative",
|
||||
test_qemu_strtoui_full_negative);
|
||||
g_test_add_func("/cutils/qemu_strtoui_full/trailing",
|
||||
test_qemu_strtoui_full_trailing);
|
||||
g_test_add_func("/cutils/qemu_strtoui_full/max",
|
||||
test_qemu_strtoui_full_max);
|
||||
|
||||
/* qemu_strtol() tests */
|
||||
g_test_add_func("/cutils/qemu_strtol/correct",
|
||||
test_qemu_strtol_correct);
|
||||
|
|
|
@ -22,77 +22,9 @@
|
|||
#include "io/channel-socket.h"
|
||||
#include "io/channel-util.h"
|
||||
#include "io-channel-helpers.h"
|
||||
#include "socket-helpers.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
#ifndef AI_ADDRCONFIG
|
||||
# define AI_ADDRCONFIG 0
|
||||
#endif
|
||||
#ifndef EAI_ADDRFAMILY
|
||||
# define EAI_ADDRFAMILY 0
|
||||
#endif
|
||||
|
||||
static int check_bind(const char *hostname, bool *has_proto)
|
||||
{
|
||||
int fd = -1;
|
||||
struct addrinfo ai, *res = NULL;
|
||||
int rc;
|
||||
int ret = -1;
|
||||
|
||||
memset(&ai, 0, sizeof(ai));
|
||||
ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
|
||||
ai.ai_family = AF_UNSPEC;
|
||||
ai.ai_socktype = SOCK_STREAM;
|
||||
|
||||
/* lookup */
|
||||
rc = getaddrinfo(hostname, NULL, &ai, &res);
|
||||
if (rc != 0) {
|
||||
if (rc == EAI_ADDRFAMILY ||
|
||||
rc == EAI_FAMILY) {
|
||||
*has_proto = false;
|
||||
goto done;
|
||||
}
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
fd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||
if (fd < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (bind(fd, res->ai_addr, res->ai_addrlen) < 0) {
|
||||
if (errno == EADDRNOTAVAIL) {
|
||||
*has_proto = false;
|
||||
goto done;
|
||||
}
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*has_proto = true;
|
||||
done:
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
}
|
||||
if (res) {
|
||||
freeaddrinfo(res);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_protocol_support(bool *has_ipv4, bool *has_ipv6)
|
||||
{
|
||||
if (check_bind("127.0.0.1", has_ipv4) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (check_bind("::1", has_ipv6) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void test_io_channel_set_socket_bufs(QIOChannel *src,
|
||||
QIOChannel *dst)
|
||||
|
@ -566,7 +498,7 @@ int main(int argc, char **argv)
|
|||
* each protocol to avoid breaking tests on machines
|
||||
* with either IPv4 or IPv6 disabled.
|
||||
*/
|
||||
if (check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
|
||||
if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
266
tests/test-util-sockets.c
Normal file
266
tests/test-util-sockets.c
Normal file
|
@ -0,0 +1,266 @@
|
|||
/*
|
||||
* Tests for util/qemu-sockets.c
|
||||
*
|
||||
* Copyright 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 of the License, 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 library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qapi/error.h"
|
||||
#include "socket-helpers.h"
|
||||
#include "monitor/monitor.h"
|
||||
|
||||
static void test_fd_is_socket_bad(void)
|
||||
{
|
||||
char *tmp = g_strdup("qemu-test-util-sockets-XXXXXX");
|
||||
int fd = mkstemp(tmp);
|
||||
if (fd != 0) {
|
||||
unlink(tmp);
|
||||
}
|
||||
g_free(tmp);
|
||||
|
||||
g_assert(fd >= 0);
|
||||
|
||||
g_assert(!fd_is_socket(fd));
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void test_fd_is_socket_good(void)
|
||||
{
|
||||
int fd = qemu_socket(PF_INET, SOCK_STREAM, 0);
|
||||
|
||||
g_assert(fd >= 0);
|
||||
|
||||
g_assert(fd_is_socket(fd));
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static int mon_fd = -1;
|
||||
static const char *mon_fdname;
|
||||
|
||||
int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp)
|
||||
{
|
||||
g_assert(cur_mon);
|
||||
g_assert(mon == cur_mon);
|
||||
if (mon_fd == -1 || !g_str_equal(mon_fdname, fdname)) {
|
||||
error_setg(errp, "No fd named %s", fdname);
|
||||
return -1;
|
||||
}
|
||||
return dup(mon_fd);
|
||||
}
|
||||
|
||||
/* Syms in libqemustub.a are discarded at .o file granularity.
|
||||
* To replace monitor_get_fd() we must ensure everything in
|
||||
* stubs/monitor.c is defined, to make sure monitor.o is discarded
|
||||
* otherwise we get duplicate syms at link time.
|
||||
*/
|
||||
Monitor *cur_mon;
|
||||
void monitor_init(Chardev *chr, int flags) {}
|
||||
|
||||
|
||||
static void test_socket_fd_pass_name_good(void)
|
||||
{
|
||||
SocketAddress addr;
|
||||
int fd;
|
||||
|
||||
cur_mon = g_malloc(1); /* Fake a monitor */
|
||||
mon_fdname = "myfd";
|
||||
mon_fd = qemu_socket(AF_INET, SOCK_STREAM, 0);
|
||||
g_assert_cmpint(mon_fd, >, STDERR_FILENO);
|
||||
|
||||
addr.type = SOCKET_ADDRESS_TYPE_FD;
|
||||
addr.u.fd.str = g_strdup(mon_fdname);
|
||||
|
||||
fd = socket_connect(&addr, &error_abort);
|
||||
g_assert_cmpint(fd, !=, -1);
|
||||
g_assert_cmpint(fd, !=, mon_fd);
|
||||
close(fd);
|
||||
|
||||
fd = socket_listen(&addr, &error_abort);
|
||||
g_assert_cmpint(fd, !=, -1);
|
||||
g_assert_cmpint(fd, !=, mon_fd);
|
||||
close(fd);
|
||||
|
||||
g_free(addr.u.fd.str);
|
||||
mon_fdname = NULL;
|
||||
close(mon_fd);
|
||||
mon_fd = -1;
|
||||
g_free(cur_mon);
|
||||
cur_mon = NULL;
|
||||
}
|
||||
|
||||
static void test_socket_fd_pass_name_bad(void)
|
||||
{
|
||||
SocketAddress addr;
|
||||
Error *err = NULL;
|
||||
int fd;
|
||||
|
||||
cur_mon = g_malloc(1); /* Fake a monitor */
|
||||
mon_fdname = "myfd";
|
||||
mon_fd = dup(STDOUT_FILENO);
|
||||
g_assert_cmpint(mon_fd, >, STDERR_FILENO);
|
||||
|
||||
addr.type = SOCKET_ADDRESS_TYPE_FD;
|
||||
addr.u.fd.str = g_strdup(mon_fdname);
|
||||
|
||||
fd = socket_connect(&addr, &err);
|
||||
g_assert_cmpint(fd, ==, -1);
|
||||
error_free_or_abort(&err);
|
||||
|
||||
fd = socket_listen(&addr, &err);
|
||||
g_assert_cmpint(fd, ==, -1);
|
||||
error_free_or_abort(&err);
|
||||
|
||||
g_free(addr.u.fd.str);
|
||||
mon_fdname = NULL;
|
||||
close(mon_fd);
|
||||
mon_fd = -1;
|
||||
g_free(cur_mon);
|
||||
cur_mon = NULL;
|
||||
}
|
||||
|
||||
static void test_socket_fd_pass_name_nomon(void)
|
||||
{
|
||||
SocketAddress addr;
|
||||
Error *err = NULL;
|
||||
int fd;
|
||||
|
||||
g_assert(cur_mon == NULL);
|
||||
|
||||
addr.type = SOCKET_ADDRESS_TYPE_FD;
|
||||
addr.u.fd.str = g_strdup("myfd");
|
||||
|
||||
fd = socket_connect(&addr, &err);
|
||||
g_assert_cmpint(fd, ==, -1);
|
||||
error_free_or_abort(&err);
|
||||
|
||||
fd = socket_listen(&addr, &err);
|
||||
g_assert_cmpint(fd, ==, -1);
|
||||
error_free_or_abort(&err);
|
||||
|
||||
g_free(addr.u.fd.str);
|
||||
}
|
||||
|
||||
|
||||
static void test_socket_fd_pass_num_good(void)
|
||||
{
|
||||
SocketAddress addr;
|
||||
int fd, sfd;
|
||||
|
||||
g_assert(cur_mon == NULL);
|
||||
sfd = qemu_socket(AF_INET, SOCK_STREAM, 0);
|
||||
g_assert_cmpint(sfd, >, STDERR_FILENO);
|
||||
|
||||
addr.type = SOCKET_ADDRESS_TYPE_FD;
|
||||
addr.u.fd.str = g_strdup_printf("%d", sfd);
|
||||
|
||||
fd = socket_connect(&addr, &error_abort);
|
||||
g_assert_cmpint(fd, ==, sfd);
|
||||
|
||||
fd = socket_listen(&addr, &error_abort);
|
||||
g_assert_cmpint(fd, ==, sfd);
|
||||
|
||||
g_free(addr.u.fd.str);
|
||||
close(sfd);
|
||||
}
|
||||
|
||||
static void test_socket_fd_pass_num_bad(void)
|
||||
{
|
||||
SocketAddress addr;
|
||||
Error *err = NULL;
|
||||
int fd, sfd;
|
||||
|
||||
g_assert(cur_mon == NULL);
|
||||
sfd = dup(STDOUT_FILENO);
|
||||
|
||||
addr.type = SOCKET_ADDRESS_TYPE_FD;
|
||||
addr.u.fd.str = g_strdup_printf("%d", sfd);
|
||||
|
||||
fd = socket_connect(&addr, &err);
|
||||
g_assert_cmpint(fd, ==, -1);
|
||||
error_free_or_abort(&err);
|
||||
|
||||
fd = socket_listen(&addr, &err);
|
||||
g_assert_cmpint(fd, ==, -1);
|
||||
error_free_or_abort(&err);
|
||||
|
||||
g_free(addr.u.fd.str);
|
||||
close(sfd);
|
||||
}
|
||||
|
||||
static void test_socket_fd_pass_num_nocli(void)
|
||||
{
|
||||
SocketAddress addr;
|
||||
Error *err = NULL;
|
||||
int fd;
|
||||
|
||||
cur_mon = g_malloc(1); /* Fake a monitor */
|
||||
|
||||
addr.type = SOCKET_ADDRESS_TYPE_FD;
|
||||
addr.u.fd.str = g_strdup_printf("%d", STDOUT_FILENO);
|
||||
|
||||
fd = socket_connect(&addr, &err);
|
||||
g_assert_cmpint(fd, ==, -1);
|
||||
error_free_or_abort(&err);
|
||||
|
||||
fd = socket_listen(&addr, &err);
|
||||
g_assert_cmpint(fd, ==, -1);
|
||||
error_free_or_abort(&err);
|
||||
|
||||
g_free(addr.u.fd.str);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
bool has_ipv4, has_ipv6;
|
||||
|
||||
socket_init();
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
/* We're creating actual IPv4/6 sockets, so we should
|
||||
* check if the host running tests actually supports
|
||||
* each protocol to avoid breaking tests on machines
|
||||
* with either IPv4 or IPv6 disabled.
|
||||
*/
|
||||
if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (has_ipv4) {
|
||||
g_test_add_func("/util/socket/is-socket/bad",
|
||||
test_fd_is_socket_bad);
|
||||
g_test_add_func("/util/socket/is-socket/good",
|
||||
test_fd_is_socket_good);
|
||||
g_test_add_func("/socket/fd-pass/name/good",
|
||||
test_socket_fd_pass_name_good);
|
||||
g_test_add_func("/socket/fd-pass/name/bad",
|
||||
test_socket_fd_pass_name_bad);
|
||||
g_test_add_func("/socket/fd-pass/name/nomon",
|
||||
test_socket_fd_pass_name_nomon);
|
||||
g_test_add_func("/socket/fd-pass/num/good",
|
||||
test_socket_fd_pass_num_good);
|
||||
g_test_add_func("/socket/fd-pass/num/bad",
|
||||
test_socket_fd_pass_num_bad);
|
||||
g_test_add_func("/socket/fd-pass/num/nocli",
|
||||
test_socket_fd_pass_num_nocli);
|
||||
}
|
||||
|
||||
return g_test_run();
|
||||
}
|
109
util/cutils.c
109
util/cutils.c
|
@ -297,6 +297,115 @@ static int check_strtox_error(const char *nptr, char *ep,
|
|||
return -libc_errno;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string @nptr to an integer, and store it in @result.
|
||||
*
|
||||
* This is a wrapper around strtol() that is harder to misuse.
|
||||
* Semantics of @nptr, @endptr, @base match strtol() with differences
|
||||
* noted below.
|
||||
*
|
||||
* @nptr may be null, and no conversion is performed then.
|
||||
*
|
||||
* If no conversion is performed, store @nptr in *@endptr and return
|
||||
* -EINVAL.
|
||||
*
|
||||
* If @endptr is null, and the string isn't fully converted, return
|
||||
* -EINVAL. This is the case when the pointer that would be stored in
|
||||
* a non-null @endptr points to a character other than '\0'.
|
||||
*
|
||||
* If the conversion overflows @result, store INT_MAX in @result,
|
||||
* and return -ERANGE.
|
||||
*
|
||||
* If the conversion underflows @result, store INT_MIN in @result,
|
||||
* and return -ERANGE.
|
||||
*
|
||||
* Else store the converted value in @result, and return zero.
|
||||
*/
|
||||
int qemu_strtoi(const char *nptr, const char **endptr, int base,
|
||||
int *result)
|
||||
{
|
||||
char *ep;
|
||||
long long lresult;
|
||||
|
||||
if (!nptr) {
|
||||
if (endptr) {
|
||||
*endptr = nptr;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
lresult = strtoll(nptr, &ep, base);
|
||||
if (lresult < INT_MIN) {
|
||||
*result = INT_MIN;
|
||||
errno = ERANGE;
|
||||
} else if (lresult > INT_MAX) {
|
||||
*result = INT_MAX;
|
||||
errno = ERANGE;
|
||||
} else {
|
||||
*result = lresult;
|
||||
}
|
||||
return check_strtox_error(nptr, ep, endptr, errno);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string @nptr to an unsigned integer, and store it in @result.
|
||||
*
|
||||
* This is a wrapper around strtoul() that is harder to misuse.
|
||||
* Semantics of @nptr, @endptr, @base match strtoul() with differences
|
||||
* noted below.
|
||||
*
|
||||
* @nptr may be null, and no conversion is performed then.
|
||||
*
|
||||
* If no conversion is performed, store @nptr in *@endptr and return
|
||||
* -EINVAL.
|
||||
*
|
||||
* If @endptr is null, and the string isn't fully converted, return
|
||||
* -EINVAL. This is the case when the pointer that would be stored in
|
||||
* a non-null @endptr points to a character other than '\0'.
|
||||
*
|
||||
* If the conversion overflows @result, store UINT_MAX in @result,
|
||||
* and return -ERANGE.
|
||||
*
|
||||
* Else store the converted value in @result, and return zero.
|
||||
*
|
||||
* Note that a number with a leading minus sign gets converted without
|
||||
* the minus sign, checked for overflow (see above), then negated (in
|
||||
* @result's type). This is exactly how strtoul() works.
|
||||
*/
|
||||
int qemu_strtoui(const char *nptr, const char **endptr, int base,
|
||||
unsigned int *result)
|
||||
{
|
||||
char *ep;
|
||||
long long lresult;
|
||||
|
||||
if (!nptr) {
|
||||
if (endptr) {
|
||||
*endptr = nptr;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
lresult = strtoull(nptr, &ep, base);
|
||||
|
||||
/* Windows returns 1 for negative out-of-range values. */
|
||||
if (errno == ERANGE) {
|
||||
*result = -1;
|
||||
} else {
|
||||
if (lresult > UINT_MAX) {
|
||||
*result = UINT_MAX;
|
||||
errno = ERANGE;
|
||||
} else if (lresult < INT_MIN) {
|
||||
*result = UINT_MAX;
|
||||
errno = ERANGE;
|
||||
} else {
|
||||
*result = lresult;
|
||||
}
|
||||
}
|
||||
return check_strtox_error(nptr, ep, endptr, errno);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string @nptr to a long integer, and store it in @result.
|
||||
*
|
||||
|
|
|
@ -91,6 +91,14 @@ NetworkAddressFamily inet_netfamily(int family)
|
|||
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
|
||||
*
|
||||
|
@ -1034,6 +1042,30 @@ fail:
|
|||
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;
|
||||
|
@ -1048,7 +1080,7 @@ int socket_connect(SocketAddress *addr, Error **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:
|
||||
|
@ -1075,7 +1107,7 @@ int socket_listen(SocketAddress *addr, Error **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:
|
||||
|
|
Loading…
Reference in a new issue