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