xref: /qemu/tests/unit/socket-helpers.c (revision 71714178fa35dff1b9afc0762501c81360f7db82)
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-common.h"
239b589ffbSDaniel P. Berrange #include "qemu/sockets.h"
249b589ffbSDaniel P. Berrange #include "socket-helpers.h"
259b589ffbSDaniel P. Berrange 
269b589ffbSDaniel P. Berrange #ifndef AI_ADDRCONFIG
279b589ffbSDaniel P. Berrange # define AI_ADDRCONFIG 0
289b589ffbSDaniel P. Berrange #endif
299b589ffbSDaniel P. Berrange #ifndef EAI_ADDRFAMILY
309b589ffbSDaniel P. Berrange # define EAI_ADDRFAMILY 0
319b589ffbSDaniel P. Berrange #endif
329b589ffbSDaniel P. Berrange 
33*71714178SMarc-André Lureau /*
34*71714178SMarc-André Lureau  * @hostname: a DNS name or numeric IP address
35*71714178SMarc-André Lureau  *
36*71714178SMarc-André Lureau  * Check whether it is possible to bind & connect to ports
37*71714178SMarc-André Lureau  * on the DNS name or IP address @hostname. If an IP address
38*71714178SMarc-André Lureau  * is used, it must not be a wildcard address.
39*71714178SMarc-André Lureau  *
40*71714178SMarc-André Lureau  * Returns 0 on success, -1 on error with errno set
41*71714178SMarc-André Lureau  */
42*71714178SMarc-André Lureau static int socket_can_bind_connect(const char *hostname, int family)
439b589ffbSDaniel P. Berrange {
44abd983c0SDaniel P. Berrange     int lfd = -1, cfd = -1, afd = -1;
459b589ffbSDaniel P. Berrange     struct addrinfo ai, *res = NULL;
46abd983c0SDaniel P. Berrange     struct sockaddr_storage ss;
47abd983c0SDaniel P. Berrange     socklen_t sslen = sizeof(ss);
48abd983c0SDaniel P. Berrange     int soerr;
49abd983c0SDaniel P. Berrange     socklen_t soerrlen = sizeof(soerr);
50abd983c0SDaniel P. Berrange     bool check_soerr = false;
519b589ffbSDaniel P. Berrange     int rc;
529b589ffbSDaniel P. Berrange     int ret = -1;
539b589ffbSDaniel P. Berrange 
549b589ffbSDaniel P. Berrange     memset(&ai, 0, sizeof(ai));
559b589ffbSDaniel P. Berrange     ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
56*71714178SMarc-André Lureau     ai.ai_family = family;
579b589ffbSDaniel P. Berrange     ai.ai_socktype = SOCK_STREAM;
589b589ffbSDaniel P. Berrange 
599b589ffbSDaniel P. Berrange     /* lookup */
609b589ffbSDaniel P. Berrange     rc = getaddrinfo(hostname, NULL, &ai, &res);
619b589ffbSDaniel P. Berrange     if (rc != 0) {
629b589ffbSDaniel P. Berrange         if (rc == EAI_ADDRFAMILY ||
639b589ffbSDaniel P. Berrange             rc == EAI_FAMILY) {
649b589ffbSDaniel P. Berrange             errno = EADDRNOTAVAIL;
659b589ffbSDaniel P. Berrange         } else {
669b589ffbSDaniel P. Berrange             errno = EINVAL;
679b589ffbSDaniel P. Berrange         }
689b589ffbSDaniel P. Berrange         goto cleanup;
699b589ffbSDaniel P. Berrange     }
709b589ffbSDaniel P. Berrange 
71abd983c0SDaniel P. Berrange     lfd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
72abd983c0SDaniel P. Berrange     if (lfd < 0) {
739b589ffbSDaniel P. Berrange         goto cleanup;
749b589ffbSDaniel P. Berrange     }
759b589ffbSDaniel P. Berrange 
76abd983c0SDaniel P. Berrange     cfd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
77abd983c0SDaniel P. Berrange     if (cfd < 0) {
789b589ffbSDaniel P. Berrange         goto cleanup;
799b589ffbSDaniel P. Berrange     }
809b589ffbSDaniel P. Berrange 
81abd983c0SDaniel P. Berrange     if (bind(lfd, res->ai_addr, res->ai_addrlen) < 0) {
82abd983c0SDaniel P. Berrange         goto cleanup;
83abd983c0SDaniel P. Berrange     }
84abd983c0SDaniel P. Berrange 
85abd983c0SDaniel P. Berrange     if (listen(lfd, 1) < 0) {
86abd983c0SDaniel P. Berrange         goto cleanup;
87abd983c0SDaniel P. Berrange     }
88abd983c0SDaniel P. Berrange 
89abd983c0SDaniel P. Berrange     if (getsockname(lfd, (struct sockaddr *)&ss, &sslen) < 0) {
90abd983c0SDaniel P. Berrange         goto cleanup;
91abd983c0SDaniel P. Berrange     }
92abd983c0SDaniel P. Berrange 
93abd983c0SDaniel P. Berrange     qemu_set_nonblock(cfd);
94abd983c0SDaniel P. Berrange     if (connect(cfd, (struct sockaddr *)&ss, sslen) < 0) {
95abd983c0SDaniel P. Berrange         if (errno == EINPROGRESS) {
96abd983c0SDaniel P. Berrange             check_soerr = true;
97abd983c0SDaniel P. Berrange         } else {
98abd983c0SDaniel P. Berrange             goto cleanup;
99abd983c0SDaniel P. Berrange         }
100abd983c0SDaniel P. Berrange     }
101abd983c0SDaniel P. Berrange 
102abd983c0SDaniel P. Berrange     sslen = sizeof(ss);
103abd983c0SDaniel P. Berrange     afd = accept(lfd,  (struct sockaddr *)&ss, &sslen);
104abd983c0SDaniel P. Berrange     if (afd < 0) {
105abd983c0SDaniel P. Berrange         goto cleanup;
106abd983c0SDaniel P. Berrange     }
107abd983c0SDaniel P. Berrange 
108abd983c0SDaniel P. Berrange     if (check_soerr) {
109abd983c0SDaniel P. Berrange         if (qemu_getsockopt(cfd, SOL_SOCKET, SO_ERROR, &soerr, &soerrlen) < 0) {
110abd983c0SDaniel P. Berrange             goto cleanup;
111abd983c0SDaniel P. Berrange         }
112abd983c0SDaniel P. Berrange         if (soerr) {
113abd983c0SDaniel P. Berrange             errno = soerr;
114abd983c0SDaniel P. Berrange             goto cleanup;
115abd983c0SDaniel P. Berrange         }
116abd983c0SDaniel P. Berrange     }
117abd983c0SDaniel P. Berrange 
1189b589ffbSDaniel P. Berrange     ret = 0;
1199b589ffbSDaniel P. Berrange 
1209b589ffbSDaniel P. Berrange  cleanup:
121abd983c0SDaniel P. Berrange     if (afd != -1) {
122abd983c0SDaniel P. Berrange         close(afd);
123abd983c0SDaniel P. Berrange     }
124abd983c0SDaniel P. Berrange     if (cfd != -1) {
125abd983c0SDaniel P. Berrange         close(cfd);
126abd983c0SDaniel P. Berrange     }
127abd983c0SDaniel P. Berrange     if (lfd != -1) {
128abd983c0SDaniel P. Berrange         close(lfd);
1299b589ffbSDaniel P. Berrange     }
1309b589ffbSDaniel P. Berrange     if (res) {
1319b589ffbSDaniel P. Berrange         freeaddrinfo(res);
1329b589ffbSDaniel P. Berrange     }
1339b589ffbSDaniel P. Berrange     return ret;
1349b589ffbSDaniel P. Berrange }
1359b589ffbSDaniel P. Berrange 
1369b589ffbSDaniel P. Berrange 
1379b589ffbSDaniel P. Berrange int socket_check_protocol_support(bool *has_ipv4, bool *has_ipv6)
1389b589ffbSDaniel P. Berrange {
1399b589ffbSDaniel P. Berrange     *has_ipv4 = *has_ipv6 = false;
1409b589ffbSDaniel P. Berrange 
141*71714178SMarc-André Lureau     if (socket_can_bind_connect("127.0.0.1", PF_INET) < 0) {
1429b589ffbSDaniel P. Berrange         if (errno != EADDRNOTAVAIL) {
1439b589ffbSDaniel P. Berrange             return -1;
1449b589ffbSDaniel P. Berrange         }
1459b589ffbSDaniel P. Berrange     } else {
1469b589ffbSDaniel P. Berrange         *has_ipv4 = true;
1479b589ffbSDaniel P. Berrange     }
1489b589ffbSDaniel P. Berrange 
149*71714178SMarc-André Lureau     if (socket_can_bind_connect("::1", PF_INET6) < 0) {
1509b589ffbSDaniel P. Berrange         if (errno != EADDRNOTAVAIL) {
1519b589ffbSDaniel P. Berrange             return -1;
1529b589ffbSDaniel P. Berrange         }
1539b589ffbSDaniel P. Berrange     } else {
1549b589ffbSDaniel P. Berrange         *has_ipv6 = true;
1559b589ffbSDaniel P. Berrange     }
1569b589ffbSDaniel P. Berrange 
1579b589ffbSDaniel P. Berrange     return 0;
1589b589ffbSDaniel P. Berrange }
159