xref: /qemu/tests/unit/socket-helpers.c (revision 8a2b516ba2855c4530388051de2b8d17bc780ea8)
19b589ffbSDaniel P. Berrange /*
29b589ffbSDaniel P. Berrange  * Helper functions for tests using sockets
39b589ffbSDaniel P. Berrange  *
49b589ffbSDaniel P. Berrange  * Copyright 2015-2018 Red Hat, Inc.
59b589ffbSDaniel P. Berrange  *
69b589ffbSDaniel P. Berrange  * This program is free software; you can redistribute it and/or
79b589ffbSDaniel P. Berrange  * modify it under the terms of the GNU General Public License as
89b589ffbSDaniel P. Berrange  * published by the Free Software Foundation; either version 2 or
99b589ffbSDaniel P. Berrange  * (at your option) version 3 of the License.
109b589ffbSDaniel P. Berrange  *
119b589ffbSDaniel P. Berrange  * This program is distributed in the hope that it will be useful,
129b589ffbSDaniel P. Berrange  * but WITHOUT ANY WARRANTY; without even the implied warranty of
139b589ffbSDaniel P. Berrange  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
149b589ffbSDaniel P. Berrange  * GNU General Public License for more details.
159b589ffbSDaniel P. Berrange  *
169b589ffbSDaniel P. Berrange  * You should have received a copy of the GNU General Public License
179b589ffbSDaniel P. Berrange  * along with this program; if not, see <http://www.gnu.org/licenses/>.
189b589ffbSDaniel P. Berrange  *
199b589ffbSDaniel P. Berrange  */
209b589ffbSDaniel P. Berrange 
219b589ffbSDaniel P. Berrange #include "qemu/osdep.h"
229b589ffbSDaniel P. Berrange #include "qemu/sockets.h"
239b589ffbSDaniel P. Berrange #include "socket-helpers.h"
249b589ffbSDaniel P. Berrange 
259b589ffbSDaniel P. Berrange #ifndef AI_ADDRCONFIG
269b589ffbSDaniel P. Berrange # define AI_ADDRCONFIG 0
279b589ffbSDaniel P. Berrange #endif
289b589ffbSDaniel P. Berrange #ifndef EAI_ADDRFAMILY
299b589ffbSDaniel P. Berrange # define EAI_ADDRFAMILY 0
309b589ffbSDaniel P. Berrange #endif
319b589ffbSDaniel P. Berrange 
3271714178SMarc-André Lureau /*
3371714178SMarc-André Lureau  * @hostname: a DNS name or numeric IP address
3471714178SMarc-André Lureau  *
3571714178SMarc-André Lureau  * Check whether it is possible to bind & connect to ports
3671714178SMarc-André Lureau  * on the DNS name or IP address @hostname. If an IP address
3771714178SMarc-André Lureau  * is used, it must not be a wildcard address.
3871714178SMarc-André Lureau  *
3971714178SMarc-André Lureau  * Returns 0 on success, -1 on error with errno set
4071714178SMarc-André Lureau  */
socket_can_bind_connect(const char * hostname,int family)4171714178SMarc-André Lureau static int socket_can_bind_connect(const char *hostname, int family)
429b589ffbSDaniel P. Berrange {
43abd983c0SDaniel P. Berrange     int lfd = -1, cfd = -1, afd = -1;
449b589ffbSDaniel P. Berrange     struct addrinfo ai, *res = NULL;
45abd983c0SDaniel P. Berrange     struct sockaddr_storage ss;
46abd983c0SDaniel P. Berrange     socklen_t sslen = sizeof(ss);
47abd983c0SDaniel P. Berrange     int soerr;
48abd983c0SDaniel P. Berrange     socklen_t soerrlen = sizeof(soerr);
49abd983c0SDaniel P. Berrange     bool check_soerr = false;
509b589ffbSDaniel P. Berrange     int rc;
519b589ffbSDaniel P. Berrange     int ret = -1;
529b589ffbSDaniel P. Berrange 
539b589ffbSDaniel P. Berrange     memset(&ai, 0, sizeof(ai));
549b589ffbSDaniel P. Berrange     ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
5571714178SMarc-André Lureau     ai.ai_family = family;
569b589ffbSDaniel P. Berrange     ai.ai_socktype = SOCK_STREAM;
579b589ffbSDaniel P. Berrange 
589b589ffbSDaniel P. Berrange     /* lookup */
599b589ffbSDaniel P. Berrange     rc = getaddrinfo(hostname, NULL, &ai, &res);
609b589ffbSDaniel P. Berrange     if (rc != 0) {
61b822c05bSThomas Huth         if (rc == EAI_ADDRFAMILY || rc == EAI_FAMILY || rc == EAI_NONAME) {
629b589ffbSDaniel P. Berrange             errno = EADDRNOTAVAIL;
639b589ffbSDaniel P. Berrange         } else {
649b589ffbSDaniel P. Berrange             errno = EINVAL;
659b589ffbSDaniel P. Berrange         }
669b589ffbSDaniel P. Berrange         goto cleanup;
679b589ffbSDaniel P. Berrange     }
689b589ffbSDaniel P. Berrange 
69abd983c0SDaniel P. Berrange     lfd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
70abd983c0SDaniel P. Berrange     if (lfd < 0) {
719b589ffbSDaniel P. Berrange         goto cleanup;
729b589ffbSDaniel P. Berrange     }
739b589ffbSDaniel P. Berrange 
74abd983c0SDaniel P. Berrange     cfd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
75abd983c0SDaniel P. Berrange     if (cfd < 0) {
769b589ffbSDaniel P. Berrange         goto cleanup;
779b589ffbSDaniel P. Berrange     }
789b589ffbSDaniel P. Berrange 
79abd983c0SDaniel P. Berrange     if (bind(lfd, res->ai_addr, res->ai_addrlen) < 0) {
80abd983c0SDaniel P. Berrange         goto cleanup;
81abd983c0SDaniel P. Berrange     }
82abd983c0SDaniel P. Berrange 
83abd983c0SDaniel P. Berrange     if (listen(lfd, 1) < 0) {
84abd983c0SDaniel P. Berrange         goto cleanup;
85abd983c0SDaniel P. Berrange     }
86abd983c0SDaniel P. Berrange 
87abd983c0SDaniel P. Berrange     if (getsockname(lfd, (struct sockaddr *)&ss, &sslen) < 0) {
88abd983c0SDaniel P. Berrange         goto cleanup;
89abd983c0SDaniel P. Berrange     }
90abd983c0SDaniel P. Berrange 
91ff5927baSMarc-André Lureau     qemu_socket_set_nonblock(cfd);
92abd983c0SDaniel P. Berrange     if (connect(cfd, (struct sockaddr *)&ss, sslen) < 0) {
93abd983c0SDaniel P. Berrange         if (errno == EINPROGRESS) {
94abd983c0SDaniel P. Berrange             check_soerr = true;
95abd983c0SDaniel P. Berrange         } else {
96abd983c0SDaniel P. Berrange             goto cleanup;
97abd983c0SDaniel P. Berrange         }
98abd983c0SDaniel P. Berrange     }
99abd983c0SDaniel P. Berrange 
100abd983c0SDaniel P. Berrange     sslen = sizeof(ss);
101abd983c0SDaniel P. Berrange     afd = accept(lfd,  (struct sockaddr *)&ss, &sslen);
102abd983c0SDaniel P. Berrange     if (afd < 0) {
103abd983c0SDaniel P. Berrange         goto cleanup;
104abd983c0SDaniel P. Berrange     }
105abd983c0SDaniel P. Berrange 
106abd983c0SDaniel P. Berrange     if (check_soerr) {
107e7b79428SMarc-André Lureau         if (getsockopt(cfd, SOL_SOCKET, SO_ERROR, &soerr, &soerrlen) < 0) {
108abd983c0SDaniel P. Berrange             goto cleanup;
109abd983c0SDaniel P. Berrange         }
110abd983c0SDaniel P. Berrange         if (soerr) {
111abd983c0SDaniel P. Berrange             errno = soerr;
112abd983c0SDaniel P. Berrange             goto cleanup;
113abd983c0SDaniel P. Berrange         }
114abd983c0SDaniel P. Berrange     }
115abd983c0SDaniel P. Berrange 
1169b589ffbSDaniel P. Berrange     ret = 0;
1179b589ffbSDaniel P. Berrange 
1189b589ffbSDaniel P. Berrange  cleanup:
119abd983c0SDaniel P. Berrange     if (afd != -1) {
12025657fc6SMarc-André Lureau         close(afd);
121abd983c0SDaniel P. Berrange     }
122abd983c0SDaniel P. Berrange     if (cfd != -1) {
12325657fc6SMarc-André Lureau         close(cfd);
124abd983c0SDaniel P. Berrange     }
125abd983c0SDaniel P. Berrange     if (lfd != -1) {
12625657fc6SMarc-André Lureau         close(lfd);
1279b589ffbSDaniel P. Berrange     }
1289b589ffbSDaniel P. Berrange     if (res) {
1299b589ffbSDaniel P. Berrange         freeaddrinfo(res);
1309b589ffbSDaniel P. Berrange     }
1319b589ffbSDaniel P. Berrange     return ret;
1329b589ffbSDaniel P. Berrange }
1339b589ffbSDaniel P. Berrange 
1349b589ffbSDaniel P. Berrange 
socket_check_protocol_support(bool * has_ipv4,bool * has_ipv6)1359b589ffbSDaniel P. Berrange int socket_check_protocol_support(bool *has_ipv4, bool *has_ipv6)
1369b589ffbSDaniel P. Berrange {
1379b589ffbSDaniel P. Berrange     *has_ipv4 = *has_ipv6 = false;
1389b589ffbSDaniel P. Berrange 
13971714178SMarc-André Lureau     if (socket_can_bind_connect("127.0.0.1", PF_INET) < 0) {
1409b589ffbSDaniel P. Berrange         if (errno != EADDRNOTAVAIL) {
1419b589ffbSDaniel P. Berrange             return -1;
1429b589ffbSDaniel P. Berrange         }
1439b589ffbSDaniel P. Berrange     } else {
1449b589ffbSDaniel P. Berrange         *has_ipv4 = true;
1459b589ffbSDaniel P. Berrange     }
1469b589ffbSDaniel P. Berrange 
14771714178SMarc-André Lureau     if (socket_can_bind_connect("::1", PF_INET6) < 0) {
1489b589ffbSDaniel P. Berrange         if (errno != EADDRNOTAVAIL) {
1499b589ffbSDaniel P. Berrange             return -1;
1509b589ffbSDaniel P. Berrange         }
1519b589ffbSDaniel P. Berrange     } else {
1529b589ffbSDaniel P. Berrange         *has_ipv6 = true;
1539b589ffbSDaniel P. Berrange     }
1549b589ffbSDaniel P. Berrange 
1559b589ffbSDaniel P. Berrange     return 0;
1569b589ffbSDaniel P. Berrange }
1570370f239SBin Meng 
socket_check_afunix_support(bool * has_afunix)1580370f239SBin Meng void socket_check_afunix_support(bool *has_afunix)
1590370f239SBin Meng {
1600370f239SBin Meng     int fd;
1610370f239SBin Meng 
1620370f239SBin Meng     fd = socket(PF_UNIX, SOCK_STREAM, 0);
1630370f239SBin Meng 
1640370f239SBin Meng #ifdef _WIN32
1650370f239SBin Meng     *has_afunix = (fd != (int)INVALID_SOCKET);
1660370f239SBin Meng #else
1670370f239SBin Meng     *has_afunix = (fd >= 0);
1680370f239SBin Meng #endif
1690370f239SBin Meng 
170*e921e00dSPeter Maydell     if (*has_afunix) {
171*e921e00dSPeter Maydell         close(fd);
172*e921e00dSPeter Maydell     }
1730370f239SBin Meng }
174