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