| /* | 
 |  * 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); |