]> git.proxmox.com Git - mirror_ovs.git/blame - lib/dns-resolve.c
dns-resolve: Stop dns resolving if no DNS server configured
[mirror_ovs.git] / lib / dns-resolve.c
CommitLineData
771680d9
YS
1/*
2 * Copyright (c) 2017, 2018 Nicira, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <config.h>
18#include "dns-resolve.h"
19#include <sys/types.h>
20#include <netinet/in.h>
21#include <arpa/inet.h>
22#include <arpa/nameser.h>
23#include <errno.h>
24#include <string.h>
70c5afb0 25#include <sys/stat.h>
771680d9
YS
26#include <unbound.h>
27#include "hash.h"
28#include "openvswitch/hmap.h"
29#include "openvswitch/vlog.h"
30#include "timeval.h"
31
32VLOG_DEFINE_THIS_MODULE(dns_resolve);
33
34/* Guard all_reqs__ and resolve_state of each request. */
35static struct ovs_mutex dns_mutex__ = OVS_MUTEX_INITIALIZER;
36static struct hmap all_reqs__;
37static struct ub_ctx *ub_ctx__;
38
39static bool thread_is_daemon;
40
41static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
42
43enum resolve_state {
44 RESOLVE_INVALID,
45 RESOLVE_PENDING,
46 RESOLVE_GOOD,
47 RESOLVE_ERROR
48};
49
50struct resolve_request {
51 struct hmap_node hmap_node; /* node for all_reqs__ */
52 char *name; /* the domain name to be resolved */
53 char *addr; /* the resolved ip address */
54 enum resolve_state state; /* state of this request */
55 time_t time; /* resolving time */
56 struct ub_result *ub_result; /* the stored unbound result */
57};
58
59static struct resolve_request *resolve_find_or_new__(const char *name)
60 OVS_REQUIRES(dns_mutex__);
61static bool resolve_check_expire__(struct resolve_request *req)
62 OVS_REQUIRES(dns_mutex__);
63static bool resolve_check_valid__(struct resolve_request *req)
64 OVS_REQUIRES(dns_mutex__);
65static bool resolve_async__(struct resolve_request *req, int qtype)
66 OVS_REQUIRES(dns_mutex__);
67static void resolve_callback__(void *req, int err, struct ub_result *)
68 OVS_REQUIRES(dns_mutex__);
69static bool resolve_result_to_addr__(struct ub_result *result, char **addr);
70static bool dns_resolve_sync__(const char *name, char **addr);
71
72/* Pass a true 'is_daemon' if you don't want the DNS-resolving to block the
73 * running thread.
74 */
75void
76dns_resolve_init(bool is_daemon)
77{
78 ub_ctx__ = ub_ctx_create();
79 if (ub_ctx__ == NULL) {
80 VLOG_ERR_RL(&rl, "Failed to create libunbound context, "
81 "so asynchronous DNS resolving is disabled.");
82 return;
83 }
84
771680d9 85#ifdef __linux__
70c5afb0
BP
86 const char *filename = "/etc/resolv.conf";
87 struct stat s;
88 if (!stat(filename, &s) || errno != ENOENT) {
89 int retval = ub_ctx_resolvconf(ub_ctx__, filename);
90 if (retval != 0) {
91 VLOG_WARN_RL(&rl, "Failed to read %s: %s",
92 filename, ub_strerror(retval));
ba8eb43a
YS
93 ub_ctx_delete(ub_ctx__);
94 ub_ctx__ = NULL;
95 return;
70c5afb0 96 }
ba8eb43a
YS
97 } else {
98 VLOG_WARN_RL(&rl, "Failed to read %s: %s",
99 filename, ovs_strerror(errno));
100 ub_ctx_delete(ub_ctx__);
101 ub_ctx__ = NULL;
102 return;
771680d9
YS
103 }
104#endif
105
106 /* Handles '/etc/hosts' on Linux and 'WINDIR/etc/hosts' on Windows. */
70c5afb0 107 int retval = ub_ctx_hosts(ub_ctx__, NULL);
771680d9
YS
108 if (retval != 0) {
109 VLOG_WARN_RL(&rl, "Failed to read etc/hosts: %s",
110 ub_strerror(retval));
111 }
112
113 ub_ctx_async(ub_ctx__, true);
114 hmap_init(&all_reqs__);
115 thread_is_daemon = is_daemon;
116}
117
118/* Returns true on success. Otherwise, returns false and the error information
119 * can be found in logs. If there is no error information, then the resolving
120 * is in process and the caller should call again later. The value of '*addr'
121 * is always nullified if false is returned. If this function is called under
122 * daemon-context, the resolving will undergo asynchronously. Otherwise, a
123 * synchronouse resolving will take place.
124 *
125 * This function is thread-safe.
126 *
127 * The caller is responsible for freeing the returned '*addr'.
128 */
129bool
130dns_resolve(const char *name, char **addr)
131 OVS_EXCLUDED(dns_mutex__)
132{
133 bool success = false;
134
135 if (!thread_is_daemon) {
136 return dns_resolve_sync__(name, addr);
137 }
138
139 *addr = NULL;
140 ovs_mutex_lock(&dns_mutex__);
141
142 if (ub_ctx__ == NULL) {
143 goto unlock;
144 }
145
146 /* ub_process is inside lock as it invokes resolve_callback__. */
147 int retval = ub_process(ub_ctx__);
148 if (retval != 0) {
149 VLOG_ERR_RL(&rl, "dns-resolve error: %s", ub_strerror(retval));
150 goto unlock;
151 }
152
153 struct resolve_request *req;
154 req = resolve_find_or_new__(name);
155 if (resolve_check_valid__(req)) {
156 *addr = xstrdup(req->addr);
157 success = true;
158 } else if (req->state != RESOLVE_PENDING) {
159 success = resolve_async__(req, ns_t_a);
160 }
161unlock:
162 ovs_mutex_unlock(&dns_mutex__);
163 return success;
164}
165
166void
167dns_resolve_destroy(void)
168{
169 if (ub_ctx__ != NULL) {
170 /* Outstanding requests will be killed. */
171 ub_ctx_delete(ub_ctx__);
172 ub_ctx__ = NULL;
173
a84b8865
YS
174 struct resolve_request *req, *next;
175 HMAP_FOR_EACH_SAFE (req, next, hmap_node, &all_reqs__) {
771680d9
YS
176 ub_resolve_free(req->ub_result);
177 free(req->addr);
178 free(req->name);
179 free(req);
180 }
181 hmap_destroy(&all_reqs__);
182 }
183}
184
185static struct resolve_request *
186resolve_find_or_new__(const char *name)
187 OVS_REQUIRES(dns_mutex__)
188{
189 struct resolve_request *req;
190
191 HMAP_FOR_EACH_IN_BUCKET(req, hmap_node, hash_string(name, 0),
192 &all_reqs__) {
193 if (!strcmp(name, req->name)) {
194 return req;
195 }
196 }
197
198 req = xzalloc(sizeof *req);
199 req->name = xstrdup(name);
200 req->state = RESOLVE_INVALID;
201 hmap_insert(&all_reqs__, &req->hmap_node, hash_string(req->name, 0));
202 return req;
203}
204
205static bool
206resolve_check_expire__(struct resolve_request *req)
207 OVS_REQUIRES(dns_mutex__)
208{
209 return time_now() > req->time + req->ub_result->ttl;
210}
211
212static bool
213resolve_check_valid__(struct resolve_request *req)
214 OVS_REQUIRES(dns_mutex__)
215{
216 return (req != NULL
217 && req->state == RESOLVE_GOOD
218 && !resolve_check_expire__(req));
219}
220
221static bool
222resolve_async__(struct resolve_request *req, int qtype)
223 OVS_REQUIRES(dns_mutex__)
224{
225 if (qtype == ns_t_a || qtype == ns_t_aaaa) {
226 int retval;
227 retval = ub_resolve_async(ub_ctx__, req->name,
228 qtype, ns_c_in, req,
229 resolve_callback__, NULL);
230 if (retval != 0) {
231 req->state = RESOLVE_ERROR;
232 return false;
233 } else {
234 req->state = RESOLVE_PENDING;
235 return true;
236 }
237 }
238 return false;
239}
240
241static void
242resolve_callback__(void *req_, int err, struct ub_result *result)
243 OVS_REQUIRES(dns_mutex__)
244{
245 struct resolve_request *req = req_;
246
247 if (err != 0 || (result->qtype == ns_t_aaaa && !result->havedata)) {
248 req->state = RESOLVE_ERROR;
249 VLOG_ERR_RL(&rl, "%s: failed to resolve", req->name);
250 return;
251 }
252
253 /* IPv4 address is empty, try IPv6. */
254 if (result->qtype == ns_t_a && !result->havedata) {
255 ub_resolve_free(result);
256 resolve_async__(req, ns_t_aaaa);
257 return;
258 }
259
260 char *addr;
261 if (!resolve_result_to_addr__(result, &addr)) {
262 req->state = RESOLVE_ERROR;
263 VLOG_ERR_RL(&rl, "%s: failed to resolve", req->name);
264 return;
265 }
266
267 ub_resolve_free(req->ub_result);
268 free(req->addr);
269
270 req->ub_result = result;
271 req->addr = addr;
272 req->state = RESOLVE_GOOD;
273 req->time = time_now();
274}
275
276static bool
277resolve_result_to_addr__(struct ub_result *result, char **addr)
278{
279 int af = result->qtype == ns_t_a ? AF_INET : AF_INET6;
280 char buffer[INET6_ADDRSTRLEN];
281
282 /* XXX: only the first returned IP is used. */
283 if (inet_ntop(af, result->data[0], buffer, sizeof buffer)) {
284 *addr = xstrdup(buffer);
285 } else {
286 *addr = NULL;
287 }
288
289 return (*addr != NULL);
290}
291
292static bool
293dns_resolve_sync__(const char *name, char **addr)
294{
295 *addr = NULL;
296
297 if (ub_ctx__ == NULL) {
298 dns_resolve_init(false);
299 if (ub_ctx__ == NULL) {
300 return false;
301 }
302 }
303
304 struct ub_result *result;
305 int retval = ub_resolve(ub_ctx__, name, ns_t_a, ns_c_in, &result);
306 if (retval != 0) {
307 return false;
308 } else if (!result->havedata) {
309 ub_resolve_free(result);
310
311 retval = ub_resolve(ub_ctx__, name, ns_t_aaaa, ns_c_in, &result);
312 if (retval != 0) {
313 return false;
314 } else if (!result->havedata) {
315 ub_resolve_free(result);
316 return false;
317 }
318 }
319
320 bool success = resolve_result_to_addr__(result, addr);
321 ub_resolve_free(result);
322 return success;
323}