1 /* 2 * QEMU DNS resolver 3 * 4 * Copyright (c) 2016 Red Hat, Inc. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library 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 GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18 * 19 */ 20 21 #include "qemu/osdep.h" 22 #include "io/dns-resolver.h" 23 #include "qapi/clone-visitor.h" 24 #include "qapi/qapi-visit-sockets.h" 25 #include "qemu/sockets.h" 26 #include "qapi/error.h" 27 #include "qemu/cutils.h" 28 #include "qemu/module.h" 29 30 #ifndef AI_NUMERICSERV 31 # define AI_NUMERICSERV 0 32 #endif 33 34 static QIODNSResolver *instance; 35 static GOnce instance_init = G_ONCE_INIT; 36 37 static gpointer qio_dns_resolve_init_instance(gpointer unused G_GNUC_UNUSED) 38 { 39 instance = QIO_DNS_RESOLVER(object_new(TYPE_QIO_DNS_RESOLVER)); 40 return NULL; 41 } 42 43 QIODNSResolver *qio_dns_resolver_get_instance(void) 44 { 45 g_once(&instance_init, qio_dns_resolve_init_instance, NULL); 46 return instance; 47 } 48 49 static int qio_dns_resolver_lookup_sync_inet(QIODNSResolver *resolver, 50 SocketAddress *addr, 51 size_t *naddrs, 52 SocketAddress ***addrs, 53 Error **errp) 54 { 55 struct addrinfo ai, *res, *e; 56 InetSocketAddress *iaddr = &addr->u.inet; 57 char port[33]; 58 char uaddr[INET6_ADDRSTRLEN + 1]; 59 char uport[33]; 60 int rc; 61 Error *err = NULL; 62 size_t i; 63 64 *naddrs = 0; 65 *addrs = NULL; 66 67 memset(&ai, 0, sizeof(ai)); 68 ai.ai_flags = AI_PASSIVE; 69 if (iaddr->has_numeric && iaddr->numeric) { 70 ai.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV; 71 } 72 ai.ai_family = inet_ai_family_from_address(iaddr, &err); 73 ai.ai_socktype = SOCK_STREAM; 74 75 if (err) { 76 error_propagate(errp, err); 77 return -1; 78 } 79 80 if (iaddr->host == NULL) { 81 error_setg(errp, "host not specified"); 82 return -1; 83 } 84 if (iaddr->port != NULL) { 85 pstrcpy(port, sizeof(port), iaddr->port); 86 } else { 87 port[0] = '\0'; 88 } 89 90 rc = getaddrinfo(strlen(iaddr->host) ? iaddr->host : NULL, 91 strlen(port) ? port : NULL, &ai, &res); 92 if (rc != 0) { 93 error_setg(errp, "address resolution failed for %s:%s: %s", 94 iaddr->host, port, gai_strerror(rc)); 95 return -1; 96 } 97 98 for (e = res; e != NULL; e = e->ai_next) { 99 (*naddrs)++; 100 } 101 102 *addrs = g_new0(SocketAddress *, *naddrs); 103 104 /* create socket + bind */ 105 for (i = 0, e = res; e != NULL; i++, e = e->ai_next) { 106 SocketAddress *newaddr = g_new0(SocketAddress, 1); 107 108 newaddr->type = SOCKET_ADDRESS_TYPE_INET; 109 110 getnameinfo((struct sockaddr *)e->ai_addr, e->ai_addrlen, 111 uaddr, INET6_ADDRSTRLEN, uport, 32, 112 NI_NUMERICHOST | NI_NUMERICSERV); 113 114 newaddr->u.inet = (InetSocketAddress){ 115 .host = g_strdup(uaddr), 116 .port = g_strdup(uport), 117 .has_numeric = true, 118 .numeric = true, 119 .has_to = iaddr->has_to, 120 .to = iaddr->to, 121 .has_ipv4 = iaddr->has_ipv4, 122 .ipv4 = iaddr->ipv4, 123 .has_ipv6 = iaddr->has_ipv6, 124 .ipv6 = iaddr->ipv6, 125 #ifdef HAVE_IPPROTO_MPTCP 126 .has_mptcp = iaddr->has_mptcp, 127 .mptcp = iaddr->mptcp, 128 #endif 129 }; 130 131 (*addrs)[i] = newaddr; 132 } 133 freeaddrinfo(res); 134 return 0; 135 } 136 137 138 static int qio_dns_resolver_lookup_sync_nop(QIODNSResolver *resolver, 139 SocketAddress *addr, 140 size_t *naddrs, 141 SocketAddress ***addrs, 142 Error **errp) 143 { 144 *naddrs = 1; 145 *addrs = g_new0(SocketAddress *, 1); 146 (*addrs)[0] = QAPI_CLONE(SocketAddress, addr); 147 148 return 0; 149 } 150 151 152 int qio_dns_resolver_lookup_sync(QIODNSResolver *resolver, 153 SocketAddress *addr, 154 size_t *naddrs, 155 SocketAddress ***addrs, 156 Error **errp) 157 { 158 switch (addr->type) { 159 case SOCKET_ADDRESS_TYPE_INET: 160 return qio_dns_resolver_lookup_sync_inet(resolver, 161 addr, 162 naddrs, 163 addrs, 164 errp); 165 166 case SOCKET_ADDRESS_TYPE_UNIX: 167 case SOCKET_ADDRESS_TYPE_VSOCK: 168 case SOCKET_ADDRESS_TYPE_FD: 169 return qio_dns_resolver_lookup_sync_nop(resolver, 170 addr, 171 naddrs, 172 addrs, 173 errp); 174 175 default: 176 abort(); 177 } 178 } 179 180 181 struct QIODNSResolverLookupData { 182 SocketAddress *addr; 183 SocketAddress **addrs; 184 size_t naddrs; 185 }; 186 187 188 static void qio_dns_resolver_lookup_data_free(gpointer opaque) 189 { 190 struct QIODNSResolverLookupData *data = opaque; 191 size_t i; 192 193 qapi_free_SocketAddress(data->addr); 194 for (i = 0; i < data->naddrs; i++) { 195 qapi_free_SocketAddress(data->addrs[i]); 196 } 197 198 g_free(data->addrs); 199 g_free(data); 200 } 201 202 203 static void qio_dns_resolver_lookup_worker(QIOTask *task, 204 gpointer opaque) 205 { 206 QIODNSResolver *resolver = QIO_DNS_RESOLVER(qio_task_get_source(task)); 207 struct QIODNSResolverLookupData *data = opaque; 208 Error *err = NULL; 209 210 qio_dns_resolver_lookup_sync(resolver, 211 data->addr, 212 &data->naddrs, 213 &data->addrs, 214 &err); 215 if (err) { 216 qio_task_set_error(task, err); 217 } else { 218 qio_task_set_result_pointer(task, opaque, NULL); 219 } 220 221 object_unref(OBJECT(resolver)); 222 } 223 224 225 void qio_dns_resolver_lookup_async(QIODNSResolver *resolver, 226 SocketAddress *addr, 227 QIOTaskFunc func, 228 gpointer opaque, 229 GDestroyNotify notify) 230 { 231 QIOTask *task; 232 struct QIODNSResolverLookupData *data = 233 g_new0(struct QIODNSResolverLookupData, 1); 234 235 data->addr = QAPI_CLONE(SocketAddress, addr); 236 237 task = qio_task_new(OBJECT(resolver), func, opaque, notify); 238 239 qio_task_run_in_thread(task, 240 qio_dns_resolver_lookup_worker, 241 data, 242 qio_dns_resolver_lookup_data_free, 243 NULL); 244 } 245 246 247 void qio_dns_resolver_lookup_result(QIODNSResolver *resolver, 248 QIOTask *task, 249 size_t *naddrs, 250 SocketAddress ***addrs) 251 { 252 struct QIODNSResolverLookupData *data = 253 qio_task_get_result_pointer(task); 254 size_t i; 255 256 *naddrs = 0; 257 *addrs = NULL; 258 if (!data) { 259 return; 260 } 261 262 *naddrs = data->naddrs; 263 *addrs = g_new0(SocketAddress *, data->naddrs); 264 for (i = 0; i < data->naddrs; i++) { 265 (*addrs)[i] = QAPI_CLONE(SocketAddress, data->addrs[i]); 266 } 267 } 268 269 270 static const TypeInfo qio_dns_resolver_info = { 271 .parent = TYPE_OBJECT, 272 .name = TYPE_QIO_DNS_RESOLVER, 273 .instance_size = sizeof(QIODNSResolver), 274 }; 275 276 277 static void qio_dns_resolver_register_types(void) 278 { 279 type_register_static(&qio_dns_resolver_info); 280 } 281 282 283 type_init(qio_dns_resolver_register_types); 284