|  | /* | 
|  | * QEMU DNS resolver | 
|  | * | 
|  | * Copyright (c) 2016 Red Hat, Inc. | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, see <http://www.gnu.org/licenses/>. | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include "qemu/osdep.h" | 
|  | #include "io/dns-resolver.h" | 
|  | #include "qapi/clone-visitor.h" | 
|  | #include "qapi/qapi-visit-sockets.h" | 
|  | #include "qemu/sockets.h" | 
|  | #include "qapi/error.h" | 
|  | #include "qemu/cutils.h" | 
|  | #include "qemu/module.h" | 
|  |  | 
|  | #ifndef AI_NUMERICSERV | 
|  | # define AI_NUMERICSERV 0 | 
|  | #endif | 
|  |  | 
|  | static QIODNSResolver *instance; | 
|  | static GOnce instance_init = G_ONCE_INIT; | 
|  |  | 
|  | static gpointer qio_dns_resolve_init_instance(gpointer unused G_GNUC_UNUSED) | 
|  | { | 
|  | instance = QIO_DNS_RESOLVER(object_new(TYPE_QIO_DNS_RESOLVER)); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | QIODNSResolver *qio_dns_resolver_get_instance(void) | 
|  | { | 
|  | g_once(&instance_init, qio_dns_resolve_init_instance, NULL); | 
|  | return instance; | 
|  | } | 
|  |  | 
|  | static int qio_dns_resolver_lookup_sync_inet(QIODNSResolver *resolver, | 
|  | SocketAddress *addr, | 
|  | size_t *naddrs, | 
|  | SocketAddress ***addrs, | 
|  | Error **errp) | 
|  | { | 
|  | struct addrinfo ai, *res, *e; | 
|  | InetSocketAddress *iaddr = &addr->u.inet; | 
|  | char port[33]; | 
|  | char uaddr[INET6_ADDRSTRLEN + 1]; | 
|  | char uport[33]; | 
|  | int rc; | 
|  | Error *err = NULL; | 
|  | size_t i; | 
|  |  | 
|  | *naddrs = 0; | 
|  | *addrs = NULL; | 
|  |  | 
|  | memset(&ai, 0, sizeof(ai)); | 
|  | ai.ai_flags = AI_PASSIVE; | 
|  | if (iaddr->has_numeric && iaddr->numeric) { | 
|  | ai.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV; | 
|  | } | 
|  | ai.ai_family = inet_ai_family_from_address(iaddr, &err); | 
|  | ai.ai_socktype = SOCK_STREAM; | 
|  |  | 
|  | if (err) { | 
|  | error_propagate(errp, err); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (iaddr->host == NULL) { | 
|  | error_setg(errp, "host not specified"); | 
|  | return -1; | 
|  | } | 
|  | if (iaddr->port != NULL) { | 
|  | pstrcpy(port, sizeof(port), iaddr->port); | 
|  | } else { | 
|  | port[0] = '\0'; | 
|  | } | 
|  |  | 
|  | rc = getaddrinfo(strlen(iaddr->host) ? iaddr->host : NULL, | 
|  | strlen(port) ? port : NULL, &ai, &res); | 
|  | if (rc != 0) { | 
|  | error_setg(errp, "address resolution failed for %s:%s: %s", | 
|  | iaddr->host, port, gai_strerror(rc)); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | for (e = res; e != NULL; e = e->ai_next) { | 
|  | (*naddrs)++; | 
|  | } | 
|  |  | 
|  | *addrs = g_new0(SocketAddress *, *naddrs); | 
|  |  | 
|  | /* create socket + bind */ | 
|  | for (i = 0, e = res; e != NULL; i++, e = e->ai_next) { | 
|  | SocketAddress *newaddr = g_new0(SocketAddress, 1); | 
|  |  | 
|  | newaddr->type = SOCKET_ADDRESS_TYPE_INET; | 
|  |  | 
|  | getnameinfo((struct sockaddr *)e->ai_addr, e->ai_addrlen, | 
|  | uaddr, INET6_ADDRSTRLEN, uport, 32, | 
|  | NI_NUMERICHOST | NI_NUMERICSERV); | 
|  |  | 
|  | newaddr->u.inet = (InetSocketAddress){ | 
|  | .host = g_strdup(uaddr), | 
|  | .port = g_strdup(uport), | 
|  | .has_numeric = true, | 
|  | .numeric = true, | 
|  | .has_to = iaddr->has_to, | 
|  | .to = iaddr->to, | 
|  | .has_ipv4 = iaddr->has_ipv4, | 
|  | .ipv4 = iaddr->ipv4, | 
|  | .has_ipv6 = iaddr->has_ipv6, | 
|  | .ipv6 = iaddr->ipv6, | 
|  | #ifdef HAVE_IPPROTO_MPTCP | 
|  | .has_mptcp = iaddr->has_mptcp, | 
|  | .mptcp = iaddr->mptcp, | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | (*addrs)[i] = newaddr; | 
|  | } | 
|  | freeaddrinfo(res); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int qio_dns_resolver_lookup_sync_nop(QIODNSResolver *resolver, | 
|  | SocketAddress *addr, | 
|  | size_t *naddrs, | 
|  | SocketAddress ***addrs, | 
|  | Error **errp) | 
|  | { | 
|  | *naddrs = 1; | 
|  | *addrs = g_new0(SocketAddress *, 1); | 
|  | (*addrs)[0] = QAPI_CLONE(SocketAddress, addr); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | int qio_dns_resolver_lookup_sync(QIODNSResolver *resolver, | 
|  | SocketAddress *addr, | 
|  | size_t *naddrs, | 
|  | SocketAddress ***addrs, | 
|  | Error **errp) | 
|  | { | 
|  | switch (addr->type) { | 
|  | case SOCKET_ADDRESS_TYPE_INET: | 
|  | return qio_dns_resolver_lookup_sync_inet(resolver, | 
|  | addr, | 
|  | naddrs, | 
|  | addrs, | 
|  | errp); | 
|  |  | 
|  | case SOCKET_ADDRESS_TYPE_UNIX: | 
|  | case SOCKET_ADDRESS_TYPE_VSOCK: | 
|  | case SOCKET_ADDRESS_TYPE_FD: | 
|  | return qio_dns_resolver_lookup_sync_nop(resolver, | 
|  | addr, | 
|  | naddrs, | 
|  | addrs, | 
|  | errp); | 
|  |  | 
|  | default: | 
|  | abort(); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | struct QIODNSResolverLookupData { | 
|  | SocketAddress *addr; | 
|  | SocketAddress **addrs; | 
|  | size_t naddrs; | 
|  | }; | 
|  |  | 
|  |  | 
|  | static void qio_dns_resolver_lookup_data_free(gpointer opaque) | 
|  | { | 
|  | struct QIODNSResolverLookupData *data = opaque; | 
|  | size_t i; | 
|  |  | 
|  | qapi_free_SocketAddress(data->addr); | 
|  | for (i = 0; i < data->naddrs; i++) { | 
|  | qapi_free_SocketAddress(data->addrs[i]); | 
|  | } | 
|  |  | 
|  | g_free(data->addrs); | 
|  | g_free(data); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void qio_dns_resolver_lookup_worker(QIOTask *task, | 
|  | gpointer opaque) | 
|  | { | 
|  | QIODNSResolver *resolver = QIO_DNS_RESOLVER(qio_task_get_source(task)); | 
|  | struct QIODNSResolverLookupData *data = opaque; | 
|  | Error *err = NULL; | 
|  |  | 
|  | qio_dns_resolver_lookup_sync(resolver, | 
|  | data->addr, | 
|  | &data->naddrs, | 
|  | &data->addrs, | 
|  | &err); | 
|  | if (err) { | 
|  | qio_task_set_error(task, err); | 
|  | } else { | 
|  | qio_task_set_result_pointer(task, opaque, NULL); | 
|  | } | 
|  |  | 
|  | object_unref(OBJECT(resolver)); | 
|  | } | 
|  |  | 
|  |  | 
|  | void qio_dns_resolver_lookup_async(QIODNSResolver *resolver, | 
|  | SocketAddress *addr, | 
|  | QIOTaskFunc func, | 
|  | gpointer opaque, | 
|  | GDestroyNotify notify) | 
|  | { | 
|  | QIOTask *task; | 
|  | struct QIODNSResolverLookupData *data = | 
|  | g_new0(struct QIODNSResolverLookupData, 1); | 
|  |  | 
|  | data->addr = QAPI_CLONE(SocketAddress, addr); | 
|  |  | 
|  | task = qio_task_new(OBJECT(resolver), func, opaque, notify); | 
|  |  | 
|  | qio_task_run_in_thread(task, | 
|  | qio_dns_resolver_lookup_worker, | 
|  | data, | 
|  | qio_dns_resolver_lookup_data_free, | 
|  | NULL); | 
|  | } | 
|  |  | 
|  |  | 
|  | void qio_dns_resolver_lookup_result(QIODNSResolver *resolver, | 
|  | QIOTask *task, | 
|  | size_t *naddrs, | 
|  | SocketAddress ***addrs) | 
|  | { | 
|  | struct QIODNSResolverLookupData *data = | 
|  | qio_task_get_result_pointer(task); | 
|  | size_t i; | 
|  |  | 
|  | *naddrs = 0; | 
|  | *addrs = NULL; | 
|  | if (!data) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | *naddrs = data->naddrs; | 
|  | *addrs = g_new0(SocketAddress *, data->naddrs); | 
|  | for (i = 0; i < data->naddrs; i++) { | 
|  | (*addrs)[i] = QAPI_CLONE(SocketAddress, data->addrs[i]); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | static const TypeInfo qio_dns_resolver_info = { | 
|  | .parent = TYPE_OBJECT, | 
|  | .name = TYPE_QIO_DNS_RESOLVER, | 
|  | .instance_size = sizeof(QIODNSResolver), | 
|  | }; | 
|  |  | 
|  |  | 
|  | static void qio_dns_resolver_register_types(void) | 
|  | { | 
|  | type_register_static(&qio_dns_resolver_info); | 
|  | } | 
|  |  | 
|  |  | 
|  | type_init(qio_dns_resolver_register_types); |