 563a3987b9
			
		
	
	
		563a3987b9
		
	
	
	
	
		
			
			The original InetSocketAddress struct may have has_ipv4 and has_ipv6 fields set, which will control both the ai_family used during DNS resolution, and later use of the V6ONLY flag. Currently the standalone DNS resolver code drops the has_ipv4 & has_ipv6 flags after resolving, which means the later bind() code won't correctly set V6ONLY. This fixes the following scenarios -vnc :0,ipv4=off -vnc :0,ipv6=on -vnc :::0,ipv4=off -vnc :::0,ipv6=on which all mistakenly accepted IPv4 clients Acked-by: Gerd Hoffmann <kraxel@gmail.com> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Reviewed-by: Eric Blake <eblake@redhat.com> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
		
			
				
	
	
		
			278 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			278 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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 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 "qemu/sockets.h"
 | |
| #include "qapi/error.h"
 | |
| #include "qemu/cutils.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,
 | |
|         };
 | |
| 
 | |
|         (*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);
 | |
| }
 | |
| 
 | |
| 
 | |
| 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),
 | |
|     .class_size = sizeof(QIODNSResolverClass),
 | |
| };
 | |
| 
 | |
| 
 | |
| static void qio_dns_resolver_register_types(void)
 | |
| {
 | |
|     type_register_static(&qio_dns_resolver_info);
 | |
| }
 | |
| 
 | |
| 
 | |
| type_init(qio_dns_resolver_register_types);
 |