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 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 }; 126 127 (*addrs)[i] = newaddr; 128 } 129 freeaddrinfo(res); 130 return 0; 131 } 132 133 134 static int qio_dns_resolver_lookup_sync_nop(QIODNSResolver *resolver, 135 SocketAddress *addr, 136 size_t *naddrs, 137 SocketAddress ***addrs, 138 Error **errp) 139 { 140 *naddrs = 1; 141 *addrs = g_new0(SocketAddress *, 1); 142 (*addrs)[0] = QAPI_CLONE(SocketAddress, addr); 143 144 return 0; 145 } 146 147 148 int qio_dns_resolver_lookup_sync(QIODNSResolver *resolver, 149 SocketAddress *addr, 150 size_t *naddrs, 151 SocketAddress ***addrs, 152 Error **errp) 153 { 154 switch (addr->type) { 155 case SOCKET_ADDRESS_TYPE_INET: 156 return qio_dns_resolver_lookup_sync_inet(resolver, 157 addr, 158 naddrs, 159 addrs, 160 errp); 161 162 case SOCKET_ADDRESS_TYPE_UNIX: 163 case SOCKET_ADDRESS_TYPE_VSOCK: 164 case SOCKET_ADDRESS_TYPE_FD: 165 return qio_dns_resolver_lookup_sync_nop(resolver, 166 addr, 167 naddrs, 168 addrs, 169 errp); 170 171 default: 172 abort(); 173 } 174 } 175 176 177 struct QIODNSResolverLookupData { 178 SocketAddress *addr; 179 SocketAddress **addrs; 180 size_t naddrs; 181 }; 182 183 184 static void qio_dns_resolver_lookup_data_free(gpointer opaque) 185 { 186 struct QIODNSResolverLookupData *data = opaque; 187 size_t i; 188 189 qapi_free_SocketAddress(data->addr); 190 for (i = 0; i < data->naddrs; i++) { 191 qapi_free_SocketAddress(data->addrs[i]); 192 } 193 194 g_free(data->addrs); 195 g_free(data); 196 } 197 198 199 static void qio_dns_resolver_lookup_worker(QIOTask *task, 200 gpointer opaque) 201 { 202 QIODNSResolver *resolver = QIO_DNS_RESOLVER(qio_task_get_source(task)); 203 struct QIODNSResolverLookupData *data = opaque; 204 Error *err = NULL; 205 206 qio_dns_resolver_lookup_sync(resolver, 207 data->addr, 208 &data->naddrs, 209 &data->addrs, 210 &err); 211 if (err) { 212 qio_task_set_error(task, err); 213 } else { 214 qio_task_set_result_pointer(task, opaque, NULL); 215 } 216 217 object_unref(OBJECT(resolver)); 218 } 219 220 221 void qio_dns_resolver_lookup_async(QIODNSResolver *resolver, 222 SocketAddress *addr, 223 QIOTaskFunc func, 224 gpointer opaque, 225 GDestroyNotify notify) 226 { 227 QIOTask *task; 228 struct QIODNSResolverLookupData *data = 229 g_new0(struct QIODNSResolverLookupData, 1); 230 231 data->addr = QAPI_CLONE(SocketAddress, addr); 232 233 task = qio_task_new(OBJECT(resolver), func, opaque, notify); 234 235 qio_task_run_in_thread(task, 236 qio_dns_resolver_lookup_worker, 237 data, 238 qio_dns_resolver_lookup_data_free, 239 NULL); 240 } 241 242 243 void qio_dns_resolver_lookup_result(QIODNSResolver *resolver, 244 QIOTask *task, 245 size_t *naddrs, 246 SocketAddress ***addrs) 247 { 248 struct QIODNSResolverLookupData *data = 249 qio_task_get_result_pointer(task); 250 size_t i; 251 252 *naddrs = 0; 253 *addrs = NULL; 254 if (!data) { 255 return; 256 } 257 258 *naddrs = data->naddrs; 259 *addrs = g_new0(SocketAddress *, data->naddrs); 260 for (i = 0; i < data->naddrs; i++) { 261 (*addrs)[i] = QAPI_CLONE(SocketAddress, data->addrs[i]); 262 } 263 } 264 265 266 static const TypeInfo qio_dns_resolver_info = { 267 .parent = TYPE_OBJECT, 268 .name = TYPE_QIO_DNS_RESOLVER, 269 .instance_size = sizeof(QIODNSResolver), 270 .class_size = sizeof(QIODNSResolverClass), 271 }; 272 273 274 static void qio_dns_resolver_register_types(void) 275 { 276 type_register_static(&qio_dns_resolver_info); 277 } 278 279 280 type_init(qio_dns_resolver_register_types); 281