]> git.proxmox.com Git - mirror_ovs.git/blame - lib/dns-resolve.c
bfd: Support overlay BFD
[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
9ce4aa8e
YS
85 const char *filename = getenv("OVS_RESOLV_CONF");
86 if (!filename) {
87#ifdef _WIN32
88 /* On Windows, NULL means to use the system default nameserver. */
89#else
90 filename = "/etc/resolv.conf";
91#endif
92 }
70c5afb0 93 struct stat s;
9ce4aa8e 94 if (!filename || !stat(filename, &s) || errno != ENOENT) {
70c5afb0
BP
95 int retval = ub_ctx_resolvconf(ub_ctx__, filename);
96 if (retval != 0) {
97 VLOG_WARN_RL(&rl, "Failed to read %s: %s",
9ce4aa8e
YS
98 filename ? filename : "system default nameserver",
99 ub_strerror(retval));
ba8eb43a
YS
100 ub_ctx_delete(ub_ctx__);
101 ub_ctx__ = NULL;
102 return;
70c5afb0 103 }
ba8eb43a
YS
104 } else {
105 VLOG_WARN_RL(&rl, "Failed to read %s: %s",
106 filename, ovs_strerror(errno));
107 ub_ctx_delete(ub_ctx__);
108 ub_ctx__ = NULL;
109 return;
771680d9 110 }
771680d9
YS
111
112 /* Handles '/etc/hosts' on Linux and 'WINDIR/etc/hosts' on Windows. */
70c5afb0 113 int retval = ub_ctx_hosts(ub_ctx__, NULL);
771680d9
YS
114 if (retval != 0) {
115 VLOG_WARN_RL(&rl, "Failed to read etc/hosts: %s",
116 ub_strerror(retval));
117 }
118
119 ub_ctx_async(ub_ctx__, true);
120 hmap_init(&all_reqs__);
121 thread_is_daemon = is_daemon;
122}
123
124/* Returns true on success. Otherwise, returns false and the error information
125 * can be found in logs. If there is no error information, then the resolving
126 * is in process and the caller should call again later. The value of '*addr'
127 * is always nullified if false is returned. If this function is called under
128 * daemon-context, the resolving will undergo asynchronously. Otherwise, a
129 * synchronouse resolving will take place.
130 *
131 * This function is thread-safe.
132 *
133 * The caller is responsible for freeing the returned '*addr'.
134 */
135bool
136dns_resolve(const char *name, char **addr)
137 OVS_EXCLUDED(dns_mutex__)
138{
139 bool success = false;
140
141 if (!thread_is_daemon) {
142 return dns_resolve_sync__(name, addr);
143 }
144
145 *addr = NULL;
146 ovs_mutex_lock(&dns_mutex__);
147
148 if (ub_ctx__ == NULL) {
149 goto unlock;
150 }
151
152 /* ub_process is inside lock as it invokes resolve_callback__. */
153 int retval = ub_process(ub_ctx__);
154 if (retval != 0) {
155 VLOG_ERR_RL(&rl, "dns-resolve error: %s", ub_strerror(retval));
156 goto unlock;
157 }
158
159 struct resolve_request *req;
160 req = resolve_find_or_new__(name);
161 if (resolve_check_valid__(req)) {
162 *addr = xstrdup(req->addr);
163 success = true;
164 } else if (req->state != RESOLVE_PENDING) {
165 success = resolve_async__(req, ns_t_a);
166 }
167unlock:
168 ovs_mutex_unlock(&dns_mutex__);
169 return success;
170}
171
172void
173dns_resolve_destroy(void)
174{
175 if (ub_ctx__ != NULL) {
176 /* Outstanding requests will be killed. */
177 ub_ctx_delete(ub_ctx__);
178 ub_ctx__ = NULL;
179
a84b8865
YS
180 struct resolve_request *req, *next;
181 HMAP_FOR_EACH_SAFE (req, next, hmap_node, &all_reqs__) {
771680d9
YS
182 ub_resolve_free(req->ub_result);
183 free(req->addr);
184 free(req->name);
185 free(req);
186 }
187 hmap_destroy(&all_reqs__);
188 }
189}
190
191static struct resolve_request *
192resolve_find_or_new__(const char *name)
193 OVS_REQUIRES(dns_mutex__)
194{
195 struct resolve_request *req;
196
197 HMAP_FOR_EACH_IN_BUCKET(req, hmap_node, hash_string(name, 0),
198 &all_reqs__) {
199 if (!strcmp(name, req->name)) {
200 return req;
201 }
202 }
203
204 req = xzalloc(sizeof *req);
205 req->name = xstrdup(name);
206 req->state = RESOLVE_INVALID;
207 hmap_insert(&all_reqs__, &req->hmap_node, hash_string(req->name, 0));
208 return req;
209}
210
211static bool
212resolve_check_expire__(struct resolve_request *req)
213 OVS_REQUIRES(dns_mutex__)
214{
215 return time_now() > req->time + req->ub_result->ttl;
216}
217
218static bool
219resolve_check_valid__(struct resolve_request *req)
220 OVS_REQUIRES(dns_mutex__)
221{
222 return (req != NULL
223 && req->state == RESOLVE_GOOD
224 && !resolve_check_expire__(req));
225}
226
227static bool
228resolve_async__(struct resolve_request *req, int qtype)
229 OVS_REQUIRES(dns_mutex__)
230{
231 if (qtype == ns_t_a || qtype == ns_t_aaaa) {
232 int retval;
233 retval = ub_resolve_async(ub_ctx__, req->name,
234 qtype, ns_c_in, req,
235 resolve_callback__, NULL);
236 if (retval != 0) {
237 req->state = RESOLVE_ERROR;
238 return false;
239 } else {
240 req->state = RESOLVE_PENDING;
241 return true;
242 }
243 }
244 return false;
245}
246
247static void
248resolve_callback__(void *req_, int err, struct ub_result *result)
249 OVS_REQUIRES(dns_mutex__)
250{
251 struct resolve_request *req = req_;
252
253 if (err != 0 || (result->qtype == ns_t_aaaa && !result->havedata)) {
9ff0b84c 254 ub_resolve_free(result);
771680d9
YS
255 req->state = RESOLVE_ERROR;
256 VLOG_ERR_RL(&rl, "%s: failed to resolve", req->name);
257 return;
258 }
259
260 /* IPv4 address is empty, try IPv6. */
261 if (result->qtype == ns_t_a && !result->havedata) {
262 ub_resolve_free(result);
263 resolve_async__(req, ns_t_aaaa);
264 return;
265 }
266
267 char *addr;
268 if (!resolve_result_to_addr__(result, &addr)) {
9ff0b84c 269 ub_resolve_free(result);
771680d9
YS
270 req->state = RESOLVE_ERROR;
271 VLOG_ERR_RL(&rl, "%s: failed to resolve", req->name);
272 return;
273 }
274
275 ub_resolve_free(req->ub_result);
276 free(req->addr);
277
278 req->ub_result = result;
279 req->addr = addr;
280 req->state = RESOLVE_GOOD;
281 req->time = time_now();
282}
283
284static bool
285resolve_result_to_addr__(struct ub_result *result, char **addr)
286{
287 int af = result->qtype == ns_t_a ? AF_INET : AF_INET6;
288 char buffer[INET6_ADDRSTRLEN];
289
290 /* XXX: only the first returned IP is used. */
291 if (inet_ntop(af, result->data[0], buffer, sizeof buffer)) {
292 *addr = xstrdup(buffer);
293 } else {
294 *addr = NULL;
295 }
296
297 return (*addr != NULL);
298}
299
300static bool
301dns_resolve_sync__(const char *name, char **addr)
302{
303 *addr = NULL;
304
305 if (ub_ctx__ == NULL) {
306 dns_resolve_init(false);
307 if (ub_ctx__ == NULL) {
308 return false;
309 }
310 }
311
312 struct ub_result *result;
313 int retval = ub_resolve(ub_ctx__, name, ns_t_a, ns_c_in, &result);
314 if (retval != 0) {
315 return false;
316 } else if (!result->havedata) {
317 ub_resolve_free(result);
318
319 retval = ub_resolve(ub_ctx__, name, ns_t_aaaa, ns_c_in, &result);
320 if (retval != 0) {
321 return false;
322 } else if (!result->havedata) {
323 ub_resolve_free(result);
324 return false;
325 }
326 }
327
328 bool success = resolve_result_to_addr__(result, addr);
329 ub_resolve_free(result);
330 return success;
331}