qemu/tests/unit/socket-helpers.c
<<
>>
Prefs
   1/*
   2 * Helper functions for tests using sockets
   3 *
   4 * Copyright 2015-2018 Red Hat, Inc.
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License as
   8 * published by the Free Software Foundation; either version 2 or
   9 * (at your option) version 3 of the License.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
  18 *
  19 */
  20
  21#include "qemu/osdep.h"
  22#include "qemu/sockets.h"
  23#include "socket-helpers.h"
  24
  25#ifndef AI_ADDRCONFIG
  26# define AI_ADDRCONFIG 0
  27#endif
  28#ifndef EAI_ADDRFAMILY
  29# define EAI_ADDRFAMILY 0
  30#endif
  31
  32/*
  33 * @hostname: a DNS name or numeric IP address
  34 *
  35 * Check whether it is possible to bind & connect to ports
  36 * on the DNS name or IP address @hostname. If an IP address
  37 * is used, it must not be a wildcard address.
  38 *
  39 * Returns 0 on success, -1 on error with errno set
  40 */
  41static int socket_can_bind_connect(const char *hostname, int family)
  42{
  43    int lfd = -1, cfd = -1, afd = -1;
  44    struct addrinfo ai, *res = NULL;
  45    struct sockaddr_storage ss;
  46    socklen_t sslen = sizeof(ss);
  47    int soerr;
  48    socklen_t soerrlen = sizeof(soerr);
  49    bool check_soerr = false;
  50    int rc;
  51    int ret = -1;
  52
  53    memset(&ai, 0, sizeof(ai));
  54    ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
  55    ai.ai_family = family;
  56    ai.ai_socktype = SOCK_STREAM;
  57
  58    /* lookup */
  59    rc = getaddrinfo(hostname, NULL, &ai, &res);
  60    if (rc != 0) {
  61        if (rc == EAI_ADDRFAMILY || rc == EAI_FAMILY || rc == EAI_NONAME) {
  62            errno = EADDRNOTAVAIL;
  63        } else {
  64            errno = EINVAL;
  65        }
  66        goto cleanup;
  67    }
  68
  69    lfd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
  70    if (lfd < 0) {
  71        goto cleanup;
  72    }
  73
  74    cfd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
  75    if (cfd < 0) {
  76        goto cleanup;
  77    }
  78
  79    if (bind(lfd, res->ai_addr, res->ai_addrlen) < 0) {
  80        goto cleanup;
  81    }
  82
  83    if (listen(lfd, 1) < 0) {
  84        goto cleanup;
  85    }
  86
  87    if (getsockname(lfd, (struct sockaddr *)&ss, &sslen) < 0) {
  88        goto cleanup;
  89    }
  90
  91    qemu_socket_set_nonblock(cfd);
  92    if (connect(cfd, (struct sockaddr *)&ss, sslen) < 0) {
  93        if (errno == EINPROGRESS) {
  94            check_soerr = true;
  95        } else {
  96            goto cleanup;
  97        }
  98    }
  99
 100    sslen = sizeof(ss);
 101    afd = accept(lfd,  (struct sockaddr *)&ss, &sslen);
 102    if (afd < 0) {
 103        goto cleanup;
 104    }
 105
 106    if (check_soerr) {
 107        if (getsockopt(cfd, SOL_SOCKET, SO_ERROR, &soerr, &soerrlen) < 0) {
 108            goto cleanup;
 109        }
 110        if (soerr) {
 111            errno = soerr;
 112            goto cleanup;
 113        }
 114    }
 115
 116    ret = 0;
 117
 118 cleanup:
 119    if (afd != -1) {
 120        close(afd);
 121    }
 122    if (cfd != -1) {
 123        close(cfd);
 124    }
 125    if (lfd != -1) {
 126        close(lfd);
 127    }
 128    if (res) {
 129        freeaddrinfo(res);
 130    }
 131    return ret;
 132}
 133
 134
 135int socket_check_protocol_support(bool *has_ipv4, bool *has_ipv6)
 136{
 137    *has_ipv4 = *has_ipv6 = false;
 138
 139    if (socket_can_bind_connect("127.0.0.1", PF_INET) < 0) {
 140        if (errno != EADDRNOTAVAIL) {
 141            return -1;
 142        }
 143    } else {
 144        *has_ipv4 = true;
 145    }
 146
 147    if (socket_can_bind_connect("::1", PF_INET6) < 0) {
 148        if (errno != EADDRNOTAVAIL) {
 149            return -1;
 150        }
 151    } else {
 152        *has_ipv6 = true;
 153    }
 154
 155    return 0;
 156}
 157
 158void socket_check_afunix_support(bool *has_afunix)
 159{
 160    int fd;
 161
 162    fd = socket(PF_UNIX, SOCK_STREAM, 0);
 163    close(fd);
 164
 165#ifdef _WIN32
 166    *has_afunix = (fd != (int)INVALID_SOCKET);
 167#else
 168    *has_afunix = (fd >= 0);
 169#endif
 170
 171    return;
 172}
 173