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