]>
git.proxmox.com Git - mirror_ovs.git/blob - lib/dns-resolve.c
2 * Copyright (c) 2017, 2018 Nicira, Inc.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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>
28 #include "openvswitch/hmap.h"
29 #include "openvswitch/vlog.h"
32 VLOG_DEFINE_THIS_MODULE(dns_resolve
);
34 /* Guard all_reqs__ and resolve_state of each request. */
35 static struct ovs_mutex dns_mutex__
= OVS_MUTEX_INITIALIZER
;
36 static struct hmap all_reqs__
;
37 static struct ub_ctx
*ub_ctx__
;
39 static bool thread_is_daemon
;
41 static struct vlog_rate_limit rl
= VLOG_RATE_LIMIT_INIT(1, 1);
50 struct 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 */
59 static struct resolve_request
*resolve_find_or_new__(const char *name
)
60 OVS_REQUIRES(dns_mutex__
);
61 static bool resolve_check_expire__(struct resolve_request
*req
)
62 OVS_REQUIRES(dns_mutex__
);
63 static bool resolve_check_valid__(struct resolve_request
*req
)
64 OVS_REQUIRES(dns_mutex__
);
65 static bool resolve_async__(struct resolve_request
*req
, int qtype
)
66 OVS_REQUIRES(dns_mutex__
);
67 static void resolve_callback__(void *req
, int err
, struct ub_result
*)
68 OVS_REQUIRES(dns_mutex__
);
69 static bool resolve_result_to_addr__(struct ub_result
*result
, char **addr
);
70 static bool dns_resolve_sync__(const char *name
, char **addr
);
72 /* Pass a true 'is_daemon' if you don't want the DNS-resolving to block the
76 dns_resolve_init(bool is_daemon
)
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.");
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
);
89 VLOG_WARN_RL(&rl
, "Failed to set libunbound context config: %s",
91 ub_ctx_delete(ub_ctx__
);
97 const char *filename
= getenv("OVS_RESOLV_CONF");
100 /* On Windows, NULL means to use the system default nameserver. */
102 filename
= "/etc/resolv.conf";
106 if (!filename
|| !stat(filename
, &s
) || errno
!= ENOENT
) {
107 int retval
= ub_ctx_resolvconf(ub_ctx__
, filename
);
109 VLOG_WARN_RL(&rl
, "Failed to read %s: %s",
110 filename
? filename
: "system default nameserver",
111 ub_strerror(retval
));
112 ub_ctx_delete(ub_ctx__
);
117 VLOG_WARN_RL(&rl
, "Failed to read %s: %s",
118 filename
, ovs_strerror(errno
));
119 ub_ctx_delete(ub_ctx__
);
124 /* Handles '/etc/hosts' on Linux and 'WINDIR/etc/hosts' on Windows. */
125 int retval
= ub_ctx_hosts(ub_ctx__
, NULL
);
127 VLOG_WARN_RL(&rl
, "Failed to read etc/hosts: %s",
128 ub_strerror(retval
));
131 ub_ctx_async(ub_ctx__
, true);
132 hmap_init(&all_reqs__
);
133 thread_is_daemon
= is_daemon
;
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.
143 * This function is thread-safe.
145 * The caller is responsible for freeing the returned '*addr'.
148 dns_resolve(const char *name
, char **addr
)
149 OVS_EXCLUDED(dns_mutex__
)
151 bool success
= false;
153 if (!thread_is_daemon
) {
154 return dns_resolve_sync__(name
, addr
);
158 ovs_mutex_lock(&dns_mutex__
);
160 if (ub_ctx__
== NULL
) {
164 /* ub_process is inside lock as it invokes resolve_callback__. */
165 int retval
= ub_process(ub_ctx__
);
167 VLOG_ERR_RL(&rl
, "dns-resolve error: %s", ub_strerror(retval
));
171 struct resolve_request
*req
;
172 req
= resolve_find_or_new__(name
);
173 if (resolve_check_valid__(req
)) {
174 *addr
= xstrdup(req
->addr
);
176 } else if (req
->state
!= RESOLVE_PENDING
) {
177 success
= resolve_async__(req
, ns_t_a
);
180 ovs_mutex_unlock(&dns_mutex__
);
185 dns_resolve_destroy(void)
187 if (ub_ctx__
!= NULL
) {
188 /* Outstanding requests will be killed. */
189 ub_ctx_delete(ub_ctx__
);
192 struct resolve_request
*req
, *next
;
193 HMAP_FOR_EACH_SAFE (req
, next
, hmap_node
, &all_reqs__
) {
194 ub_resolve_free(req
->ub_result
);
199 hmap_destroy(&all_reqs__
);
203 static struct resolve_request
*
204 resolve_find_or_new__(const char *name
)
205 OVS_REQUIRES(dns_mutex__
)
207 struct resolve_request
*req
;
209 HMAP_FOR_EACH_IN_BUCKET(req
, hmap_node
, hash_string(name
, 0),
211 if (!strcmp(name
, req
->name
)) {
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));
224 resolve_check_expire__(struct resolve_request
*req
)
225 OVS_REQUIRES(dns_mutex__
)
227 return time_now() > req
->time
+ req
->ub_result
->ttl
;
231 resolve_check_valid__(struct resolve_request
*req
)
232 OVS_REQUIRES(dns_mutex__
)
235 && req
->state
== RESOLVE_GOOD
236 && !resolve_check_expire__(req
));
240 resolve_async__(struct resolve_request
*req
, int qtype
)
241 OVS_REQUIRES(dns_mutex__
)
243 if (qtype
== ns_t_a
|| qtype
== ns_t_aaaa
) {
245 retval
= ub_resolve_async(ub_ctx__
, req
->name
,
247 resolve_callback__
, NULL
);
249 req
->state
= RESOLVE_ERROR
;
252 req
->state
= RESOLVE_PENDING
;
260 resolve_callback__(void *req_
, int err
, struct ub_result
*result
)
261 OVS_REQUIRES(dns_mutex__
)
263 struct resolve_request
*req
= req_
;
265 if (err
!= 0 || (result
->qtype
== ns_t_aaaa
&& !result
->havedata
)) {
266 ub_resolve_free(result
);
267 req
->state
= RESOLVE_ERROR
;
268 VLOG_ERR_RL(&rl
, "%s: failed to resolve", req
->name
);
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
);
280 if (!resolve_result_to_addr__(result
, &addr
)) {
281 ub_resolve_free(result
);
282 req
->state
= RESOLVE_ERROR
;
283 VLOG_ERR_RL(&rl
, "%s: failed to resolve", req
->name
);
287 ub_resolve_free(req
->ub_result
);
290 req
->ub_result
= result
;
292 req
->state
= RESOLVE_GOOD
;
293 req
->time
= time_now();
297 resolve_result_to_addr__(struct ub_result
*result
, char **addr
)
299 int af
= result
->qtype
== ns_t_a
? AF_INET
: AF_INET6
;
300 char buffer
[INET6_ADDRSTRLEN
];
302 /* XXX: only the first returned IP is used. */
303 if (inet_ntop(af
, result
->data
[0], buffer
, sizeof buffer
)) {
304 *addr
= xstrdup(buffer
);
309 return (*addr
!= NULL
);
313 dns_resolve_sync__(const char *name
, char **addr
)
317 if (ub_ctx__
== NULL
) {
318 dns_resolve_init(false);
319 if (ub_ctx__
== NULL
) {
324 struct ub_result
*result
;
325 int retval
= ub_resolve(ub_ctx__
, name
, ns_t_a
, ns_c_in
, &result
);
328 } else if (!result
->havedata
) {
329 ub_resolve_free(result
);
331 retval
= ub_resolve(ub_ctx__
, name
, ns_t_aaaa
, ns_c_in
, &result
);
334 } else if (!result
->havedata
) {
335 ub_resolve_free(result
);
340 bool success
= resolve_result_to_addr__(result
, addr
);
341 ub_resolve_free(result
);