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