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