xref: /qemu/tests/unit/socket-helpers.c (revision b822c05b812a39940f78e4d020852d134d49dc99)
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 
3371714178SMarc-André Lureau /*
3471714178SMarc-André Lureau  * @hostname: a DNS name or numeric IP address
3571714178SMarc-André Lureau  *
3671714178SMarc-André Lureau  * Check whether it is possible to bind & connect to ports
3771714178SMarc-André Lureau  * on the DNS name or IP address @hostname. If an IP address
3871714178SMarc-André Lureau  * is used, it must not be a wildcard address.
3971714178SMarc-André Lureau  *
4071714178SMarc-André Lureau  * Returns 0 on success, -1 on error with errno set
4171714178SMarc-André Lureau  */
4271714178SMarc-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;
5671714178SMarc-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) {
62*b822c05bSThomas Huth         if (rc == EAI_ADDRFAMILY || rc == EAI_FAMILY || rc == EAI_NONAME) {
639b589ffbSDaniel P. Berrange             errno = EADDRNOTAVAIL;
649b589ffbSDaniel P. Berrange         } else {
659b589ffbSDaniel P. Berrange             errno = EINVAL;
669b589ffbSDaniel P. Berrange         }
679b589ffbSDaniel P. Berrange         goto cleanup;
689b589ffbSDaniel P. Berrange     }
699b589ffbSDaniel P. Berrange 
70abd983c0SDaniel P. Berrange     lfd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
71abd983c0SDaniel P. Berrange     if (lfd < 0) {
729b589ffbSDaniel P. Berrange         goto cleanup;
739b589ffbSDaniel P. Berrange     }
749b589ffbSDaniel P. Berrange 
75abd983c0SDaniel P. Berrange     cfd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
76abd983c0SDaniel P. Berrange     if (cfd < 0) {
779b589ffbSDaniel P. Berrange         goto cleanup;
789b589ffbSDaniel P. Berrange     }
799b589ffbSDaniel P. Berrange 
80abd983c0SDaniel P. Berrange     if (bind(lfd, res->ai_addr, res->ai_addrlen) < 0) {
81abd983c0SDaniel P. Berrange         goto cleanup;
82abd983c0SDaniel P. Berrange     }
83abd983c0SDaniel P. Berrange 
84abd983c0SDaniel P. Berrange     if (listen(lfd, 1) < 0) {
85abd983c0SDaniel P. Berrange         goto cleanup;
86abd983c0SDaniel P. Berrange     }
87abd983c0SDaniel P. Berrange 
88abd983c0SDaniel P. Berrange     if (getsockname(lfd, (struct sockaddr *)&ss, &sslen) < 0) {
89abd983c0SDaniel P. Berrange         goto cleanup;
90abd983c0SDaniel P. Berrange     }
91abd983c0SDaniel P. Berrange 
92abd983c0SDaniel P. Berrange     qemu_set_nonblock(cfd);
93abd983c0SDaniel P. Berrange     if (connect(cfd, (struct sockaddr *)&ss, sslen) < 0) {
94abd983c0SDaniel P. Berrange         if (errno == EINPROGRESS) {
95abd983c0SDaniel P. Berrange             check_soerr = true;
96abd983c0SDaniel P. Berrange         } else {
97abd983c0SDaniel P. Berrange             goto cleanup;
98abd983c0SDaniel P. Berrange         }
99abd983c0SDaniel P. Berrange     }
100abd983c0SDaniel P. Berrange 
101abd983c0SDaniel P. Berrange     sslen = sizeof(ss);
102abd983c0SDaniel P. Berrange     afd = accept(lfd,  (struct sockaddr *)&ss, &sslen);
103abd983c0SDaniel P. Berrange     if (afd < 0) {
104abd983c0SDaniel P. Berrange         goto cleanup;
105abd983c0SDaniel P. Berrange     }
106abd983c0SDaniel P. Berrange 
107abd983c0SDaniel P. Berrange     if (check_soerr) {
108abd983c0SDaniel P. Berrange         if (qemu_getsockopt(cfd, SOL_SOCKET, SO_ERROR, &soerr, &soerrlen) < 0) {
109abd983c0SDaniel P. Berrange             goto cleanup;
110abd983c0SDaniel P. Berrange         }
111abd983c0SDaniel P. Berrange         if (soerr) {
112abd983c0SDaniel P. Berrange             errno = soerr;
113abd983c0SDaniel P. Berrange             goto cleanup;
114abd983c0SDaniel P. Berrange         }
115abd983c0SDaniel P. Berrange     }
116abd983c0SDaniel P. Berrange 
1179b589ffbSDaniel P. Berrange     ret = 0;
1189b589ffbSDaniel P. Berrange 
1199b589ffbSDaniel P. Berrange  cleanup:
120abd983c0SDaniel P. Berrange     if (afd != -1) {
121abd983c0SDaniel P. Berrange         close(afd);
122abd983c0SDaniel P. Berrange     }
123abd983c0SDaniel P. Berrange     if (cfd != -1) {
124abd983c0SDaniel P. Berrange         close(cfd);
125abd983c0SDaniel P. Berrange     }
126abd983c0SDaniel P. Berrange     if (lfd != -1) {
127abd983c0SDaniel P. Berrange         close(lfd);
1289b589ffbSDaniel P. Berrange     }
1299b589ffbSDaniel P. Berrange     if (res) {
1309b589ffbSDaniel P. Berrange         freeaddrinfo(res);
1319b589ffbSDaniel P. Berrange     }
1329b589ffbSDaniel P. Berrange     return ret;
1339b589ffbSDaniel P. Berrange }
1349b589ffbSDaniel P. Berrange 
1359b589ffbSDaniel P. Berrange 
1369b589ffbSDaniel P. Berrange int socket_check_protocol_support(bool *has_ipv4, bool *has_ipv6)
1379b589ffbSDaniel P. Berrange {
1389b589ffbSDaniel P. Berrange     *has_ipv4 = *has_ipv6 = false;
1399b589ffbSDaniel P. Berrange 
14071714178SMarc-André Lureau     if (socket_can_bind_connect("127.0.0.1", PF_INET) < 0) {
1419b589ffbSDaniel P. Berrange         if (errno != EADDRNOTAVAIL) {
1429b589ffbSDaniel P. Berrange             return -1;
1439b589ffbSDaniel P. Berrange         }
1449b589ffbSDaniel P. Berrange     } else {
1459b589ffbSDaniel P. Berrange         *has_ipv4 = true;
1469b589ffbSDaniel P. Berrange     }
1479b589ffbSDaniel P. Berrange 
14871714178SMarc-André Lureau     if (socket_can_bind_connect("::1", PF_INET6) < 0) {
1499b589ffbSDaniel P. Berrange         if (errno != EADDRNOTAVAIL) {
1509b589ffbSDaniel P. Berrange             return -1;
1519b589ffbSDaniel P. Berrange         }
1529b589ffbSDaniel P. Berrange     } else {
1539b589ffbSDaniel P. Berrange         *has_ipv6 = true;
1549b589ffbSDaniel P. Berrange     }
1559b589ffbSDaniel P. Berrange 
1569b589ffbSDaniel P. Berrange     return 0;
1579b589ffbSDaniel P. Berrange }
158