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