xref: /openbmc/qemu/io/dns-resolver.c (revision dc5bd18f)
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 "qapi/qapi-visit-sockets.h"
25 #include "qemu/sockets.h"
26 #include "qapi/error.h"
27 #include "qemu/cutils.h"
28 
29 #ifndef AI_NUMERICSERV
30 # define AI_NUMERICSERV 0
31 #endif
32 
33 static QIODNSResolver *instance;
34 static GOnce instance_init = G_ONCE_INIT;
35 
36 static gpointer qio_dns_resolve_init_instance(gpointer unused G_GNUC_UNUSED)
37 {
38     instance = QIO_DNS_RESOLVER(object_new(TYPE_QIO_DNS_RESOLVER));
39     return NULL;
40 }
41 
42 QIODNSResolver *qio_dns_resolver_get_instance(void)
43 {
44     g_once(&instance_init, qio_dns_resolve_init_instance, NULL);
45     return instance;
46 }
47 
48 static int qio_dns_resolver_lookup_sync_inet(QIODNSResolver *resolver,
49                                              SocketAddress *addr,
50                                              size_t *naddrs,
51                                              SocketAddress ***addrs,
52                                              Error **errp)
53 {
54     struct addrinfo ai, *res, *e;
55     InetSocketAddress *iaddr = &addr->u.inet;
56     char port[33];
57     char uaddr[INET6_ADDRSTRLEN + 1];
58     char uport[33];
59     int rc;
60     Error *err = NULL;
61     size_t i;
62 
63     *naddrs = 0;
64     *addrs = NULL;
65 
66     memset(&ai, 0, sizeof(ai));
67     ai.ai_flags = AI_PASSIVE;
68     if (iaddr->has_numeric && iaddr->numeric) {
69         ai.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV;
70     }
71     ai.ai_family = inet_ai_family_from_address(iaddr, &err);
72     ai.ai_socktype = SOCK_STREAM;
73 
74     if (err) {
75         error_propagate(errp, err);
76         return -1;
77     }
78 
79     if (iaddr->host == NULL) {
80         error_setg(errp, "host not specified");
81         return -1;
82     }
83     if (iaddr->port != NULL) {
84         pstrcpy(port, sizeof(port), iaddr->port);
85     } else {
86         port[0] = '\0';
87     }
88 
89     rc = getaddrinfo(strlen(iaddr->host) ? iaddr->host : NULL,
90                      strlen(port) ? port : NULL, &ai, &res);
91     if (rc != 0) {
92         error_setg(errp, "address resolution failed for %s:%s: %s",
93                    iaddr->host, port, gai_strerror(rc));
94         return -1;
95     }
96 
97     for (e = res; e != NULL; e = e->ai_next) {
98         (*naddrs)++;
99     }
100 
101     *addrs = g_new0(SocketAddress *, *naddrs);
102 
103     /* create socket + bind */
104     for (i = 0, e = res; e != NULL; i++, e = e->ai_next) {
105         SocketAddress *newaddr = g_new0(SocketAddress, 1);
106 
107         newaddr->type = SOCKET_ADDRESS_TYPE_INET;
108 
109         getnameinfo((struct sockaddr *)e->ai_addr, e->ai_addrlen,
110                     uaddr, INET6_ADDRSTRLEN, uport, 32,
111                     NI_NUMERICHOST | NI_NUMERICSERV);
112 
113         newaddr->u.inet = (InetSocketAddress){
114             .host = g_strdup(uaddr),
115             .port = g_strdup(uport),
116             .has_numeric = true,
117             .numeric = true,
118             .has_to = iaddr->has_to,
119             .to = iaddr->to,
120             .has_ipv4 = iaddr->has_ipv4,
121             .ipv4 = iaddr->ipv4,
122             .has_ipv6 = iaddr->has_ipv6,
123             .ipv6 = iaddr->ipv6,
124         };
125 
126         (*addrs)[i] = newaddr;
127     }
128     freeaddrinfo(res);
129     return 0;
130 }
131 
132 
133 static int qio_dns_resolver_lookup_sync_nop(QIODNSResolver *resolver,
134                                             SocketAddress *addr,
135                                             size_t *naddrs,
136                                             SocketAddress ***addrs,
137                                             Error **errp)
138 {
139     *naddrs = 1;
140     *addrs = g_new0(SocketAddress *, 1);
141     (*addrs)[0] = QAPI_CLONE(SocketAddress, addr);
142 
143     return 0;
144 }
145 
146 
147 int qio_dns_resolver_lookup_sync(QIODNSResolver *resolver,
148                                  SocketAddress *addr,
149                                  size_t *naddrs,
150                                  SocketAddress ***addrs,
151                                  Error **errp)
152 {
153     switch (addr->type) {
154     case SOCKET_ADDRESS_TYPE_INET:
155         return qio_dns_resolver_lookup_sync_inet(resolver,
156                                                  addr,
157                                                  naddrs,
158                                                  addrs,
159                                                  errp);
160 
161     case SOCKET_ADDRESS_TYPE_UNIX:
162     case SOCKET_ADDRESS_TYPE_VSOCK:
163     case SOCKET_ADDRESS_TYPE_FD:
164         return qio_dns_resolver_lookup_sync_nop(resolver,
165                                                 addr,
166                                                 naddrs,
167                                                 addrs,
168                                                 errp);
169 
170     default:
171         abort();
172     }
173 }
174 
175 
176 struct QIODNSResolverLookupData {
177     SocketAddress *addr;
178     SocketAddress **addrs;
179     size_t naddrs;
180 };
181 
182 
183 static void qio_dns_resolver_lookup_data_free(gpointer opaque)
184 {
185     struct QIODNSResolverLookupData *data = opaque;
186     size_t i;
187 
188     qapi_free_SocketAddress(data->addr);
189     for (i = 0; i < data->naddrs; i++) {
190         qapi_free_SocketAddress(data->addrs[i]);
191     }
192 
193     g_free(data->addrs);
194     g_free(data);
195 }
196 
197 
198 static void qio_dns_resolver_lookup_worker(QIOTask *task,
199                                            gpointer opaque)
200 {
201     QIODNSResolver *resolver = QIO_DNS_RESOLVER(qio_task_get_source(task));
202     struct QIODNSResolverLookupData *data = opaque;
203     Error *err = NULL;
204 
205     qio_dns_resolver_lookup_sync(resolver,
206                                  data->addr,
207                                  &data->naddrs,
208                                  &data->addrs,
209                                  &err);
210     if (err) {
211         qio_task_set_error(task, err);
212     } else {
213         qio_task_set_result_pointer(task, opaque, NULL);
214     }
215 
216     object_unref(OBJECT(resolver));
217 }
218 
219 
220 void qio_dns_resolver_lookup_async(QIODNSResolver *resolver,
221                                    SocketAddress *addr,
222                                    QIOTaskFunc func,
223                                    gpointer opaque,
224                                    GDestroyNotify notify)
225 {
226     QIOTask *task;
227     struct QIODNSResolverLookupData *data =
228         g_new0(struct QIODNSResolverLookupData, 1);
229 
230     data->addr = QAPI_CLONE(SocketAddress, addr);
231 
232     task = qio_task_new(OBJECT(resolver), func, opaque, notify);
233 
234     qio_task_run_in_thread(task,
235                            qio_dns_resolver_lookup_worker,
236                            data,
237                            qio_dns_resolver_lookup_data_free);
238 }
239 
240 
241 void qio_dns_resolver_lookup_result(QIODNSResolver *resolver,
242                                     QIOTask *task,
243                                     size_t *naddrs,
244                                     SocketAddress ***addrs)
245 {
246     struct QIODNSResolverLookupData *data =
247         qio_task_get_result_pointer(task);
248     size_t i;
249 
250     *naddrs = 0;
251     *addrs = NULL;
252     if (!data) {
253         return;
254     }
255 
256     *naddrs = data->naddrs;
257     *addrs = g_new0(SocketAddress *, data->naddrs);
258     for (i = 0; i < data->naddrs; i++) {
259         (*addrs)[i] = QAPI_CLONE(SocketAddress, data->addrs[i]);
260     }
261 }
262 
263 
264 static const TypeInfo qio_dns_resolver_info = {
265     .parent = TYPE_OBJECT,
266     .name = TYPE_QIO_DNS_RESOLVER,
267     .instance_size = sizeof(QIODNSResolver),
268     .class_size = sizeof(QIODNSResolverClass),
269 };
270 
271 
272 static void qio_dns_resolver_register_types(void)
273 {
274     type_register_static(&qio_dns_resolver_info);
275 }
276 
277 
278 type_init(qio_dns_resolver_register_types);
279