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 29 #ifndef AI_NUMERICSERV 30 # define AI_NUMERICSERV 0 31 #endif 32 33 static QIODNSResolver *instance; 34 static GOnce instance_init = G_ONCE_INIT; 35 36 static gpointer qio_dns_resolve_init_instance(gpointer unused G_GNUC_UNUSED) 37 { 38 instance = QIO_DNS_RESOLVER(object_new(TYPE_QIO_DNS_RESOLVER)); 39 return NULL; 40 } 41 42 QIODNSResolver *qio_dns_resolver_get_instance(void) 43 { 44 g_once(&instance_init, qio_dns_resolve_init_instance, NULL); 45 return instance; 46 } 47 48 static int qio_dns_resolver_lookup_sync_inet(QIODNSResolver *resolver, 49 SocketAddress *addr, 50 size_t *naddrs, 51 SocketAddress ***addrs, 52 Error **errp) 53 { 54 struct addrinfo ai, *res, *e; 55 InetSocketAddress *iaddr = &addr->u.inet; 56 char port[33]; 57 char uaddr[INET6_ADDRSTRLEN + 1]; 58 char uport[33]; 59 int rc; 60 Error *err = NULL; 61 size_t i; 62 63 *naddrs = 0; 64 *addrs = NULL; 65 66 memset(&ai, 0, sizeof(ai)); 67 ai.ai_flags = AI_PASSIVE; 68 if (iaddr->has_numeric && iaddr->numeric) { 69 ai.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV; 70 } 71 ai.ai_family = inet_ai_family_from_address(iaddr, &err); 72 ai.ai_socktype = SOCK_STREAM; 73 74 if (err) { 75 error_propagate(errp, err); 76 return -1; 77 } 78 79 if (iaddr->host == NULL) { 80 error_setg(errp, "host not specified"); 81 return -1; 82 } 83 if (iaddr->port != NULL) { 84 pstrcpy(port, sizeof(port), iaddr->port); 85 } else { 86 port[0] = '\0'; 87 } 88 89 rc = getaddrinfo(strlen(iaddr->host) ? iaddr->host : NULL, 90 strlen(port) ? port : NULL, &ai, &res); 91 if (rc != 0) { 92 error_setg(errp, "address resolution failed for %s:%s: %s", 93 iaddr->host, port, gai_strerror(rc)); 94 return -1; 95 } 96 97 for (e = res; e != NULL; e = e->ai_next) { 98 (*naddrs)++; 99 } 100 101 *addrs = g_new0(SocketAddress *, *naddrs); 102 103 /* create socket + bind */ 104 for (i = 0, e = res; e != NULL; i++, e = e->ai_next) { 105 SocketAddress *newaddr = g_new0(SocketAddress, 1); 106 107 newaddr->type = SOCKET_ADDRESS_TYPE_INET; 108 109 getnameinfo((struct sockaddr *)e->ai_addr, e->ai_addrlen, 110 uaddr, INET6_ADDRSTRLEN, uport, 32, 111 NI_NUMERICHOST | NI_NUMERICSERV); 112 113 newaddr->u.inet = (InetSocketAddress){ 114 .host = g_strdup(uaddr), 115 .port = g_strdup(uport), 116 .has_numeric = true, 117 .numeric = true, 118 .has_to = iaddr->has_to, 119 .to = iaddr->to, 120 .has_ipv4 = iaddr->has_ipv4, 121 .ipv4 = iaddr->ipv4, 122 .has_ipv6 = iaddr->has_ipv6, 123 .ipv6 = iaddr->ipv6, 124 }; 125 126 (*addrs)[i] = newaddr; 127 } 128 freeaddrinfo(res); 129 return 0; 130 } 131 132 133 static int qio_dns_resolver_lookup_sync_nop(QIODNSResolver *resolver, 134 SocketAddress *addr, 135 size_t *naddrs, 136 SocketAddress ***addrs, 137 Error **errp) 138 { 139 *naddrs = 1; 140 *addrs = g_new0(SocketAddress *, 1); 141 (*addrs)[0] = QAPI_CLONE(SocketAddress, addr); 142 143 return 0; 144 } 145 146 147 int qio_dns_resolver_lookup_sync(QIODNSResolver *resolver, 148 SocketAddress *addr, 149 size_t *naddrs, 150 SocketAddress ***addrs, 151 Error **errp) 152 { 153 switch (addr->type) { 154 case SOCKET_ADDRESS_TYPE_INET: 155 return qio_dns_resolver_lookup_sync_inet(resolver, 156 addr, 157 naddrs, 158 addrs, 159 errp); 160 161 case SOCKET_ADDRESS_TYPE_UNIX: 162 case SOCKET_ADDRESS_TYPE_VSOCK: 163 case SOCKET_ADDRESS_TYPE_FD: 164 return qio_dns_resolver_lookup_sync_nop(resolver, 165 addr, 166 naddrs, 167 addrs, 168 errp); 169 170 default: 171 abort(); 172 } 173 } 174 175 176 struct QIODNSResolverLookupData { 177 SocketAddress *addr; 178 SocketAddress **addrs; 179 size_t naddrs; 180 }; 181 182 183 static void qio_dns_resolver_lookup_data_free(gpointer opaque) 184 { 185 struct QIODNSResolverLookupData *data = opaque; 186 size_t i; 187 188 qapi_free_SocketAddress(data->addr); 189 for (i = 0; i < data->naddrs; i++) { 190 qapi_free_SocketAddress(data->addrs[i]); 191 } 192 193 g_free(data->addrs); 194 g_free(data); 195 } 196 197 198 static void qio_dns_resolver_lookup_worker(QIOTask *task, 199 gpointer opaque) 200 { 201 QIODNSResolver *resolver = QIO_DNS_RESOLVER(qio_task_get_source(task)); 202 struct QIODNSResolverLookupData *data = opaque; 203 Error *err = NULL; 204 205 qio_dns_resolver_lookup_sync(resolver, 206 data->addr, 207 &data->naddrs, 208 &data->addrs, 209 &err); 210 if (err) { 211 qio_task_set_error(task, err); 212 } else { 213 qio_task_set_result_pointer(task, opaque, NULL); 214 } 215 216 object_unref(OBJECT(resolver)); 217 } 218 219 220 void qio_dns_resolver_lookup_async(QIODNSResolver *resolver, 221 SocketAddress *addr, 222 QIOTaskFunc func, 223 gpointer opaque, 224 GDestroyNotify notify) 225 { 226 QIOTask *task; 227 struct QIODNSResolverLookupData *data = 228 g_new0(struct QIODNSResolverLookupData, 1); 229 230 data->addr = QAPI_CLONE(SocketAddress, addr); 231 232 task = qio_task_new(OBJECT(resolver), func, opaque, notify); 233 234 qio_task_run_in_thread(task, 235 qio_dns_resolver_lookup_worker, 236 data, 237 qio_dns_resolver_lookup_data_free, 238 NULL); 239 } 240 241 242 void qio_dns_resolver_lookup_result(QIODNSResolver *resolver, 243 QIOTask *task, 244 size_t *naddrs, 245 SocketAddress ***addrs) 246 { 247 struct QIODNSResolverLookupData *data = 248 qio_task_get_result_pointer(task); 249 size_t i; 250 251 *naddrs = 0; 252 *addrs = NULL; 253 if (!data) { 254 return; 255 } 256 257 *naddrs = data->naddrs; 258 *addrs = g_new0(SocketAddress *, data->naddrs); 259 for (i = 0; i < data->naddrs; i++) { 260 (*addrs)[i] = QAPI_CLONE(SocketAddress, data->addrs[i]); 261 } 262 } 263 264 265 static const TypeInfo qio_dns_resolver_info = { 266 .parent = TYPE_OBJECT, 267 .name = TYPE_QIO_DNS_RESOLVER, 268 .instance_size = sizeof(QIODNSResolver), 269 .class_size = sizeof(QIODNSResolverClass), 270 }; 271 272 273 static void qio_dns_resolver_register_types(void) 274 { 275 type_register_static(&qio_dns_resolver_info); 276 } 277 278 279 type_init(qio_dns_resolver_register_types); 280