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 */ 41 static 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_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 135 int 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